使用 CSCS 腳本語言進行跨平台開發

已發表: 2022-03-10
快速總結 ↬在這篇文章中,Vassili Kaplan 解釋瞭如何使用腳本語言來開發跨平台的移動應用程序。 您會在 iOS 和 Android 中找到包括在屏幕上放置小部件、SQLite、Web 請求和 JSON 解析的示例。
我們的目標不是搭建平台; 這是跨越所有這些。

- 馬克·扎克伯格

CSCS (Customized Scripting in C#) 是一種用 C# 實現的開源腳本語言。 在語法上它與 JavaScript 非常相似,但也與 Python 有一些相似之處。 其中一些相似之處是眾所周知的if…elif…else構造中的關鍵字,並且也具有與 Python 中相同的變量範圍定義(例如,在if塊內或循環內定義的變量也將在外部可見) .

與 JavaScript 和 Python 不同,CSCS 中的變量和函數不區分大小寫。 CSCS 的主要目標是讓開發人員編寫盡可能少的代碼。 此外,相同的代碼用於 iOS 和 Android 開發。 此外,CSCS 可用於 Windows、Mac 和 Unity 開發。

注意您可以在此處閱讀有關 Microsoft 如何在其 Maquette 產品(基於 Unity)中使用 CSCS 的更多信息。

通過將 CSCS 的 C# 源代碼嵌入到 Visual Studio Xamarin 項目中,可以將 CSCS 添加到你的項目中。 與大多數其他語言不同,您擁有 CSCS 源代碼的完全所有權,並且可以輕鬆添加或修改其功能。 我將在本文後面分享一個這樣的例子。

此外,我們將學習如何開始使用 CSCS並使用其他文章中介紹的一些更高級的功能。 在這些功能中,我們將通過帶有 JSON 字符串解析的 Web 請求訪問 Web 服務,我們還將在 iOS 和 Android 上使用 SQLite。

最簡單的入門方法是下載使用 CSCS 的項目示例並開始使用start.cscs文件。 這就是我們將在下一節中做的事情:創建一個帶有基本 GUI 和事件的 iOS/Android 應用程序。

跳躍後更多! 繼續往下看↓

“你好世界!” 在 CSCS

讓我們從一個相對簡單的 CSCS 代碼示例開始,它構建了一個帶有一些小部件的屏幕:

 AutoScale(); SetBackgroundColor("light_green"); locLabelText = GetLocation("ROOT", "CENTER", "ROOT", "TOP"); AddLabel(locLabelText, "labelText", "Welcome " + _DEVICE_INFO_ + " " + _VERSION_INFO_ + " User!", 600, 100); locTextEdit = GetLocation("ROOT", "LEFT", labelText, "BOTTOM"); AddTextEdit(locTextEdit, "textEdit", "Your name", 320, 80); locButton = GetLocation(textEdit,"RIGHT",textEdit, "CENTER"); AddButton(locButton, "buttonHi", "Hello", 160, 80); function buttonHi_click(sender, arg) { name = getText(textEdit); msg = name != "" ? "Hello, "+ name + "!" : "Hello, World!"; AlertDialog("My Great App", msg); }

下圖顯示了在單擊“Hello”按鈕且未在“文本編輯”字段中輸入任何內容後,iPhone 和 Android 設備上生成的用戶界面:

你好世界!”在 iPhone(左)和 Android(右)上
“你好世界!” 在 iPhone(左)和 Android(右)上(大預覽)

讓我們簡要回顧一下上面的代碼。 它從AutoScale()函數調用開始,它的作用是告訴解析器小部件大小與屏幕大小相關,即它們將自動調整大小(小部件在更大的屏幕上看起來更大,在更小的屏幕上看起來更小屏)。 每個小部件也可以覆蓋此設置。

請注意,無需在單擊按鈕時創建特殊處理程序。 如果你定義了一個名為widgetName_click()的函數,當用戶單擊一個名為widgetName的小部件時,它將用作處理程序(它不必是按鈕,它實際上可以是任何小部件)。 這就是為什麼一旦用戶點擊按鈕就會觸發函數buttonHi_click()的原因。

您可能已經註意到 GUI 完全是用代碼構建的。 這是通過在添加時提供一個相對的小部件位置來完成的。 定位命令的一般格式如下:

 location = GetLocation(WidgetX, HorizontalPlacement, WidgetY, VerticalPlacement, deltaX=0, deltaY=0, autoResize=true);

因此,您可以相對於屏幕上的其他小部件放置一個小部件。 小部件的一個特例是“ROOT”小部件,即主屏幕。

創建位置後,您需要將其作為參數提供給以下任何函數:

  • AddLabel
  • AddButton
  • AddCombobox
  • AddStepper
  • AddListView
  • AddTextView
  • AddStepper
  • AddImageView
  • AddSlider
  • AddPickerView ,
  • 等等。

以上所有具有相同的結構:

 AddButton(location, newWidgetname, initialValue, width, height);

如果之前運行了AutoScale() CSCS 命令,則小部件的寬度和高度將與屏幕大小相關。 此外,初始值(如果是按鈕)是其上顯示的文本。 這可以通過調用SetText(widgetName, newText)隨時更改。

使用 Visual Studio Code 調試 CSCS

我們還可以使用 Visual Studio Code 來調試 CSCS 腳本。 如果您想開發適用於 Android 和 iOS 的應用程序,則需要使用 Mac。 安裝 Visual Studio Code 後,安裝 CSCS 調試器和 REPL 擴展。

為了使用擴展,在你的start.cscs CSCS 腳本的任何地方添加這行代碼:

 StartDebugger();

下圖顯示瞭如何使用 Visual Studio Code 調試和更改“Hello, World!”的功能我們在上一節中開發的應用程序。 在接下來的示例中,我們將動態添加標籤和按鈕到現有佈局。

為此,我們只需選擇要由解析器執行的代碼,然後按Ctrl + 8 。 結果,將在屏幕中央添加一個標籤和一個按鈕。 我們還添加了一個按鈕處理程序,它將在每次單擊按鈕時使用當前時間更新新標籤。

使用 Visual Studio Code 即時更改佈局
使用 Visual Studio Code 動態更改佈局(大預覽)

在 CSCS 中使用 SQLite

SQLite 是一種 ACID(原子性、一致性、隔離性、持久性)類型的關係數據庫,由 Richard Hipp 開發(第一個版本於 2000 年發布)。 與其他關係數據庫(如 Microsoft SQL Server 或 Oracle 數據庫)不同,它是嵌入式的。 (不僅嵌入到設備中,還嵌入到最終程序中。)它作為一個非常緊湊的庫包含在程序中,大小不到 500 KB。 但是,如果兩個應用程序都知道數據庫文件路徑,則兩個應用程序(由同一開發人員發布)可以讀取相同的 SQLite DB。

SQLite 的優點是無需額外安裝即可在 iOS 或 Android 設備上使用。 缺點是它顯然不能像“普通”數據庫那樣保存盡可能多的數據,而且它是弱類型的(即你可以插入一個字符串而不是一個整數——然後它將被轉換為一個整數或失敗時為 0)。 另一方面,後者也可以被視為優勢。

無需額外的導入語句即可從 CSCS 輕鬆使用 SQLite。 下表將幫助您了解 CSCS 中使用的主要 SQLite 函數的概述:

命令描述
SQLInit(DBName) 初始化數據庫或設置要與後續 DB 語句一起使用的數據庫。
SQLDBExists(DBName) 檢查 DB 是否已初始化。 還設置要與後續 DB 語句一起使用的數據庫。
SQLQuery(query) 執行 SQL 查詢(選擇語句)。 返回包含記錄的表。
SQLNonQuery(nonQuery) 執行 SQL 非查詢,例如更新、創建或刪除語句。 返回受影響的記錄數。
SQLInsert(tableName, columnList, data) 將傳遞的記錄數據表插入到指定的 DB 表中。 columnList參數具有以下結構: colName1,colName2,…,colNameN

表 1:CSCS 中的 SQLite 命令

這就是通常使用SQLInit()SQLDBExists()函數的方式:

 DBName = "myDB.db1"; if (!SQLDBExists(DBName)) { create = "CREATE TABLE [Data] (Symbol ntext, Low real, High real, Close real, Volume real, Stamp text DEFAULT CURRENT_TIMESTAMP)"; SQLNonQuery(create); } SQLInit(DBName);

稍後我們將看到更多關於如何選擇數據並將數據插入 SQLite 數據庫的示例。 我將向您展示如何將已從 Web 服務提取的股票數據寫入本地 SQLite 數據庫的示例。

向 CSCS 添加自定義功能

在本節中,我們將了解如何擴展 CSCS 功能。 例如,我們將在下面看到 CSCS 睡眠功能的現有實現。

要添加自定義功能,您需要做的就是通過從ParserFunction類派生、覆蓋其Evaluate()方法並將該類註冊到解析器來創建一個新類。 這是一個簡短的版本(沒有錯誤檢查):

 class SleepFunction : ParserFunction { protected override Variable Evaluate(ParsingScript script) { List args = script.GetFunctionArgs(); int sleepms = Utils.GetSafeInt(args, 0); Thread.Sleep(sleepms); return Variable.EmptyInstance; } } class SleepFunction : ParserFunction { protected override Variable Evaluate(ParsingScript script) { List args = script.GetFunctionArgs(); int sleepms = Utils.GetSafeInt(args, 0); Thread.Sleep(sleepms); return Variable.EmptyInstance; } }

可以在初始化階段的任何地方通過以下命令向解析器註冊類:

 ParserFunction.RegisterFunction("Sleep", new SleepFunction());

而已! 現在,一旦解析器提取了“睡眠”標記,就會調用SleepFunction類的Evaluate()方法。

請注意,CSCS 不區分大小寫(除了核心控制流語句: ifelifelseforwhilefunctionincludenewclassreturntrythrowcatchbreakcontinue )。 這意味著您可以鍵入“sleep(100)”或“Sleep(100)”——這兩個調用都會將正在執行的線程掛起 100 毫秒。

在 CSCS 中處理 JSON

JSON(JavaScript Object Notation)是一種輕量級的數據交換格式,由屬性-值對和數組-類型對組成。 它是由 Douglas Crockford 在 2000 年代初開發的(大約在 SQLite 出現的同時)。

在本節中,我們將學習如何使用 CSCS 解析 JSON。

解析 JSON 字符串的 CSCS 函數是GetVariableFromJSON(jsonText) 。 此函數返回一個哈希表,其中鍵是 JSON 字符串中的屬性。

考慮以下 JSON 字符串示例:

 jsonString = '{ "eins" : 1, "zwei" : "zweiString", "mehr" : { "uno": "dos" }, "arrayValue" : [ "une", "deux" ] }';

調用後:

 a = GetVariableFromJSON();

變量a將是具有以下值的哈希表:

 a["eins"] = 1 a["zwei"] = "zweiString" a["mehr"]["uno"] = "dos" a["arrayValue"][0] = "une" a["arrayValue"][1] = "deux"

在下一節中,我們將看到另一個從 Web 服務解析 JSON 字符串的示例。

帶有 SQLite、Web 請求和 JSON 的應用程序示例

對於使用 SQLite、Web 服務和 JSON 解析的應用程序,我們將使用 Alpha Vantage Web 服務。 您可以免費獲得 API 密鑰,但免費版本允許訪問他們的網絡服務每分鐘不超過 5 次。

使用 Alpha Vantage,您可以提取各種金融數據集——包括股票價格。 這就是我們將在示例應用程序中執行的操作。

下圖顯示了 Stocks 應用程序在 iOS 和 Android 設備上的外觀。

從 iOS(左)和 Android(右)上的 Alpha Vantage Web 服務中提取股票
從 iOS(左)和 Android(右)上的 Alpha Vantage Web 服務中提取股票(大預覽)

構建 GUI 的 CSCS 代碼如下:

 locLabel = GetLocation("ROOT","CENTER", "ROOT","TOP", 0,30); AddLabel(locLabel, "labelRefresh", "", 480, 60); locSFWidget = GetLocation("ROOT","CENTER", labelRefresh,"BOTTOM"); AddSfDataGrid(locSFWidget, "DataGrid", "", graphWidth, graphHeight); listCols = {"Symbol","string", "Low","number", "High", "number", "Close","number", "Volume","number"}; AddWidgetData(DataGrid, listCols, "columns"); colWidth = {17, 19, 19, 19, 26}; AddWidgetData(DataGrid, colWidth, "columnWidth"); locButton = GetLocation("ROOT","CENTER",DataGrid,"BOTTOM"); AddButton(locButton, "buttonRefresh", "Refresh", 160, 80); locLabelError = GetLocation("ROOT","CENTER","ROOT","BOTTOM"); AddLabel(locLabelError, "labelError", "", 600, 160); SetFontColor(labelError, "red"); AlignText(labelError, "center"); getDataFromDB();

getDataFromDB()方法將從 SQLite 數據庫中提取所有數據。 它使用如下定義的 SQL 查詢:

 query = "SELECT Symbol, Low, High, Close, Volume, DATETIME(Stamp, 'localtime') as Stamp FROM Data ORDER BY Stamp DESC LIMIT 5;";

看看下面的getDataFromDB()實現代碼。

 function getDataFromDB() { results = SQLQuery(query); for (i = 1; i < results.Size; i++) { vals = results[i]; stock = vals[0]; low = Round(vals[1], 2); high = Round(vals[2], 2); close = Round(vals[3], 2); volume = Round(vals[4], 2); refresh = vals[5]; stockData = {stock, low, high, close, volume}; AddWidgetData(DataGrid, stockData, "item"); } SetText(labelRefresh, "DB Last Refresh: " + refresh); lockGui(false); }

現在讓我們看看我們如何從 Alpha Vantage Web 服務中獲取數據。 首先,我們初始化數據:

 baseURL = "https://www.alphavantage.co/query? " + "function=TIME_SERIES_DAILY&symbol="; apikey = "Y12T0TY5EUS6BC5F"; stocks = {"MSFT", "AAPL", "GOOG", "FB", "AMZN"}; totalStocks = stocks.Size;

接下來,只要用戶點擊“刷新”按鈕,我們就會一一加載股票:

 function buttonRefresh_click(object, arg) { lockGui(); SetText(labelRefresh, "Loading ..."); SetText(labelError, ""); ClearWidget(DataGrid); loadedStocks = 0; getData(stocks[loadedStocks]); } function getData(symbol) { stockUrl = baseURL + symbol + "&apikey=" + apikey; WebRequest("GET", stockUrl, "", symbol, "OnSuccess", "OnFailure"); }

下面是用於從 Web 服務獲取數據的主要 CSCS 函數:

 WebRequest("GET", stockUrl, "", symbol, "OnSuccess", "OnFailure");

最後兩個參數是在完成 Web 請求時調用的函數。 例如,如果發生故障,將調用以下 CSCS 函數:

 function OnFailure(object, errorCode, text) { SetText(labelError, text); lockGui(false); }

結果,用戶將收到如下所示的錯誤消息:

請求網絡數據時出錯
請求 Web 數據時出錯(大預覽)

但是,如果一切順利,我們將解析 JSON 字符串並將其內容插入 SQLite DB。

 function OnSuccess(object, errorCode, text) { jsonFromText = GetVariableFromJSON(text); metaData = jsonFromText[0]; result = jsonFromText[1]; symbol = metaData["2. Symbol"]; lastRefreshed = metaData["3. Last Refreshed"]; allDates = result.keys; dateData = result[allDates[0]]; high = Round(dateData["2. high"], 2); low = Round(dateData["3. low"], 2); close = Round(dateData["4. close"], 2); volume = dateData["5. volume"]; stockData = {symbol, low, high, close, volume}; SQLInsert("Data","Symbol,Low,High,Close,Volume",stockData); if (++loadedStocks >= totalStocks) { getDataFromDB(); } else { getData(stocks[loadedStocks]); } }

為了了解我們如何訪問上面哈希表中的不同字段,讓我們看一下從 Alpha Vantage Web 請求中接收到的實際字符串:

 { "Meta Data": { "1. Information": "Daily Prices (open, high, low, close) and Volumes", "2. Symbol": "MSFT", "3. Last Refreshed": "2019-10-02 14:23:20", "4. Output Size": "Compact", "5. Time Zone": "US/Eastern" }, "Time Series (Daily)": { "2019-10-02": { "1. open": "136.3400", "2. high": "136.3700", "3. low": "133.5799", "4. close": "134.4100", "5. volume": "11213086" }, … } }

如您所見,我們將最新日期作為包含所有提取日期的allDates數組的第一個元素。

結論

將 CSCS 添加到您的項目很容易。 您需要做的只是將 CSCS 的源代碼作為模塊嵌入到您的項目中——就像在示例 Xamarin 項目中所做的那樣。

您是否在項目中使用和擴展 CSCS 腳本語言? 在下面發表評論-我很高興收到您的來信!

延伸閱讀

如果你想更多地探索 CSCS 語言,這裡有一些我寫過的關於這個主題的文章:

  • “C# 中的拆分和合併表達式解析器”,MSDN 雜誌(2015 年 10 月)
  • “C# 中的可自定義腳本”,MSDN 雜誌(2016 年 2 月)
  • “使用可自定義的腳本語言編寫本機移動應用程序”,MSDN 雜誌(2018 年 2 月)
  • “CSCS:C# 中的自定義腳本”,GitHub
  • “使用函數式腳本語言開發跨平台原生應用程序”,CODE 雜誌
  • “簡潔地實現自定義語言”(電子書)
  • “簡潔地用函數式語言編寫原生移動應用程序”(電子書)

作為附加資源,我還建議您閱讀如何通過預編譯 CSCS 函數來提高其性能。