###############################################################################
#
# init.eagle --
#
# Extensible Adaptable Generalized Logic Engine (Eagle)
# Eagle Interpreter Initialization File
#
# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved.
#
# See the file "license.terms" for information on usage and redistribution of
# this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
# RCS: @(#) $Id: $
#
###############################################################################
#
# NOTE: Use our own namespace here because even though we do not directly
# support namespaces ourselves, we do not want to pollute the global
# namespace if this script actually ends up being evaluated in Tcl.
#
namespace eval ::Eagle {
#
# NOTE: This is the procedure that detects whether or not we are
# running in Eagle (otherwise, we are running in vanilla Tcl).
# This procedure must function correctly in both Tcl and Eagle
# and must return non-zero only when running in Eagle. This
# procedure must be defined in this script file because it is
# needed while this script file is being evaluated. The same
# procedure is also defined in the "platform.eagle" file.
#
# <bootstrap>
proc isEagle {} {
#
# NOTE: Nothing too fancy or expensive should be done in here. In
# theory, use of this routine should be rare; however, in
# practice, this routine is actually used quite a bit (e.g.
# by the test suite).
#
return [expr {[info exists ::tcl_platform(engine)] && \
[string compare -nocase eagle $::tcl_platform(engine)] == 0}]
}
#
# NOTE: This procedure is designed to "load" (i.e. [source]) other script
# files that logically belong to the package defined in this script
# file. Upon success, an empty string will be returned. A script
# error may be raised. This should work properly in both Tcl and
# Eagle. This procedure must be defined in this script file because
# it is needed while this script file is being evaluated.
#
# <bootstrap>
proc loadScripts { directory fileNamesOnly } {
#
# NOTE: Does the directory specified by the caller contain information
# useful in locating the script file?
#
if {[string length $directory] == 0 || $directory eq "."} then {
#
# NOTE: The directory specified by the caller contains no useful
# information, use the Tcl library directory instead, if
# possible.
#
if {[info exists ::tcl_library] && \
[string length $::tcl_library] > 0 && \
[file isdirectory $::tcl_library]} then {
#
# NOTE: Ok, use the Tcl library directory.
#
set directory $::tcl_library
}
}
#
# NOTE: Start out with the [source] command.
#
set baseCommand source
#
# NOTE: When using Eagle, use the -withinfo option to preserve the
# location information for procedures defined in the specified
# script file.
#
if {[isEagle]} then {
lappend baseCommand -withinfo true --
}
#
# NOTE: Load each script file specified by the caller, in the exact
# order they were specified.
#
foreach fileNameOnly $fileNamesOnly {
#
# NOTE: Start with the base [source] command, maybe with some
# options.
#
set fileCommand $baseCommand
#
# NOTE: Add the qualified file name, which may or may not be fully
# qualified.
#
lappend fileCommand [file join $directory $fileNameOnly]
#
# NOTE: Evaluate the resulting command in the callers context.
#
uplevel 1 $fileCommand
}
#
# NOTE: Success, return an empty string.
#
return ""
}
#
# NOTE: This procedure is designed to "load" (i.e. [source]) other script
# files that logically belong to the package defined in this script
# file. Upon success, an empty string will be returned. A script
# error may be raised. This should work properly in both Tcl and
# Eagle. This procedure must be defined in this script file because
# it is needed while this script file is being evaluated. If there
# exists a global array element no($fileNameOnly) corresponding to a
# particular script file to be loaded, it will be skipped.
#
# <bootstrap>
proc maybeLoadScripts { directory fileNamesOnly } {
set matchFileNames [list]
foreach fileNameOnly $fileNamesOnly {
if {![info exists ::no($fileNameOnly)]} then {
lappend matchFileNames $fileNameOnly
}
}
return [uplevel 1 [list loadScripts $directory $matchFileNames]]
}
if {![interp issafe]} then {
#
# NOTE: Load the extra script library files that contain commonly used
# procedures that are shared between native Tcl and Eagle.
#
maybeLoadScripts [file dirname [info script]] [list \
auxiliary.eagle database.eagle exec.eagle file1.eagle \
file2.eagle file3.eagle info.eagle list.eagle \
pkgt.eagle platform.eagle testlog.eagle unzip.eagle]
#
# NOTE: Load the extra script library files that contain procedures that
# require a specific language (i.e. either native Tcl or Eagle).
#
if {[isEagle]} then {
maybeLoadScripts [file dirname [info script]] [list \
compat.eagle csharp.eagle object.eagle process.eagle \
runopt.eagle unkobj.eagle update.eagle]
} else {
maybeLoadScripts [file dirname [info script]] [list shim.eagle]
}
}
if {[isEagle]} then {
###########################################################################
############################ BEGIN Eagle ONLY #############################
###########################################################################
#
# NOTE: This procedure uses the [source] command to evaluate a script
# file while preserving the location information for procedures
# defined with it.
#
# <pkgIndex>
proc sourceWithInfo { args } {
catch {
set savedCacheFlags None
object invoke -flags +NonPublic Interpreter.GetActive \
BeginNoArgumentCache savedCacheFlags
}
try {
catch {
set savedInterpreterFlags None
object invoke -flags +NonPublic Interpreter.GetActive \
BeginArgumentLocation savedInterpreterFlags
}
try {
set command [list source]; eval lappend command $args
return [uplevel 1 $command]
} finally {
catch {
object invoke -flags +NonPublic Interpreter.GetActive \
EndArgumentLocation savedInterpreterFlags
}
}
} finally {
catch {
object invoke -flags +NonPublic Interpreter.GetActive \
EndNoArgumentCache savedCacheFlags
}
}
}
if {![interp issafe]} then {
#
# NOTE: This is the [unknown] command for Eagle. It will normally be
# executed by the script engine when a command is not found.
# By default, it will simply raise a script error; however, if
# the "eagleUnknownObjectInvoke" runtime option is set, it will
# first attempt to use the (unknown) command name as the name
# of a CLR type.
#
# <create>
proc unknown { name args } {
#
# NOTE: This is an [unknown] procedure that normally produces an
# appropriate error message; however, it can optionally try
# to invoke a static object method.
#
# TODO: Add support for auto-loading packages here in the future?
#
if {[llength [info commands hasRuntimeOption]] > 0 && \
[hasRuntimeOption eagleUnknownObjectInvoke] && \
[llength [info commands object]] > 0 && \
[llength [info commands unknownObjectInvoke]] > 0} then {
#
# NOTE: In the context of the caller, attempt to invoke a static
# object method using the specified arguments (which may
# contain variable names).
#
if {[catch {
eval unknownObjectInvoke 1 [list $name] $args
} result] == 0} then {
#
# NOTE: The static object method was invoked successfully.
# Return its result.
#
return -code ok $result
} elseif {[string length $result] > 0} then {
#
# NOTE: Attempting to invoke the static object method raised
# an error. Re-raise it now. If no error message was
# provided, fallback on the default (below).
#
return -code error $result
}
}
return -code error [appendArgs "invalid command name \"" $name \"]
}
} else {
#
# NOTE: This is the [unknown] command for Eagle. It will normally be
# executed by the script engine when a command is not found.
# It will simply raise a script error. This procedure is also
# defined in "safe.eagle".
#
# <create>
proc unknown { name args } {
#
# NOTE: This is an [unknown] procedure that produces an appropriate
# error message.
#
# TODO: Add support for auto-loading packages here in the future?
#
# NOTE: This command cannot use [appendArgs] because that procedure
# is defined in another file that is never loaded into "safe"
# interpreters.
#
return -code error "invalid command name \"$name\""
}
}
#
# NOTE: This namespace and the procedure defined within it are used for
# compatibility with native Tcl.
#
namespace eval ::tcl::tm {
#
# NOTE: Ideally, this procedure should be created in the "::tcl::tm"
# namespace.
#
# <create>
proc ::tcl::tm::UnknownHandler { original name args } {
#
# NOTE: Do nothing except call the original handler.
#
uplevel 1 $original [::linsert $args 0 $name]
}
}
#
# NOTE: This procedure is normally executed by the package management
# subsystem of Eagle when a package is requested that cannot be
# found. By default, it will force a scan of all known package
# indexes.
#
# <create>
proc tclPkgUnknown { name args } {
#
# NOTE: Force a rescan of "pkgIndex" files. This must be done in
# the global scope so that the special global variable 'dir'
# set by the package index loading subsystem can be accessed.
#
uplevel #0 [list package scan -host -normal -refresh]
}
#
# NOTE: This procedure marks a procedure for "fast" execution; for now,
# this means disabling anything that makes variable access slower
# while the target procedure is executing.
#
# <experimental>
proc makeProcedureFast { name fast } {
#
# NOTE: This should work properly in Eagle only.
#
catch {
uplevel 1 [list object invoke -flags +NonPublic \
Interpreter.GetActive MakeProcedureFast $name $fast]
}
}
#
# NOTE: This procedure marks a variable for "fast" access; for now, this
# means disabling anything that makes variable access slower while
# the target variable is being read, set, or unset.
#
# <experimental>
proc makeVariableFast { name fast } {
#
# NOTE: This should work properly in Eagle only.
#
catch {
uplevel 1 [list object invoke -flags +NonPublic \
Interpreter.GetActive MakeVariableFast $name $fast]
}
}
#
# NOTE: Add script library files borrowed from native Tcl.
#
if {![interp issafe]} then {
maybeLoadScripts [file dirname [info script]] [list word.tcl]
}
###########################################################################
############################# END Eagle ONLY ##############################
###########################################################################
} else {
###########################################################################
############################# BEGIN Tcl ONLY ##############################
###########################################################################
#
# NOTE: Exports the necessary commands from this package and import them
# into the global namespace.
#
if {[llength [info commands exportAndImportPackageCommands]] > 0} then {
exportAndImportPackageCommands [namespace current] [list \
isEagle loadScripts maybeLoadScripts isWindows isInteractive \
haveGaruda isTclThread isMono isDotNetCore isSameFileName \
getEnvironmentVariable combineFlags getCompileInfo getPlatformInfo \
getPluginName getPluginPath getPackageInstallPath appendArgs \
lappendArgs getDictionaryValue getColumnValue getRowColumnValue \
tqputs tqlog makeBinaryChannel makeAsciiChannel makeUnicodeChannel \
makeLogChannel readFile readSharedFile writeFile appendFile \
appendLogFile appendSharedFile appendSharedLogFile \
readAsciiFile writeAsciiFile readUnicodeFile \
writeUnicodeFile getDirResultPath addToPath removeFromPath \
execShell maybeGetExitCode lshuffle ldifference filter map reduce \
getLengthModifier debug findDirectories \
findDirectoriesRecursive findFiles findFilesRecursive \
copyFilesRecursive exportAndImportPackageCommands setupUnzipVars \
unzipMustBeInstalled extractZipArchive] false false
}
###########################################################################
############################## END Tcl ONLY ###############################
###########################################################################
}
#
# NOTE: Provide the Eagle "library" package to the interpreter.
#
package provide Eagle.Library \
[expr {[isEagle] ? [info engine PatchLevel] : "1.0"}]
}