A C++ library for scripting and automating IBM TN5250 terminal emulator interactions.
- Full scripting language with lexer, parser, AST, and executor pipeline.
- Screen interaction commands:
-
TYPEtext input andPRESSkey simulation (AID keys and local keys). -
EXPECTconditions to wait for screen content, cursor position, or keyboard state. -
EXTRACTdata from screen text, fields, cursor position, and rows. -
MOVE CURSORwith absolute coordinates, field index, or directional movement.
-
- Variable system with
SET,INC,DEC,ADD, and string interpolation. - Built-in variables set by the executor:
-
$SCREEN_ROWS,$SCREEN_COLS: screen size. -
$CURSOR_ROW,$CURSOR_COL: cursor position (1-based). -
$KEYBOARD_STATE:UNLOCKED,LOCKED,ERRORLOCKEDorSYSTEMREQUEST. -
$MESSAGE_WAITING:1when the message-waiting indicator is on, otherwise0. -
$EXPECT_RESULT:OKorTIMEOUTfor the lastEXPECT. -
$REPEAT_INDEX: current iteration of the innermostREPEAT, starting at0.
-
- Control flow:
-
IF/ELSE/ENDIFconditionals with==,!=,<,>,<=,>=,CONTAINS,ISSET. - Compound conditions with
AND,OR,NOT, and parenthesized grouping. -
WHILE/ENDWHILEandREPEAT/ENDREPEATloops. -
LABEL/GOTOfor jump-based flow control.
-
- User-defined functions with
DEF/ENDDEF,CALL,RETURN, and parameter scoping. - Error handling with
ON TIMEOUT GOTOandON ERROR GOTO. - Timing control with
WAIT,GLOBAL DELAY,GLOBAL JITTER, andGLOBAL EXPECT_TIMEOUT. - Utility commands:
LOG,PAUSE,INPUT,ABORT.-
INPUT PASSWORD "Password:" $PASSasks for a secret:inputRequestedflags the field as masked, andLOG/ABORTmessages show********for that variable (and for values built from it) whileTYPEsends the real value.
-
- Script metadata via comments (
# @script.name,# @script.author,# @menu.path). - Abstract
ScreenInterfacefor integration with any Qt-based host application. - Editor support: token classification for syntax colouring, context-aware completion, linting, and formatting.
- Comprehensive test suite with 160+ test cases.
5250script uses CMake and requires C++20 and Qt6:
cmake -B build -DBUILD_5250SCRIPT_TESTS=ON
cmake --build build
ctest --test-dir build
#include <5250script/script_parser.h>
#include <5250script/script_executor.h>
// 1. Parse the script
ScriptParser parser;
ParseResult result = parser.parse(scriptText);
if (result.hasErrors()) {
for (const auto &err : result.errors) {
qDebug() << "Line" << err.line << ":" << err.message;
}
return;
}
// 2. Create executor with screen implementation
ScriptExecutor executor;
executor.setScreen(screenImpl);
// 3. Set initial variables
QHash<QString, QString> vars;
vars["USERNAME"] = "QSECOFR";
vars["PASSWORD"] = "PASSWORD";
executor.setInitialVariables(vars);
// 4. Connect signals
connect(&executor, &ScriptExecutor::injectAIDKey,
this, &MainWindow::handleAIDKey);
connect(&executor, &ScriptExecutor::executionFinished,
this, &MainWindow::onScriptFinished);
// 5. Execute
executor.execute(result);The editor helpers only need Qt Core and reuse the lexer and parser, so an editor built on them accepts exactly what the executor runs.
#include <5250script/script_completion.h>
#include <5250script/script_formatter.h>
#include <5250script/script_language.h>
#include <5250script/script_linter.h>
using namespace core::scripting;
// Syntax colouring: classify each token of a line. ScriptToken::column and
// ::length give the exact source span; comments start at commentStart().
ScriptLexer lexer;
TokenLine tokens = lexer.tokenizeLine(lineText, lineNumber);
QVector<language::TokenCategory> categories = language::classifyLine(tokens, lineText);
int commentAt = language::commentStart(lineText);
// Completion at a cursor (1-based line, 0-based column): replace the text
// from replaceStart to the cursor with the chosen item's insertText.
CompletionResult completion = complete(scriptText, line, column);
// Problems: parser errors plus unused-variable and unreachable-code warnings
for (const Diagnostic &d : lint(scriptText))
qDebug() << d.line << d.column << d.length << d.message;
// Re-indent blocks and strip trailing whitespace
QString formatted = formatScript(scriptText);# @script.name = "Login Script"
# @script.author = "Admin"
# Set credentials
SET $user "QSECOFR"
SET $pass "PASSWORD"
# Wait for login screen
EXPECT TEXT "User" AT ROW 6
MOVE CURSOR AT INPUTFIELD 1
TYPE $user
PRESS TAB
TYPE $pass
PRESS ENTER
# Verify login succeeded
EXPECT TEXT "Main Menu"
LOG "Login successful"
-
Automating a login with retry logic:
DEF login($user, $pass) EXPECT TEXT "User" AT ROW 6 MOVE CURSOR AT INPUTFIELD 1 TYPE $user PRESS TAB TYPE $pass PRESS ENTER ENDDEF ON TIMEOUT GOTO retry GLOBAL EXPECT_TIMEOUT 5000 SET $attempts 0 LABEL retry INC $attempts IF $attempts > 3 ABORT "Login failed after 3 attempts" ENDIF CALL login("QSECOFR", "PASSWORD") EXPECT TEXT "Main Menu" LOG "Logged in after $attempts attempt(s)" -
Extracting screen data with conditions:
EXPECT KEYBOARD UNLOCKED EXTRACT $title FROM 1 1 LENGTH 40 IF $title CONTAINS "Error" LOG "Error screen detected: $title" ABORT "Unexpected error screen" ENDIF SET $row 1 WHILE $row <= 24 EXTRACT $line LINE $row IF $line CONTAINS "MSGW" LOG "Message waiting on row $row" ENDIF INC $row ENDWHILE
Pull requests are welcome. Feel free to open an issue if you want to add other features.
