Hong-Phuc Bui
2024-10-16 f8613c9ce2bd4b74b11727d2eae204f49151bcba
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/**
 * @author Hong-Phuc Bui
 * initial version 27.12.2018
 *
 * At a glance, this file provides a mini platform to test javascript code as a style of traditional
 * console based programming language. It creates an input field for argument vectors, a div
 * for standard output, and an Object DomOut for printing something to the standard output div.
 *
 * This file can be linked into an HTML File with the tag <script>. To use
 * this file, one must provide a javascript function with signature
 *
 * <pre>
 * window.main = function(... argv)
 * {
 *   // .... code here
 * }
 * </pre>
 *
 * This function is called when the button with id "run" is clicked.A value in input field with id "argv" is split in
 * an array of string and passed to main. Some example how an input are splitted:
 *
 *  *Input*                 → *argv*
 *  hello world             → ["hello", "world"]
 *  Vincent "van Gogh"      → ["vincent", "van Gogh"]
 *  12 34 56                → ["12", "34", "56"]
 *
 * */
 
import {DomOutput} from "./DomIO.js";
// "private" variable, only for this file
const OUTPUT_ID = 'text-output';
const INPUT_ID = 'argv';
// Create Output Terminal
export let terminal = new DomOutput(OUTPUT_ID);
 
 
/**
 *  Calls the function main, if the function main throws any error, print it in the
 *  Terminal and log it into the Browser console.
 *
 *  The input of a user in input-field 'argv' is split to an array, every whitespaces are used as separator.
 *  If a Part of the input is framed in a double or a single quote pair, it is parsed as a single argument.
 *  For Example:
 *
 *  *Input*                 → *argv*
 *  hello world             → ["hello", "world"]
 *  Vincent "van Gogh"      → ["vincent", "van Gogh"]
 *  12 34 56                → ["12", "34", "56"]
 *
 */
function runMain() {
    try {
        if ( typeof main === "function") {
            let argvElement = document.getElementById(INPUT_ID);
            if (argvElement) {
                if (argvElement.files) {
                    if (argvElement.files.length > 0) {
                        main(...argvElement.files);
                    }
                } else {
                    let argumentsList = argvElement.value.trim();
                    if (argumentsList.length > 0) {
                        const parser = {
                            "string": /([^\s"]+)|"([^"]*)"/ ,
                            "word": /[+\-.\w]+/,
                            "space": /\s+/
                        };
                        let tokens = tokenize(argumentsList, parser, "unknown");
                        let argv = [];
                        for(const t of tokens) {
                            console.log(t);
                            if(t.type === "word") {
                                argv.push(t.token);
                            } else if (t.type === "string") {
                                let token = t.token;
                                if (token.startsWith('"')) {
                                    argv.push(token.slice(1, token.length-1));
                                } else {
                                    argv.push(token);
                                }
                            }
                        }
                        main(...argv);
                    }else {
                        main();
                    }
                }
            }else {
                main();
            }
        } else {
            throw new Error("Function window.main = function(...argv) is not defined!");
        }
    }catch (ex) {
        console.error(ex);
        terminal.printl(ex);
    }
}
 
 
function init() {
    try {
        //Empty every output in console
        //document.getElementById(OUTPUT_ID).innerHTML = "";
        //Student can the test the code in main() by giving something in <input id="run"/>
        document.getElementById("run").addEventListener("click", runMain);
    } catch (e) {
        console.error(e);
    }
}
 
/**
 * Origin work here: https://gist.github.com/borgar/451393/7698c95178898c9466214867b46acb2ab2f56d68
 * This work is credited to https://gist.github.com/borgar
 *
 * Tiny tokenizer
 *
 * - Accepts a subject string and an object of regular expressions for parsing
 * - Returns an array of token objects
 *
 * tokenize('this is text.', { word:/\w+/, whitespace:/\s+/, punctuation:/[^\w\s]/ }, 'invalid');
 * result => [{ token="this", type="word" },{ token=" ", type="whitespace" }, Object { token="is", type="word" }, ... ]
 *
 */
function tokenize ( s, parsers, deftok ) {
    let m, r, t, tokens = [];
    while ( s ) {
        t = null;
        m = s.length;
        for ( const [key,parser] of Object.entries(parsers) ) {
            r = parser.exec( s );
            // try to choose the best match if there are several
            // where "best" is the closest to the current starting point
            if ( r && ( r.index < m ) ) {
                t = {
                    token: r[ 0 ],
                    type: key,
                    matches: r.slice( 1 )
                }
                m = r.index;
            }
        }
        if ( m ) {
            // there is text between last token and currently
            // matched token - push that out as default or "unknown"
            tokens.push({
                token : s.substr( 0, m ),
                type  : deftok || 'unknown'
            });
        }
        if ( t ) {
            // push current token onto sequence
            tokens.push( t );
        }
        s = s.substr( m + (t ? t.token.length : 0) );
    }
    return tokens;
}
 
/* call init when DOM is loaded */
document.addEventListener("DOMContentLoaded", init);