2026-07-18 18:48:23 +02:00
# include <iostream>
# include "error.h"
# include "command.h"
# include "log.h"
# include "argv.h"
# include "file.h"
# include "util.h"
# include "machine.h"
# include "show.h"
Klammerset: the @@@klammerset construct, its search path, and const correctness
The @@@klammerset system command formally declares a klammerset — a
named, logically related group of klammer definitions — with an
operative, idempotent declaration (:requires and :files load in order
at the declaration point, relative to the declaring file). A bare
symbol given to ktext -k, kdesc --input, or :requires resolves to
x/x.k on the search path: the document's directory, then
KLAMMERTEXT_KLAMMERSETS, then KLAMMERTEXT_HOME; kdesc --klammerset
lists the available sets. sks/sks.k is the first declared klammerset,
so `-k sks` loads the SKS by name. The engine's lookup classes were
renamed *_set → *_registry to keep the two concepts apart, and the
whole C++ tree now follows standard const-correctness conventions.
tst/ gains klammerset_test.sh (18 cases).
(from dev 64b1abf23e56)
2026-07-30 23:50:07 +02:00
# include "target_registry.h"
# include "klammerset_registry.h"
2026-07-18 18:48:23 +02:00
int main ( int argc , char * argv [ ] )
{
try {
set_verbose_level ( argc , argv ) ;
Argv args { } ;
args . req ( " filenames " , " Input files in Klammertext format. " , " 'list' " ) ;
args . opt ( " s " , " Text processed before input files. " , " input-string " , " " , " 'text' " ) ;
args . opt ( " t " , " Output target; default is general (unspecified) " , " target " ,
Klammerset: the @@@klammerset construct, its search path, and const correctness
The @@@klammerset system command formally declares a klammerset — a
named, logically related group of klammer definitions — with an
operative, idempotent declaration (:requires and :files load in order
at the declaration point, relative to the declaring file). A bare
symbol given to ktext -k, kdesc --input, or :requires resolves to
x/x.k on the search path: the document's directory, then
KLAMMERTEXT_KLAMMERSETS, then KLAMMERTEXT_HOME; kdesc --klammerset
lists the available sets. sks/sks.k is the first declared klammerset,
so `-k sks` loads the SKS by name. The engine's lookup classes were
renamed *_set → *_registry to keep the two concepts apart, and the
whole C++ tree now follows standard const-correctness conventions.
tst/ gains klammerset_test.sh (18 cases).
(from dev 64b1abf23e56)
2026-07-30 23:50:07 +02:00
Target_registry : : general_name , " 'word' " ) ;
2026-07-18 18:48:23 +02:00
args . opt ( " o " , " Output basename; meaning and default defined by target. " , " basename " ,
" " , " 'word' " ) ;
Klammerset: the @@@klammerset construct, its search path, and const correctness
The @@@klammerset system command formally declares a klammerset — a
named, logically related group of klammer definitions — with an
operative, idempotent declaration (:requires and :files load in order
at the declaration point, relative to the declaring file). A bare
symbol given to ktext -k, kdesc --input, or :requires resolves to
x/x.k on the search path: the document's directory, then
KLAMMERTEXT_KLAMMERSETS, then KLAMMERTEXT_HOME; kdesc --klammerset
lists the available sets. sks/sks.k is the first declared klammerset,
so `-k sks` loads the SKS by name. The engine's lookup classes were
renamed *_set → *_registry to keep the two concepts apart, and the
whole C++ tree now follows standard const-correctness conventions.
tst/ gains klammerset_test.sh (18 cases).
(from dev 64b1abf23e56)
2026-07-30 23:50:07 +02:00
args . opt ( " k " , " Klammerset symbol (resolved on the klammerset search path) or the pathname of a klammerset definition file; default is the Standard Klammer Set. With a value of \" none \" , no klammerset is loaded. " ,
2026-07-18 18:48:23 +02:00
" pathname " , " " , " 'word' " ) ;
args . flag ( " d " , " Display the output to the screen, rather than writing files. " ) ;
args . flag ( " m " , " Show the Klammermachine state at the beginning of processing. " ) ;
args . opt ( " v " , " 'verbosity' " , " degree " , " 0 " , " 'verbosity' " ) ;
if ( show_usage ( argc , argv ) ) {
args . usage ( file_basename ( argv [ 0 ] ) ) ;
exit ( 1 ) ;
}
args . parse ( argc , argv ) ;
if ( verbose_level > 0 ) {
args . describe ( ) ;
}
std : : string input_text = args . as_string ( " s " ) ;
2026-07-26 23:22:59 +02:00
// argv boundaries are authoritative (a shell-quoted "my file.kt" is
// one element); group_filename_tokens adds the standalone-"/" list
// form, the existence rescue for unquoted spaces, and ~ expansion.
std : : vector < std : : string > input_filenames =
group_filename_tokens ( args . as_vector ( " filenames " ) ) ;
2026-07-18 18:48:23 +02:00
if ( input_text . empty ( ) & & input_filenames . empty ( ) ) {
throw Argument_error (
" You must specify input filenames and/or text " , Locator ( ) ) ;
}
auto [ target , output_dir , output_basename , output_filename ,
write_files , display_only ] =
2026-07-26 23:22:59 +02:00
parse_args ( input_filenames , args . as_string ( " t " ) ,
expand_tilde ( args . as_string ( " o " ) ) , args . as_bool ( " d " ) ) ;
2026-07-18 18:48:23 +02:00
if ( * ( output_filename . end ( ) - 1 ) = = ' * '
& & ! display_only ) {
throw Argument_error (
" You must specify an output target or "
" display the results with the \" -d \" flag. " ,
Locator ( ) ) ;
}
Machine M ;
M . m_state . open_frame ( " ktext " ) ;
M . m_state . set ( " K_target " , target ) ;
M . m_state . set ( " K_output_dir " , output_dir ) ;
M . m_state . set ( " K_output_basename " , output_basename ) ;
M . m_state . set ( " K_stdout_only " , display_only ? " true " : " false " ) ;
2026-07-26 23:22:59 +02:00
// The list form uses the standalone-"/" separator (filenames may
// contain spaces); K_input_filename is the single root input file.
M . m_state . set ( " K_input_filenames " , join ( input_filenames , " / " ) ) ;
M . m_state . set ( " K_input_filename " ,
input_filenames . empty ( ) ? " " : input_filenames [ 0 ] ) ;
2026-07-18 18:48:23 +02:00
M . m_state . set ( " K_verbose_level " , std : : to_string ( verbose_level ) ) ;
if ( ! input_filenames . empty ( ) ) {
M . m_state . set (
" K_input_dir " , absolute_pathname ( file_directory ( input_filenames [ 0 ] ) ) ) ;
} else {
M . m_state . set ( " K_input_dir " , fs : : current_path ( ) . string ( ) ) ;
}
std : : string klammerset_filename = args . as_string ( " k " ) ;
if ( klammerset_filename ! = " none " ) {
if ( klammerset_filename . empty ( ) ) {
klammerset_filename = M . m_state . value ( " KLAMMERTEXT_HOME " ) + " /sks/sks.k " ;
Klammerset: the @@@klammerset construct, its search path, and const correctness
The @@@klammerset system command formally declares a klammerset — a
named, logically related group of klammer definitions — with an
operative, idempotent declaration (:requires and :files load in order
at the declaration point, relative to the declaring file). A bare
symbol given to ktext -k, kdesc --input, or :requires resolves to
x/x.k on the search path: the document's directory, then
KLAMMERTEXT_KLAMMERSETS, then KLAMMERTEXT_HOME; kdesc --klammerset
lists the available sets. sks/sks.k is the first declared klammerset,
so `-k sks` loads the SKS by name. The engine's lookup classes were
renamed *_set → *_registry to keep the two concepts apart, and the
whole C++ tree now follows standard const-correctness conventions.
tst/ gains klammerset_test.sh (18 cases).
(from dev 64b1abf23e56)
2026-07-30 23:50:07 +02:00
} else if ( is_klammerset_symbol ( klammerset_filename ) ) {
// A bare symbol resolves on the klammerset search path;
// the local stage is the input document's directory (the
// cwd when the input is a string).
klammerset_filename = resolve_klammerset_symbol (
klammerset_filename , M . m_state . value ( " K_input_dir " ) , Locator ( ) ) . string ( ) ;
2026-07-18 18:48:23 +02:00
}
K : : log ( 1 , " Reading klammerset filename: " + klammerset_filename ) ;
M . read ( fs : : path ( klammerset_filename ) ) ;
}
if ( ! input_text . empty ( ) ) {
M . read ( input_text + " \n " ) ;
}
for ( auto p : input_filenames ) {
M . read ( fs : : path ( p ) ) ;
}
if ( args . as_bool ( " m " ) ) {
std : : cout < < M < < " \n " ;
}
std : : string result = trim ( M . apply ( target ) ) ;
if ( display_only & & ! result . empty ( ) ) {
std : : cout < < result < < " \n " ;
} else if ( ! result . empty ( ) ) {
msg ( ) < < " Output filename: " < < output_filename < < " \n " ;
string_to_file ( output_filename , result + " \n " ) ;
( void ) K : : log ( 1 , " Wrote file: " + output_filename ) ;
}
}
catch ( Error & err ) {
err . print_message ( ) ;
std : : cout < < " \n " ;
return 1 ;
}
return 0 ;
}