Kicsit ki szoktam ugratni magam a „haszontalan demók” miatt, amelyeket szeretek építeni, de szinte következetesen a végén tanulok valami újat. Lehet, hogy ez nem valami hihetetlenül mély dolog földrengető felismerése, de általában, ha tanulok valamit, és ha meg tudom osztani, azt győzelemnek tekintem. Példa: BASIC programok futtatása szerver nélküli környezetben.

A BASIC-hez eléggé puha pont van a szívemben. Megtanultam Applesoft BASIC-cal kódolni egy 2e-n (vagy 2+-on, most nem vagyok benne biztos), és még mindig emlékszem arra az örömre, amikor az első programomat futtattam. (Egy hihetetlenül hülye hiba után, ami azért történt, mert nem olvastam el a dokumentumokat. Szerencsére ez soha többé nem fordult elő.) Nemrég találkoztam egy remek kis online BASIC tolmácskal a http://calormen.com/jsbasic/ oldalon, és mikor Észrevettem, hogy nyílt forráskódú, és úgy gondoltam, jó lenne elindítani és futtatni OpenWhiskben.

Most – hadd fogalmazzak világosan. Ez (legalább) három okból rossz ötlet.

  1. A kód már 100%-ban ügyféloldali. Ha a használati esetem egy kliensoldali alkalmazás, akkor az OpenWhiskre való felhelyezés nem ér semmit. Valójában lelassítja a dolgokat, mivel a kódomnak HTTP-hívást kell indítania a szervernek a kód futtatásához.
  2. A BASIC egy interaktív nyelv. Bemeneti bevitelt kérhet, aminek nem feltétlenül van értelme a „futtatás és a kimenet visszaadása” környezetben.
  3. Végül, különösen az Applesoft BASIC rendelkezik grafikus móddal. (Tulajdonképpen kettő.) Elméletileg be tudnám állítani az OpenWhisk-et, hogy képeket adjon vissza (és jó lenne, ha ez működne), ennek nem feltétlenül van értelme a demómban.

Persze miért hagyjam, hogy ez megállítson? A műveleti kódon dolgoztam. Azonnal egy problémába ütköztem, mivel a könyvtár dokumentációja kissé hiányzik. De amikor "hibajelentést" adtam le róla, nagyon gyorsan választ kaptam. A legnagyobb probléma az, hogy meg kell mondanom a könyvtárnak, mit tegyen a bemeneten és a kimeneten. Bemenetnél nem csinálok semmit (csak nem fogom támogatni a programbevitelt), a kimenetnél pedig csak egy karakterláncban tárolom. Íme az általam felépített akció:

const basic = require('./basic').basic;


function main(args) {
    let result = '';
    let program = basic.compile(args.input);

    program.init({
        tty: {
            getCursorPosition: function() { return { x: 0, y: 0 }; },
            setCursorPosition: function() { },
            getScreenSize: function() { return { width: 80, height: 24 }; },
            writeChar: function(ch) { 
                //console.log('writeChar called with: '+ch);
                result += ch;
            },
            writeString: function(string) { 
                //console.log('writeString called with: '+string);
                result += string+'\n';
            },
            readChar: function(callback) {
                //callback(host.console.getc());
                callback('');
            },
            readLine: function(callback, prompt) {
                //host.console.puts(prompt);
                //callback(host.console.gets().replace(/[\r\n]*/, ''));
                callback('');
            }
        }
    });

    driver = function() {
        var state;
        do {
            try {
                state = program.step(driver);
            } catch(e) {
                console.log('ERROR!');
                return {
                    error:e
                }
            }
            // may throw basic.RuntimeError
        } while (state === basic.STATE_RUNNING);
    }
    driver(); // step until done or blocked

    return {result:result};

}

exports.main = main;

Alapvetően egy string bemenettel inicializálom a kódot (a BASIC kód), majd a driver függvényen keresztül "futtatom" a programot, amíg az be nem fejeződik. Ez teljesen meghiúsul, ha bemenetet váró kódot írsz, vagy ha grafikus módokat használsz, de lehetővé teszi az alapvető dolgok megfelelő működését. (Hú, sokat gépelem, hogy „alap”.) Most nézzük az előlapot. Az egészet egy gyors fájlba írtam, így elnézést a HTML, CSS és JS keverékéért.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width">
        <style>
        #code {
            width: 500px;
            height: 300px;
        } 
        </style>
    </head>
    <body>

        <h1>Serverless Basic</h1>

        <textarea id="code">
10 print "hello"
        </textarea>
        <p/>
        <button id="runButton">Run</button>
        <div id="result"><h2>Output</h2><pre></pre></div>

        <script>
        let API = 'https://openwhisk.ng.bluemix.net/api/v1/web/rcamden%40us.ibm.com_My%20Space/basic/basic.json';
        let $code, $runButton, $result;

        document.addEventListener('DOMContentLoaded', init, false);

        function init(e) {
            $code = document.querySelector('#code');
            $runButton = document.querySelector('#runButton');
            $result = document.querySelector('#result pre');

            $runButton.addEventListener('click', runCode, false);
        }

        function runCode(e) {
            let code = $code.value;
            if(code === '') return;
            console.log('code',code);
            fetch(API, {
                method:'POST', 
                body:'input='+encodeURIComponent(code),
                headers:{
                    'Content-Type':'application/x-www-form-urlencoded; charset=utf-8'
                }
            }).then((res) => res.json()).then((res) => {
                if(res.error) {
                    $result.innerHTML = "An error was thrown. Write better code.";
                } else {
                    $result.innerHTML = res.result;
                }
            }).catch((err) => {
              console.error('error', err); 
            });
        }
        </script>
    </body>
</html>

Ez egy viszonylag egyszerű weboldal. Szöveges területet használok a bevitelhez (már benne van néhány mintakód), egy gombot a futtatáshoz és egy div gombot a kimenet megjelenítéséhez. Itt futottam bele két olyan dologba, amelyek megbotránkoztattak.

Ahhoz, hogy adatokat küldjek a műveletemhez, POST-ot akartam használni a GET helyett. A Fetch() API-val ez nem túl nehéz, de az általam látott demók mindegyike FormData objektumot használt. Ezzel az adatokat többrészes űrlapként küldi el. Amennyire meg tudom állapítani, ezt nem támogatja az OpenWhisk. Az egyértelműség kedvéért az OpenWhisk jól futott ezen a kérésen, de nem vette át az űrlapmezőket, és nem alakította azokat automatikusan argumentumokká. Ezt magam is meg tudtam volna oldani, de meg akartam tartani a kódot, ahogy van.

Urlenkódolt lekérési hívás küldéséhez először megpróbáltam hozzáadni a fent látható fejlécet. De úgy tűnik, ha FormData() objektumot küld, az felülírja az urlenkódolt értéket, és többrészes bejegyzésként tartja meg. Tehát manuálisan kellett urlenkódolnom az űrlapbejegyzésemet. Mivel ez csak egy érték volt, bár nem volt túl nehéz. Még új vagyok a Fetch-nél, ezért ha valami nyilvánvalót kihagytam, szólj.

Ha saját maga szeretné futtatni, itt megteheti: https://cfjedimaster.github.io/Serverless-Examples/basic/test.html

És igen, írhatsz egy végtelen hurkot. Emlékszem, hogy ezt csináltam a régi időkben a Sears gépein. (Természetesen soha semmi rosszat.) Az OpenWhisk 60 másodperc után automatikusan leállítja a folyamatot, így nem aggódom túlzottan amiatt, hogy ezt teszi, de kérem, ne tegye. ;)

Ja, és az ügyfél és a művelet kódja itt található: https://github.com/cfjedimaster/Serverless-Examples/tree/master/basic

Élvezd!

Eredetileg a www.raymondcamden.com oldalon tették közzé 2017. augusztus 1-jén.