Building Your First App in JBasic: Step-by-Step TutorialJBasic is a compact, readable dialect of BASIC designed for rapid prototyping, teaching programming fundamentals, and building small desktop and embedded applications. This tutorial walks you through building your first simple but complete JBasic application: a task tracker with a text-based user interface, persistent storage, and basic filtering. You’ll learn core JBasic syntax, program structure, input/output handling, file operations, and simple data structures.
What you’ll build
A command-line task tracker that supports:
- Adding tasks with title, description, priority, and status
- Listing tasks with optional filtering by status or priority
- Marking tasks as completed
- Saving and loading tasks to a local file
This app is small but touches the key features you need to scale up to larger projects.
Prerequisites
- JBasic runtime/interpreter installed on your system (refer to your JBasic distribution for install instructions).
- A plain text editor (VS Code, Sublime, Notepad++, etc.).
- Basic familiarity with programming concepts (variables, loops, conditionals).
Project structure
All code will reside in a single file named:
tasktracker.jb
Persistent data will be stored in:
tasks.db
Step 1 — Program outline and main loop
Start with the program skeleton: an initialization step, a main menu loop, and a cleanup routine.
REM tasktracker.jb - a simple task tracker in JBasic CONST DB_FILE = "tasks.db" DIM tasks(100) AS STRING DIM taskCount AS INTEGER SUB main() call loadTasks() DO call showMenu() choice = input("Enter choice: ") SELECT CASE choice CASE "1" call addTask() CASE "2" call listTasks() CASE "3" call completeTask() CASE "4" call saveTasks() PRINT "Tasks saved." CASE "5" EXIT DO CASE ELSE PRINT "Invalid choice." END SELECT LOOP call saveTasks() PRINT "Goodbye!" END SUB CALL main()
Notes:
- CONST, DIM, SUB, CALL, DO…LOOP are used here per typical JBasic syntax. Adjust keywords if your JBasic version uses slightly different names.
Step 2 — Data model and helper routines
We’ll store each task as a single delimited string: Title|Description|Priority|Status. Add helper routines to create and parse these task strings.
FUNCTION makeTask(title, desc, priority, status) AS STRING RETURN title + "|" + desc + "|" + priority + "|" + status END FUNCTION FUNCTION parseTask(taskStr) AS ARRAY parts = split(taskStr, "|") REM split returns an array RETURN parts END FUNCTION
If your JBasic doesn’t include split(), implement a simple splitter:
FUNCTION split(s, delim) AS ARRAY DIM res(0) AS STRING start = 1 idx = instr(s, delim) WHILE idx > 0 part = mid$(s, start, idx - start) res = append(res, part) start = idx + len(delim) idx = instr(s, delim, start) WEND res = append(res, mid$(s, start)) RETURN res END FUNCTION
Step 3 — Loading and saving tasks
Implement persistent storage with simple line-based file I/O.
SUB loadTasks() taskCount = 0 IF NOT fileExists(DB_FILE) THEN RETURN OPEN DB_FILE FOR INPUT AS #1 WHILE NOT EOF(1) LINE INPUT #1, line$ IF len(line$) > 0 THEN taskCount = taskCount + 1 tasks(taskCount) = line$ END IF WEND CLOSE #1 PRINT taskCount; " tasks loaded." END SUB SUB saveTasks() OPEN DB_FILE FOR OUTPUT AS #1 FOR i = 1 TO taskCount PRINT #1, tasks(i) NEXT i CLOSE #1 END SUB
If your JBasic uses different file I/O syntax, adapt accordingly (e.g., USING, WRITE, etc.).
Step 4 — Adding a task
Create a routine to collect user input and append a new task.
SUB addTask() title$ = input("Title: ") desc$ = input("Description: ") priority$ = input("Priority (Low/Medium/High): ") status$ = "Open" taskStr = makeTask(title$, desc$, priority$, status$) taskCount = taskCount + 1 tasks(taskCount) = taskStr PRINT "Task added (#"; taskCount; ")." END SUB
Validation tips:
- Trim whitespace and set defaults if empty.
- Normalize priority to a fixed set.
Step 5 — Listing and filtering tasks
Display tasks with optional filters.
SUB listTasks() filter = input("Filter by (status/priority/none): ") IF lower$(filter) = "none" OR filter = "" THEN filter = "none" PRINT "ID | Priority | Status | Title" PRINT "---------------------------------" FOR i = 1 TO taskCount parts = parseTask(tasks(i)) title$ = parts(0) desc$ = parts(1) priority$ = parts(2) status$ = parts(3) show = FALSE IF filter = "none" THEN show = TRUE IF lower$(filter) = "status" THEN fstatus = input("Show status (Open/Done): ") IF lower$(status$) = lower$(fstatus) THEN show = TRUE ELSEIF lower$(filter) = "priority" THEN fprio = input("Show priority (Low/Medium/High): ") IF lower$(priority$) = lower$(fprio) THEN show = TRUE END IF IF show THEN PRINT i; " | "; priority$; " | "; status$; " | "; title$ END IF NEXT i END SUB
Note: For better UX, request filter values before looping.
Step 6 — Marking tasks completed
Allow marking a specific task as done.
SUB completeTask() id$ = input("Enter task ID to mark complete: ") id = val(id$) IF id < 1 OR id > taskCount THEN PRINT "Invalid ID." RETURN END IF parts = parseTask(tasks(id)) parts(3) = "Done" tasks(id) = parts(0) + "|" + parts(1) + "|" + parts(2) + "|" + parts(3) PRINT "Task #"; id; " marked as Done." END SUB
Step 7 — Polishing and error handling
- Add input validation (non-empty title, valid priority).
- Protect against overly long arrays: if taskCount >= UBOUND(tasks) then resize or reject new entries.
- Handle file I/O errors with ON ERROR or equivalent.
- Consider adding delete and edit functionality.
Example run
A sample interaction (user input after prompts):
Title: Buy groceries
Description: Milk, eggs, bread
Priority (Low/Medium/High): Medium
Task added (#1).
List -> shows task with ID 1, Medium, Open, Buy groceries
Complete ID 1 -> Task #1 marked as Done
Save -> “Tasks saved.” Exit -> “Goodbye!”
Extensions you can add
- GUI using available JBasic GUI libraries (if present) for windows, buttons, and dialogs.
- Tagging and search features.
- Due dates with simple date parsing and sorting.
- Export/import JSON or CSV for compatibility.
Full source (put together)
REM Full tasktracker.jb (basic) CONST DB_FILE = "tasks.db" DIM tasks(100) AS STRING DIM taskCount AS INTEGER FUNCTION makeTask(title, desc, priority, status) AS STRING RETURN title + "|" + desc + "|" + priority + "|" + status END FUNCTION FUNCTION parseTask(taskStr) AS ARRAY RETURN split(taskStr, "|") END FUNCTION FUNCTION split(s, delim) AS ARRAY DIM res(0) AS STRING start = 1 idx = instr(s, delim) WHILE idx > 0 part = mid$(s, start, idx - start) res = append(res, part) start = idx + len(delim) idx = instr(s, delim, start) WEND res = append(res, mid$(s, start)) RETURN res END FUNCTION SUB loadTasks() taskCount = 0 IF NOT fileExists(DB_FILE) THEN RETURN OPEN DB_FILE FOR INPUT AS #1 WHILE NOT EOF(1) LINE INPUT #1, line$ IF len(line$) > 0 THEN taskCount = taskCount + 1 tasks(taskCount) = line$ END IF WEND CLOSE #1 PRINT taskCount; " tasks loaded." END SUB SUB saveTasks() OPEN DB_FILE FOR OUTPUT AS #1 FOR i = 1 TO taskCount PRINT #1, tasks(i) NEXT i CLOSE #1 END SUB SUB addTask() title$ = input("Title: ") IF trim$(title$) = "" THEN PRINT "Title cannot be empty." RETURN END IF desc$ = input("Description: ") priority$ = input("Priority (Low/Medium/High): ") IF len(priority$) = 0 THEN priority$ = "Low" status$ = "Open" taskStr = makeTask(title$, desc$, priority$, status$) taskCount = taskCount + 1 tasks(taskCount) = taskStr PRINT "Task added (#"; taskCount; ")." END SUB SUB listTasks() filterType = input("Filter by (none/status/priority): ") IF lower$(filterType) = "none" OR filterType = "" THEN filterType = "none" IF lower$(filterType) = "status" THEN fstatus = input("Status to show (Open/Done): ") IF lower$(filterType) = "priority" THEN fprio = input("Priority to show (Low/Medium/High): ") PRINT "ID | Priority | Status | Title" PRINT "---------------------------------" FOR i = 1 TO taskCount parts = parseTask(tasks(i)) title$ = parts(0) desc$ = parts(1) priority$ = parts(2) status$ = parts(3) show = FALSE IF filterType = "none" THEN show = TRUE IF lower$(filterType) = "status" AND lower$(status$) = lower$(fstatus) THEN show = TRUE IF lower$(filterType) = "priority" AND lower$(priority$) = lower$(fprio) THEN show = TRUE IF show THEN PRINT i; " | "; priority$; " | "; status$; " | "; title$ END IF NEXT i END SUB SUB completeTask() id$ = input("Enter task ID to mark complete: ") id = val(id$) IF id < 1 OR id > taskCount THEN PRINT "Invalid ID." RETURN END IF parts = parseTask(tasks(id)) parts(3) = "Done" tasks(id) = parts(0) + "|" + parts(1) + "|" + parts(2) + "|" + parts(3) PRINT "Task #"; id; " marked as Done." END SUB SUB showMenu() PRINT PRINT "TaskTracker - Menu" PRINT "1) Add Task" PRINT "2) List Tasks" PRINT "3) Complete Task" PRINT "4) Save Tasks" PRINT "5) Exit" END SUB SUB main() call loadTasks() DO call showMenu() choice = input("Enter choice: ") SELECT CASE choice CASE "1" call addTask() CASE "2" call listTasks() CASE "3" call completeTask() CASE "4" call saveTasks() PRINT "Tasks saved." CASE "5" EXIT DO CASE ELSE PRINT "Invalid choice." END SELECT LOOP call saveTasks() PRINT "Goodbye!" END SUB CALL main()
Final notes
- Adjust syntax for your specific JBasic implementation (function names like input(), string functions, file I/O may differ).
- This example emphasizes clear structure and simple persistence; it’s a solid foundation for learning JBasic and adding features like GUI, concurrency, or network sync.