Un-ignored: the dev drive is the ground truth the restoration and emulator work constantly reference (DPL3/LIBDPL + VRENDER i860 renderer source, BT/RP live+dev game trees, VGL_LABS pod boot, scene/audio content). Kept in-repo for the pod-owner community. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
555 lines
15 KiB
Plaintext
555 lines
15 KiB
Plaintext
/*
|
|
** BRIEF -- Basic Reconfigurable Interactive Editing Facility
|
|
**
|
|
** Written by Dave Nanian and Michael Strickman.
|
|
*/
|
|
|
|
/*
|
|
** language.cb:
|
|
**
|
|
** This file contains the support modules for BRIEF's language package
|
|
** system.
|
|
*/
|
|
|
|
void _call_packages (string event_name);
|
|
void _parse_packages (string event_name);
|
|
string process_package (string event_name, string mac_name, string existing_packages, string file_ext);
|
|
void _reset_packages ();
|
|
string _package_abbrev ();
|
|
string escape_re (string original, ~string);
|
|
|
|
extern int _package_buf; // Parsed language package information.
|
|
|
|
string _curr_active; // The list of currently active packages.
|
|
|
|
void _init ()
|
|
{
|
|
int old_buf;
|
|
|
|
register_macro (1, "_call_on_packages");
|
|
register_macro (6, "_call_first_packages");
|
|
|
|
old_buf = set_buffer (_package_buf = create_buffer ("Packages", NULL, 1));
|
|
delete_line ();
|
|
set_buffer (old_buf);
|
|
}
|
|
|
|
void _call_on_packages ()
|
|
{
|
|
_call_packages ("_on");
|
|
}
|
|
|
|
void _call_first_packages ()
|
|
{
|
|
string filename;
|
|
|
|
inq_names (NULL, NULL, filename);
|
|
|
|
if (inq_message () == "New file (unable to open " + filename + ").")
|
|
_call_packages ("_new");
|
|
else
|
|
_call_packages ("_existing");
|
|
|
|
_call_packages ("_first");
|
|
}
|
|
|
|
/*
|
|
** _call_packages:
|
|
**
|
|
** This macro is the main event dispatcher for the BRIEF language
|
|
** package system. It calls packages specified in the BPACKAGES
|
|
** environment variable in response to events generated by the system or
|
|
** by the user using some housekeeping information generated by
|
|
** _parse_packages that allows rapid access to them.
|
|
*/
|
|
|
|
void _call_packages (string event_name)
|
|
{
|
|
int old_buf,
|
|
loc;
|
|
|
|
string file_ext;
|
|
|
|
inq_names (NULL, file_ext);
|
|
|
|
if (_curr_active != "")
|
|
_reset_packages ();
|
|
|
|
old_buf = set_buffer (_package_buf);
|
|
top_of_buffer ();
|
|
|
|
/*
|
|
** First, we check to see if we've already encounted this extension
|
|
** and event. If so, the information we need is present in the
|
|
** package buffer, and we call the macros therein.
|
|
**
|
|
** If either the extension or event haven't been encountered before,
|
|
** we call _parse_packages to do more work.
|
|
*/
|
|
|
|
if (loc = search_fwd ("." + file_ext + event_name + ";", 0))
|
|
{
|
|
string to_call,
|
|
off_macro;
|
|
|
|
next_char (loc - 1);
|
|
to_call = trim (read ());
|
|
set_buffer (old_buf);
|
|
|
|
while (strlen (to_call))
|
|
{
|
|
if (!(loc = index (to_call, ",")))
|
|
loc = strlen (to_call) + 1;
|
|
|
|
if (event_name != "_on")
|
|
execute_macro (substr (to_call, 1, loc - 1));
|
|
else if ((off_macro = execute_macro (substr (to_call, 1, loc - 1))) != "")
|
|
_curr_active += (strlen (_curr_active) ? ";" : "") + off_macro;
|
|
|
|
to_call = substr (to_call, loc + 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
set_buffer (old_buf);
|
|
_parse_packages (event_name);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** _reset_packages:
|
|
**
|
|
** This macro processes the "off" event. Any macros that returned
|
|
** "off" event macros from their event handles have those strings kept
|
|
** in the _curr_active global variable. These macros are called here.
|
|
**
|
|
** Note that "off" macro strings should never contain semicolons.
|
|
*/
|
|
|
|
void _reset_packages ()
|
|
{
|
|
int loc;
|
|
|
|
while (strlen (_curr_active))
|
|
{
|
|
if (!(loc = index (_curr_active, ";")))
|
|
loc = strlen (_curr_active) + 1;
|
|
|
|
execute_macro (substr (_curr_active, 1, loc - 1));
|
|
_curr_active = substr (_curr_active, loc + 1);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** _evaluate_package:
|
|
**
|
|
** This macro takes a macro name and an extension and constructs a
|
|
** package name from it, using all of the possible equivalent extensions.
|
|
*/
|
|
|
|
string _evaluate_package (string extension, string macro_name)
|
|
{
|
|
int old_buf = set_buffer (_package_buf),
|
|
len;
|
|
|
|
string package_name;
|
|
|
|
top_of_buffer ();
|
|
|
|
if (len = search_fwd ("." + extension + macro_name + ";", 0))
|
|
{
|
|
next_char (len - 1);
|
|
package_name = trim (read ());
|
|
}
|
|
else
|
|
{
|
|
int loc;
|
|
|
|
string equivalents;
|
|
|
|
next_char (search_fwd ("." + escape_re (extension) + "_equivalents") - 1);
|
|
equivalents = extension + trim (read ()) + "==";
|
|
|
|
while (loc = index (equivalents, "="))
|
|
if (inq_macro (package_name = (loc != 1 ? "." : "") + substr (equivalents, 1, loc - 1) + macro_name))
|
|
break;
|
|
else
|
|
equivalents = substr (equivalents, loc + 1);
|
|
|
|
beginning_of_line ();
|
|
insert ("." + extension + macro_name + ";" + package_name + "\n");
|
|
}
|
|
set_buffer (old_buf);
|
|
returns (package_name);
|
|
}
|
|
|
|
/*
|
|
** _parse_packages:
|
|
**
|
|
** This macro deals with more extensive package expansion the first time
|
|
** a file is read, and the first time an event is executed.
|
|
*/
|
|
|
|
void _parse_packages (string event_name)
|
|
{
|
|
string bpackages,
|
|
to_call,
|
|
mac_name,
|
|
file_ext,
|
|
escaped_ext,
|
|
equivalents,
|
|
pattern;
|
|
|
|
int loc,
|
|
pass,
|
|
file_buf;
|
|
|
|
/*
|
|
** First, we get the event type and the extension of the file.
|
|
*/
|
|
|
|
inq_names (NULL, file_ext);
|
|
escaped_ext = escape_re (file_ext);
|
|
|
|
/*
|
|
** The global _package_buf contains housekeeping information that
|
|
** allows the language system to keep parsed event information around
|
|
** for the duration of the editing session. Each event occupies one
|
|
** line in the buffer (per extension), and one additional line is
|
|
** taken per extension to save the pre-parsed packages and arguments.
|
|
**
|
|
** Extensions are always found together in the package buffer; the
|
|
** events come first, followed by the pre-parsed information. This
|
|
** allows the search pattern to fall through to the pre-parsed
|
|
** information if the event hasn't occurred yet.
|
|
**
|
|
** The format of the strings is:
|
|
**
|
|
** .[extension][event name, or nothing if pre_parse];[information]
|
|
*/
|
|
|
|
file_buf = set_buffer (_package_buf);
|
|
top_of_buffer ();
|
|
|
|
/*
|
|
** This search locates any string in the buffer that corresponds
|
|
** to the current extension. If the event is in the buffer,
|
|
** _call_packages will have already dealt with it. If not, we try
|
|
** to locate the pre_parse information here.
|
|
*/
|
|
|
|
if (loc = search_fwd ("." + file_ext + ";", 0))
|
|
{
|
|
next_char (loc - 1);
|
|
to_call = trim (read ());
|
|
file_ext = "." + file_ext;
|
|
beginning_of_line ();
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
** If no pre-parse information is present in the buffer for
|
|
** the given extension, the BPACKAGES string must be retrieved
|
|
** from the environment and parsed. Note that this will only
|
|
** happen once per extension.
|
|
**
|
|
** Since the BPACKAGES string is quite complex, and a given
|
|
** extension can appear more than once (and with a number of
|
|
** different packages), the parser loops and searches for the
|
|
** extension until it can't be located any more.
|
|
**
|
|
** If the extension isn't found at all, the "default"
|
|
** extension is used and a second pass is made over the
|
|
** BPACKAGES string.
|
|
**
|
|
** If neither the extension nor the "default" extension is
|
|
** found, no events are generated.
|
|
*/
|
|
|
|
int located;
|
|
|
|
pattern = "<|[,;] @\\c" + escaped_ext + " @{[,\\-][~\\-,;:]@}@:[~:\n;]+[\n;]";
|
|
bpackages = trim (ltrim (compress (inq_environment ("BPACKAGES")))) + "\n";
|
|
|
|
while (pass != 2 && !located)
|
|
{
|
|
while (loc = search_string (pattern, bpackages, NULL, NULL, 0))
|
|
{
|
|
string curr_ext,
|
|
this_package_list;
|
|
|
|
++located;
|
|
|
|
bpackages = substr (bpackages, loc);
|
|
|
|
if (loc = index (bpackages, ";"))
|
|
{
|
|
this_package_list = substr (bpackages, 1, loc - 1);
|
|
bpackages = substr (bpackages, loc + 1);
|
|
}
|
|
else
|
|
{
|
|
this_package_list = trim (bpackages);
|
|
bpackages = "";
|
|
}
|
|
/*
|
|
** Now we have the "raw" BPACKAGES information.
|
|
** At this point, we need to check for extension
|
|
** substitution.
|
|
*/
|
|
|
|
if ((loc = index (this_package_list, "-")) && loc < index (this_package_list, ":"))
|
|
{
|
|
this_package_list = substr (this_package_list, loc + 1);
|
|
|
|
if (!((loc = index (this_package_list, ",")) && loc < index (this_package_list, ":")))
|
|
loc = index (this_package_list, ":");
|
|
|
|
curr_ext = trim (ltrim (substr (this_package_list, 1, loc - 1)));
|
|
equivalents += "=" + curr_ext;
|
|
}
|
|
else
|
|
curr_ext = file_ext;
|
|
|
|
/*
|
|
** All of the pre-expansion packages for the
|
|
** current extension are combined together in the
|
|
** to_call string separated by commas. This
|
|
** string is used later to expand and call the
|
|
** individual package macros, and is inserted
|
|
** into the buffer as pre-parse information
|
|
** once the initial parse has been completed.
|
|
*/
|
|
|
|
to_call += (strlen (to_call) ? ",=" : "=") + curr_ext + ",," + substr (this_package_list, index (this_package_list, ":") + 1);
|
|
}
|
|
/*
|
|
** If we can't find the extension any more and we've
|
|
** located some packages to call, the parse is over.
|
|
** Otherwise, we use "default" as the extension and try
|
|
** one more time.
|
|
*/
|
|
|
|
pattern = "<|[,;] @\\cdefault @{[,\\-][~\\-,;:]@}@:[~:\n;]+[\n;]";
|
|
++pass;
|
|
}
|
|
file_ext = "." + file_ext;
|
|
|
|
/*
|
|
** If the package buffer didn't contain any information about
|
|
** the current file extension, we insert the pre-parse information
|
|
** at the top of the buffer. This information can be used later
|
|
** to avoid doing an additional BPACKAGES parse.
|
|
*/
|
|
|
|
beginning_of_line ();
|
|
|
|
/*
|
|
** The extra comma here is used to generate a "null"
|
|
** event, which, for the first event, looks something like:
|
|
**
|
|
** _first or .c_first
|
|
**
|
|
** These are generated to allow packages to chain into
|
|
** various events without requiring them to be listed in
|
|
** the BPACKAGES environment variable. Note that each
|
|
** given extension in the parsed BPACKAGES string also checks
|
|
** for generic event macros. This ensures that all of the macros
|
|
** for a given extension or set of extensions are called.
|
|
*/
|
|
|
|
to_call = "=" + substr (file_ext, 2) + ",," + to_call;
|
|
insert (file_ext + ";" + to_call + "\n");
|
|
|
|
/*
|
|
** We move up here because the event information must
|
|
** appear before the pre-parse information, and the "\n"
|
|
** that was inserted moves us down a line.
|
|
*/
|
|
|
|
up ();
|
|
|
|
/*
|
|
** We also insert the list of equivalent extensions at
|
|
** this point in the buffer. This list can be used by other
|
|
** extension-type macros that want to know about equivalence.
|
|
*/
|
|
|
|
insert (file_ext + "_equivalents" + equivalents + "\n");
|
|
}
|
|
set_buffer (file_buf);
|
|
bpackages = "";
|
|
|
|
/*
|
|
** At this point, we've got a list of pre-expanded package
|
|
** names that look something like "s,pvcs,wp". These have to
|
|
** be expanded and converted into the event and extension
|
|
** specific variety. Each individual package is separated and
|
|
** passed to process_package, which expands the name (if
|
|
** necessary), handles extension-specific and generic naming
|
|
** convensions, deals with parameters, and the like. Once
|
|
** completed, the expanded package name (if any) is returned
|
|
** and saved in another string that will be inserted as the
|
|
** event-specific information in the package buffer.
|
|
*/
|
|
|
|
while (strlen (to_call))
|
|
{
|
|
if (!(loc = index (to_call, ",")))
|
|
loc = strlen (to_call) + 1;
|
|
|
|
if (substr (to_call, 1, 1) == "=")
|
|
escaped_ext = substr (to_call, 2, loc - 2);
|
|
else
|
|
{
|
|
mac_name = process_package (event_name, ltrim (trim (substr (to_call, 1, loc - 1))), bpackages, escaped_ext);
|
|
|
|
if (strlen (mac_name))
|
|
bpackages += (strlen (bpackages) ? "," : "") + mac_name;
|
|
}
|
|
to_call = substr (to_call, loc + 1);
|
|
}
|
|
/*
|
|
** Since the event-specific information wasn't found in the buffer,
|
|
** it's inserted here.
|
|
*/
|
|
|
|
file_buf = inq_buffer ();
|
|
set_buffer (_package_buf);
|
|
insert (file_ext + event_name + ";" + bpackages + "\n");
|
|
set_buffer (file_buf);
|
|
}
|
|
|
|
/*
|
|
** process_package:
|
|
**
|
|
** This macro converts generic package names into their generic or
|
|
** extension-specific macro name counterparts.
|
|
*/
|
|
|
|
string process_package (string event_name, string mac_name, string existing_packages, string file_ext)
|
|
{
|
|
string real_ext,
|
|
initial_mac,
|
|
parms;
|
|
|
|
int loc,
|
|
processed,
|
|
length;
|
|
|
|
existing_packages = "," + existing_packages + ",";
|
|
|
|
inq_names (NULL, real_ext);
|
|
|
|
file_ext = "." + file_ext;
|
|
real_ext = "." + real_ext;
|
|
|
|
if (loc = index (mac_name, " "))
|
|
{
|
|
parms = substr (mac_name, loc + 1);
|
|
mac_name = substr (mac_name, 1, loc - 1);
|
|
}
|
|
|
|
/*
|
|
** If part of the package name is found in the
|
|
** abbreviation list, the full name is retrieved and used
|
|
** instead. Note that the macros in the abbreviation list
|
|
** could be demotable groups (separated by commas).
|
|
*/
|
|
|
|
initial_mac = _package_abbrev ();
|
|
|
|
if (strlen (mac_name) && (loc = search_string ("[,;]" + mac_name + "*;", initial_mac, length)))
|
|
initial_mac = substr (initial_mac, loc + 1, length - 2);
|
|
else
|
|
initial_mac = mac_name;
|
|
|
|
/*
|
|
** This loop searches for specific and generic event
|
|
** handler macros. First, specific macros are searched
|
|
** for. If none are found, the last member of the
|
|
** demotable group is searched for generically. This is
|
|
** because generic higher level demotion macros don't
|
|
** make any sense (they'd override anything else, and
|
|
** wouldn't leave anything to demote to).
|
|
*/
|
|
|
|
while (!processed)
|
|
{
|
|
if (!(loc = index (initial_mac, ",")))
|
|
loc = strlen (initial_mac) + 1;
|
|
|
|
mac_name = substr (initial_mac, 1, loc - 1);
|
|
initial_mac = substr (initial_mac, loc + 1);
|
|
|
|
mac_name = (strlen (mac_name) ? "_" : "") + mac_name + event_name;
|
|
|
|
if (processed = inq_macro (real_ext + mac_name))
|
|
mac_name = real_ext + mac_name;
|
|
else if (real_ext != file_ext && (processed = inq_macro (file_ext + mac_name)))
|
|
mac_name = file_ext + mac_name;
|
|
else if (!strlen (initial_mac))
|
|
{
|
|
processed = inq_macro (mac_name);
|
|
break;
|
|
}
|
|
}
|
|
/*
|
|
** Here we check to see if the macro has been called
|
|
** before. If so, we ignore it.
|
|
*/
|
|
|
|
if (processed && index (existing_packages, "," + mac_name + ","))
|
|
processed = 0;
|
|
|
|
/*
|
|
** If the macro has been processed & located, we call it with its
|
|
** parameters.
|
|
*/
|
|
|
|
if (processed)
|
|
{
|
|
string generic_macro_called;
|
|
|
|
if (strlen (parms))
|
|
mac_name += " " + parms;
|
|
|
|
/*
|
|
** If current event is an "on" event, we retain the return
|
|
** value (if non-null) as an "off" event macro name.
|
|
**
|
|
** Note also that we ensure that a generically named generic
|
|
** event macro is always called before the specifically named one,
|
|
** if any.
|
|
*/
|
|
|
|
if (initial_mac == "" && event_name != mac_name && !index (existing_packages, "," + event_name + ",") && inq_macro (event_name))
|
|
{
|
|
generic_macro_called = event_name + ",";
|
|
|
|
if ((initial_mac = execute_macro (event_name)) != "" && event_name == "_on")
|
|
_curr_active += (strlen (_curr_active) ? ";" : "") + initial_mac;
|
|
}
|
|
if ((initial_mac = execute_macro (mac_name)) != "" && event_name == "_on")
|
|
_curr_active += (strlen (_curr_active) ? ";" : "") + initial_mac;
|
|
|
|
returns (generic_macro_called + mac_name);
|
|
}
|
|
else
|
|
returns ("");
|
|
}
|
|
|
|
/*
|
|
** _package_abbrev:
|
|
**
|
|
** This macro is at the terminus of the chain. It returns a string
|
|
** consisting of the abbreviations and demotable groups for the built-in
|
|
** BRIEF packages (indenting, word processing, pvcs and tlib).
|
|
*/
|
|
|
|
string _package_abbrev ()
|
|
{
|
|
returns (";template,smart,regular;pvcs;wp;tlib;vc;"); /*vc*/
|
|
}
|