start . me .
Directory path . web , gh , sbul .

HTML Document File : editor.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title> SBUL: Simple Basic Uniform Language </title>
</head>

<body>

    <div><a href="https://github.com/arkenidar/sbul">github.com/arkenidar/sbul</a>
        SBUL: a Simple Basic Uniform Language.
        an exploration of JSON-based and HTML based software making.
    </div>

    <button onclick="location.reload()">reload this</button>
    <hr>
    <div id="stdout"></div>
    <hr>


    <button onclick="next_step()">Go!</button>
    Current step <span id="UI_current_step">?</span>.

    <label> <input type="checkbox" id="UI_stop_at_every_step" checked="true">stop at every step.</label>
    <button onclick="next_step()">next step</button>

    breakpoints list. e.g. : "2,4".
    <input type="text" value="" id="UI_breakpoints_list" placeholder="breakpoints e.g. 5,6,7">

    <label> <input type="checkbox" id="UI_ignore_breakpoints_list">ignore the break-points in the list.</label>

    <hr>
    <button onclick="if(parse(program_text.textContent)) console_write('Current program was parsed fine.\n')">Parse
        program.</button>

    <style>
        .area {
            border: 1px solid black;
            padding: 5px;
        }
    </style>
    <div id="UI_program_parse_errors" contenteditable="" class="area"></div>
    <pre id="program_text" contenteditable="" class="area" oninput="program_input()">
[
"program",
["ignored note", ["json", "test 1\n"]],
["set contextual variable", ["json", "text output"], ["json", ""]],
["append text", ["json", "text output"], ["json", "count ( "]],
["append text", ["json", "text output"], ["sequence size", ["json", [1, 2, 3, 4]]]],
["append text", ["json", "text output"], ["json", " )\n"]],
["write message", ["contextual variable", ["json", "text output"]]],
["if then",
    ["json", true],
    ["procedure", ["json", "message"], ["json", "it works!"]]
],
["procedure", ["json", "message"], ["procedure", ["json", "question"],
    ["json", "enter integer"],
    ["json", "10"]
]]
]
    </pre>
    <hr>

    <script>

        var program_json_example0 = [
            "code_block",
            ["console_log", ["json", "step 1.\n"]],
            ["console_log", ["json", "step 2.\n"]],
            ["console_log", ["json", "step 3.\n"]],
            ["console_log", ["json", "step 4.\n"]],
            ["console_log", ["json", "step 5.\n"]],
            ["console_log", ["json", "end.\n"]]
        ]

        var program_json_example1 = [
            "code_block",
            ["comment", ["json", "test 1\n"]],
            ["local_variable_set", ["json", "string_output"], ["json", ""]],
            ["append", ["json", "string_output"], ["json", "count ( "]],
            ["append", ["json", "string_output"], ["array_count", ["json", [1, 2, 3, 4]]]],
            ["append", ["json", "string_output"], ["json", " )\n"]],
            ["console_log", ["local_variable_get", ["json", "string_output"]]],
            ["if_then",
                ["json", true],
                ["js_function", ["json", "alert"], ["json", "it works!"]]
            ],
            ["js_function", ["json", "alert"], ["js_function", ["json", "prompt"],
                ["json", "enter integer"],
                ["json", "10"]
            ]]
        ]

        //program_text.textContent = JSON.stringify(program_json_example0, null, 1)

        function js_function_call_list(call_list) {
            return globalThis[call_list[0]].call(globalThis, ...call_list.slice(1))
        }

        function console_write(...parameters) {
            console.log(...parameters)
            var text = parameters.join("|")
            stdout.innerHTML += text.replace("\n", "<br>")
        }

        function evaluate(item, namespace) {
            var type = item[0]
            if (type in types) {
                var type_function = types[type]
                return type_function(item, namespace)
            }
            else {
                console_write("error! type_function not found: " + type + "\n")
            }
        }

        var types = {}

        types.comment = function () { }

        types.json = function (item) { return item[1] }

        types.js_function = function (item, namespace) {
            var call_list = item.slice(1).map((item, namespace) => evaluate(item, namespace))
            return js_function_call_list(call_list)
        }

        types.console_log = function (item, namespace) {
            var what = item[1]
            var text = evaluate(what, namespace)
            console_write(text)
        }

        var code_blocks = []

        function next_step() {
            setTimeout(step_of_code_block, 0)
        }

        function step_of_code_block() {

            var code_block = code_blocks.at(-1)
            if (!code_block) {
                console_write("INFO: starting program.\n")
                execute(parse(program_text.textContent))
                return
            }

            function code_block_has_ended() {
                code_blocks.pop()
                if (!code_blocks.length) console_write("INFO: program ended.\n<hr>")
            }

            if (code_block.current_step >= code_block.steps.length) {
                code_block_has_ended()
                return
            }

            var step = code_block.steps[code_block.current_step]

            evaluate(step, code_block.namespace)

            code_block.current_step++
            //alert(identifier_of_current_step())
            program_input()

            document.all.UI_current_step.innerText = code_block.current_step

            if (code_block.current_step >= code_block.steps.length) {
                code_block_has_ended()
                return
            }

            var stop_at_every_step = document.all.UI_stop_at_every_step.checked
            var current_is_breakpoint = document.all.UI_breakpoints_list.value.split(",").indexOf(code_block.current_step.toString()) != -1
            var ignore_breakpoints = document.all.UI_ignore_breakpoints_list.checked
            var stop_condition = stop_at_every_step || (!ignore_breakpoints && current_is_breakpoint)
            if (!stop_condition) next_step()
        }

        types.code_block = function (item, namespace) {
            var code_block = {}
            code_block.current_step = 0
            code_block.steps = item.slice(1)
            code_block.namespace = namespace

            code_blocks.push(code_block)
            next_step()
        }

        types.if_then = function (item, namespace) {
            if (evaluate(item[1], namespace)) {
                return evaluate(item[2], namespace)
            }
        }

        types.if_then_else = function (item, namespace) {
            if (evaluate(item[1], namespace)) {
                return evaluate(item[2], namespace)
            } else {
                return evaluate(item[3], namespace)
            }
        }

        types.case_that_is_equal_to = function (item, namespace) {
            var cases = item.slice(2)
            for (var case_current of cases) {
                var equal_to_this = evaluate(item[1], namespace)
                if (equal_to_this == evaluate(case_current[0], namespace))
                    return evaluate(case_current[1], namespace)
            }
        }

        types.local_variable_get = function (item, namespace) {
            return namespace[evaluate(item[1], namespace)]
        }

        types.local_variable_set = function (item, namespace) {
            var value = evaluate(item[2], namespace)
            namespace[evaluate(item[1], namespace)] = value
        }

        types.array_count = function (item, namespace) {
            var array = evaluate(item[1], namespace)
            return array.length
        }

        types.append = function (item, namespace) {
            namespace[evaluate(item[1], namespace)] += evaluate(item[2], namespace)
        }

        function parse(program_code) {
            try {
                return JSON.parse(program_code)
            } catch (err) {
                console_write(err.stack + "\n")
            }
        }

        function execute(program_json) {
            evaluate(program_json, {})
        }

    </script>

    <hr>

    <button onclick="next_step()">next step</button>

    <hr>

    <div id="UI_program_display"></div>

    <script>

        types["program"] = types.code_block
        types["ignored note"] = types.comment
        types["set contextual variable"] = types.local_variable_set
        types["append text"] = types.append
        types["sequence size"] = types.array_count
        types["write message"] = types.console_log
        types["contextual variable"] = types.local_variable_get
        types["if then"] = types.if_then
        types["procedure"] = types.js_function
        message = alert
        question = prompt

        /*
                    "code_block",
                    ["comment", ["json", "test 1\n"]],
                    ["local_variable_set", ["json", "string_output"], ["json", ""]],
                    ["append", ["json", "string_output"], ["json", "count ( "]],
                    ["append", ["json", "string_output"], ["array_count", ["json", [1, 2, 3, 4]]]],
                    ["append", ["json", "string_output"], ["json", " )\n"]],
                    ["console_log", ["local_variable_get", ["json", "string_output"]]],
                    ["if_then",
                        ["json", true],
                        ["js_function", ["json", "alert"], ["json", "it works!"]]
                    ],
                    ["js_function", ["json", "alert"], ["js_function", ["json", "prompt"],
                        ["json", "enter integer"],
                        ["json", "10"]
                    ]]
        */
        var program_json_example2 = [
            "program",
            ["ignored note", ["json", "test 1\n"]],
            ["set contextual variable", ["json", "text output"], ["json", ""]],
            ["append text", ["json", "text output"], ["json", "count ( "]],
            ["append text", ["json", "text output"], ["sequence size", ["json", [1, 2, 3, 4]]]],
            ["append text", ["json", "text output"], ["json", " )\n"]],
            ["write message", ["contextual variable", ["json", "text output"]]],
            ["if then",
                ["json", true],
                ["procedure", ["json", "message"], ["json", "it works!"]]
            ],
            ["procedure", ["json", "message"], ["procedure", ["json", "question"],
                ["json", "enter integer"],
                ["json", "10"]
            ]]
        ]

        document.all.UI_program_display.innerHTML = display_html(program_json_example1, "1")

        for (var program of document.querySelectorAll(".UI_program_identifier")) {
            program.onclick = function (event) {
                var dom_element = event.target//.parentElement
                //alert(dom_element.dataset.program_identifier)
                alert(dom_element.innerHTML)
                event.stopPropagation()
            }
        }

        function identifier_of_current_step() {
            var levels = [1]
            for (var code_block of code_blocks) {
                levels.push(code_block.current_step)
            }
            return levels.join(",")
        }

        function display_html(program_json, program_identifier) {
            var button_next = ""
            var button_class = ""
            if (identifier_of_current_step() == program_identifier) {
                button_next = `<button onclick="next_step()">next step</button> `
                button_class = "current_step"
            }
            var html = `<div class="program" data-program_identifier="${program_identifier}"><button class="UI_program_identifier ${button_class}">${program_identifier}</button> ${button_next}`
            if (program_json[0] == "json") {
                html += `<i>${JSON.stringify(program_json[1])}</i>.</div>\n`
                return html
            }
            var sub_programs = program_json.slice(1).map((program_json, index) => display_html(program_json, program_identifier + "," + (index + 1)))
            html += `<b>${program_json[0]}:</b> {{${sub_programs.join("<br>\n")}}}.</div>\n`
            return html
        }

        ///

        program_input()
        function program_input() {

            try {
                var program_json = JSON.parse(program_text.textContent)
                if (program_json) {
                    document.all.UI_program_parse_errors.textContent = "no parse errors now."
                    document.all.UI_program_display.innerHTML = display_html(program_json, "1")
                }
            } catch (err) {
                ///console_write(err.stack + "\n")
                document.all.UI_program_parse_errors.textContent = err.stack
            }
        }

    </script>

    <style>
        .program>.program {
            padding-left: 5em;
        }

        .program>b {
            text-transform: capitalize;
        }

        .current_step {
            background-color: red;
        }
    </style>

    <hr>
    <script src="https://arkenidar.com/web/show-source.js" data-href="?show-source"></script>

</body>

</html>