簡介:在上一節《安全多方計算(MPC)從入門到精通:簡易教程》中,我們已經簡單介紹過Frutta語言,Frutta是JUGO為計算邏輯而開發的編程語言,計算邏輯在MPC中是為解決具體業務而編寫的算法。它是一門類C高級語言,支持大部分運算符、數據類型,表達方式的實現——300個門電路,僅需一行代碼!
1.什么是Frutta
Frutta是矩陣元為安全多方計算的算法電路文件生成而專門定制的編程語言。它是一門類高級語言,也是一門面向過程的編程語言。Frutta支持大部分運算符、數據類型,表達方式靈活實用。目前只有矩陣元提供的在線ide可以進行編譯運行。ide可以把Frutta文件編譯成可在JUGO平台上執行的電路文件。
2.程序結構
在我們學習Frutta語言的基本構建塊之前,讓我們先來看看一個最小的Frutta程序結構,在接下來的章節中可以以此作為參考。
Frutta語言主要包括以下部分:
預處理器指令
輸入輸出定義
函數
變量
語句&表達式
注釋
1. #parties 2 /* 兩個參與方 */
2.
3. #input 1 int32 /* 參與方1,可以是基礎類型,也可以是數組,結構體等復雜類型 */
4. #input 2 int32 /* 參與方2 */
5. #output 1 int32 /* 輸出方 */
6.
7. /* 主函數,完成加法操作 */
8. function void main()
9. {
10. output1 = input1 + input2;
11. }
一、基本語法
Frutta程序由各種Token組成,Token可以是關鍵字、標識符、常量、字符串值,或者是一個符號。
分號
在Frutta程序中,分號是語句結束符。也就是說,每個語句必須以分號結束。它表明一個邏輯實體的結束。
注釋
注釋就像是程序中的幫助文本,它們會被編譯器忽略。它們以 /* 開始,以字符 */ 終止。
您不能在注釋內嵌套注釋,注釋也不能出現在字符串或字符值中。
標識符
標識符是用來標識變量、函數,或任何其他用戶自定義項目的名稱。一個標識符以字母 A-Z 或 a-z 或下划線 _ 開始,后跟零個或多個字母、下划線和數字(0-9)。
標識符內不允許出現標點字符,比如 @、$ 和 %。標識符是區分大小寫。因此,Manpower 和 manpower 是兩個不同的標識符。下面列出幾個有效的標識符:
mohd zara abc move_name a_123 myname50 _temp j a23b9 retVal
關鍵字
下表列出了Frutta中的保留字。這些保留字不能作為常量名、變量名或其他標識符名稱。對於輸入輸出,請按照順序,依次寫。輸入個數跟參與計算方個數保持一致。比如有3個計算方個數,那么請依次定義input 1 xxx, input 2 xxx, input 3 xxx。輸出也同樣。
else typedef return for void if struct abs max min
int8 uint8 int16 uint16 int32 uint32 int64 uint64 bool
input1 input2 ... inputN output1 output2 ... outputN
空格
只包含空格的行,被稱為空白行,可能帶有注釋,編譯器會完全忽略它。
在Frutta中,空格用於描述空白符、制表符、換行符和注釋。空格分隔語句的各個部分,讓編譯器能識別語句中的某個元素(比如 int)在哪里結束,下一個元素在哪里開始。因此,在下面的語句中:type age;。在這里,type 和 age 之間必須至少有一個空格字符(通常是一個空白符),這樣編譯器才能夠區分它們。另一方面,在下面的語句中:
fruit = apples + oranges;
Copy
fruit 和 =,或者 = 和 apples 之間的空格字符不是必需的,但是為了增強可讀性,您可以根據需要適當增加一些空格。
二、預處理器
預處理器不是編譯器的組成部分,但是它是編譯過程中一個單獨的步驟。簡言之,預處理器只不過是一個文本替換工具而已,它們會指示編譯器在實際編譯之前完成所需的預處理。
所有的預處理器命令都是以井號(#)開頭。它必須是第一個非空字符,為了增強可讀性,預處理器指令應從第一列開始。
在Frutta中,支持兩類預編譯器指令:
include
支持包含外部文件,外部文件支持以下幾類外部文件:
Frutta文件
使用Frutta實現的文件,里面實現的函數可以在當前文件里面調用。注意,只支持當前文件,不支持非當前目錄,比如不支持#include "./lib/math.wir",只支持#include "math.wir"。一個完整的示例如下:
1. // math.wir
2. function int32 add(int32 x, int32 y)
3. {
4. return x + y;
5. }
6.
7. function int32 sub(int32 x, int32 y)
8. {
9. return x - y;
10. }
11.
12. function int32 mul(int32 x, int32 y)
13. {
14. return x * y;
15. }
16.
17. function int32 div(int32 x, int32 y)
18. {
19. return x / y;
20. }
21.
22. typedef struct Point
23. {
24. int32 x;
25. int32 y;
26. }
27.
28. /* 求兩個點之間的距離 */
29. function int32 dis(Point p1, Point p2)
30. {
31. int32 x = sub(p1.x, p2.x);
32. int32 y = sub(p1.y, p2.y);
33.
34. return add(mul(x, x), mul(y, y));
35. }
36.
37. // include.wir
38. #include "math.wir"
39.
40. #parties 2
41. #input 1 Point
42. #input 2 Point
43. #output 1 int32
44.
45. function void main()
46. {
47. output1 = dis(input1, input2);
48. }
define
簡單替換程序中的標識符。不支持宏參數,以及##連接符與#符。
1. #define NAME expression
三、數據類型
1.基本類型
Frutta語言中,任何運算都表示為整數運算,因此Frutta語言以下幾類基本類型:
| 類型 |
位數 |
數值范圍 |
| bool |
8 |
取值為0和1 |
| int8 |
8 |
-128 ~ 127 |
| uint8 |
8 |
0 ~ 255 |
| int16 |
16 |
-32768 ~ 32767 |
| uint16 |
16 |
0 ~ 65535 |
| int32 |
32 |
-2147483648 ~ 2147483647 |
| uint32 |
32 |
0 ~ 4294967295 |
| int64 |
64 |
-9223372036854775808 ~ 9223372036854775807 |
| uint64 |
64 |
0 ~ 18446744073709551615 |
也可以定義不同字節數的自定義類型。
1. typedef signed bytelength type;
2. typedef unsigned bytelength utype;
2.數據類型的隱式轉換
Frutta在以下四種情況下會進行隱式轉換:
a. 算術運算式中,低類型能夠轉換為高類型。
b. 賦值表達式中,右邊表達式的值自動隱式轉換為左邊變量的類型,並賦值給他。
c. 函數調用中參數傳遞時,系統隱式地將實參轉換為形參的類型后,賦給形參。
d. 函數有返回值時,系統將隱式地將返回表達式類型轉換為返回值類型,賦值給調用函數。
當不同類型的數據進行操作時,應當首先將其轉換成相同的數據類型,然后進行操作,轉換規則是由低級向高級轉換。
轉換規則如下示:
int8->uint8->int16->uint16->int32->uint32->int64->uint64。
舉一個int8 -> uint8的例子:
1. int8 a = -41;
2. int8 b = -90;
3. uint8 c = a + b;
4. // 最終結果為 125
3.結構體(struct)
Frutta支持類C語言的Struct,來定義結構體。結構體允許定義可存儲不同類型數據項的變量。
1) 定義結構
1. typedef struct Point
2. {
3. int32 x;
4. int32 y;
5. }
6.
7. typedef struct Circle
8. {
9. Point p;
10. uint32 radius;
11. }
以上定義可以使用 Point,Circle 定義結構體變量。
2) 訪問結構成員
為了訪問結構的成員,我們使用成員訪問運算符(.)。成員訪問運算符是結構變量名稱和我們要訪問的結構成員之間的一個英文句號。如定義一個變量Circle c訪問它的成員變量x需要這樣c.p.x。
4.數組
Frutta語言支持數組數據結構,它可以存儲一個固定大小的相同類型元素的順序集合。數組是用來存儲一系列數據,但它往往被認為是一系列相同類型的變量。
1) 聲明數組
在Frutta中要聲明一個數組,需要指定元素的類型和元素的數量,如下所示:type arrayName [ arraySize ]
Frutta支持多維數組,如下所示:type arrayName [row][col];
2) 初始化數組
在Frutta中,您可以逐個初始化數組,也可以使用一個初始化語句,如下所示:type balance[5] = {0, 0, 0, 0, 0};
大括號{ }之間的值的數目不能大於我們在數組聲明時在方括號[ ]中指定的元素數目。
3) 訪問數組元素
數組元素可以通過數組名稱加索引進行訪問。元素的索引是放在方括號內,跟在數組名稱的后邊。例如: type salary = balance[9];
上面的語句將把數組中第 10 個元素的值賦給 salary 變量。
四、常量
Frutta中,有整型常量和bool常量。
bool常量:在Frutta中,內置兩個bool常量true和false,二進制分別表示00000001和00000000。如:
bool flag = true。
整型常量:整型常量可以使用10進制和16進制表示。16進制需要以0x開頭。如:
uint32 YEAR = 365。
五、變量
Frutta中,變量其實只不過是程序可操作的多個Wire的組合。Frutta中每個變量都有不同數量的Wire,運算符可應用於變量上。
變量的名稱可以由字母、數字和下划線字符組成。它必須以字母或下划線開頭。大寫字母和小寫字母是不同的。
變量定義指定一個數據類型(數據類型必須是類型定義里面聲明的),並包含了該類型的一個或多個變量的列表,如:
type variable_list;
變量可以在聲明的時候被初始化(指定一個初始值)。初始化器由一個等號,后跟一個常量表達式組成,如下所示:
type variable_name = value;
六、運算符
運算符是一種告訴編譯器執行特定的數學或邏輯操作的符號。Frutta語言內置了豐富的運算符,並提供了以下類型的運算符:
1.算術運算符
| 運算符 |
描述 |
| + |
把兩個操作數相加 |
| - |
從第一個操作數中減去第二個操作數 |
| * |
把兩個操作數相乘 |
| / |
分子除以分母 |
| % |
取模運算符,整除后的余數 |
2.關系運算符
| 運算符 |
描述 |
| == |
檢查兩個操作數的值是否相等,如果相等則條件為真。 |
| != |
檢查兩個操作數的值是否相等,如果不相等則條件為真 |
| > |
檢查左操作數的值是否大於右操作數的值,如果是則條件為真。 |
| < |
檢查左操作數的值是否小於右操作數的值,如果是則條件為真。 |
| >= |
檢查左操作數的值是否大於或等於右操作數的值,如果是則條件為真。 |
| <= |
檢查左操作數的值是否小於或等於右操作數的值,如果是則條件為真。 |
3.位運算符
| 運算符 |
描述 |
| & |
如果同時存在於兩個操作數中,二進制 AND 運算符復制一位到結果中。 |
| \ |
如果存在於任一操作數中,二進制 OR 運算符復制一位到結果中。 |
| ^ |
如果存在於其中一個操作數中但不同時存在於兩個操作數中,二進制異或運算符復制一位到結果中。 |
| << |
二進制左移運算符。左操作數的值向左移動右操作數指定的位數。 |
| >> |
二進制右移運算符。左操作數的值向右移動右操作數指定的位數。 |
4.賦值運算符
| 運算符 |
描述 |
| = |
簡單的賦值運算符,把右邊操作數的值賦給左邊操作數 |
七、操作符
1.按位取線
支持按位取變量的線
支持以VAR{start:length}方式獲取某個變量從start開始的length根線,返回值可以賦值
給某個變量,也可以直接參與計算。
1. #parties 2
2. #input 1 int32
3. #input 2 int32
4. #output 1 int32
5.
6. function void main()
7. {
8. output1 = input1{0:8} + input2{0:8};//上面的數從0開始,取8個bit進行運算
9. }
八、控制語句
1.條件判斷語句
判斷結構要求程序員指定一個或多個要評估或測試的條件,以及條件為真時要執行的語句(必需的)和條件為假時要執行的語句(可選的)。
| 語句 |
描述 |
| if語句 |
一個 if 語句 由一個布爾表達式后跟一個或多個語句組成。 |
| if...else語句 |
一個 if 語句 后可跟一個可選的 else 語句,else 語句在布爾表達式為假時執行。 |
| 嵌套 if 語句 |
可以在一個 if 或 else if 語句內使用另一個 if 或 else if 語句。 |
2.循環判斷語句
循環語句允許我們多次執行一個語句或語句組,Frutta只支持for循環語句,如下所示:
1. for(type i=0; i<max; i++)
2. {
3.
4. }
注意:
不支持break,continue語句
結束條件i<max必須不依賴input取值,必須為明確取值
不支持外部類型聲明如for(; i<max; i++){ }
九、函數
1.函數定義
函數是一組一起執行一個任務的語句。每個Frutta程序都至少有一個函數,即主函數 main() ,所有簡單的程序都可以定義其他額外的函數。
Frutta的函數定義如下:
1. function return_type foo(paramtype1 name1, paramtype2 name2, paramtype3[5] name3)
2. {
3. return blah;
4. }
5.
6. // 一個例子如下
7. function int32 add(int32 x, int32 y)
8. {
9. return x + y;
10. }
11. // 調用
12. output1 = add(input1, input2);
一個函數的所有組成部分:
返回類型:一個函數可以返回一個值。return_type 是函數返回的值的數據類型。有些函數執行所需的操作而不返回值,在這種情況下,return_type 是關鍵字 void。
函數名稱:這是函數的實際名稱。函數名和參數列表一起構成了函數簽名。
參數:參數就像是占位符。當函數被調用時,您向參數傳遞一個值,這個值被稱為實際參數。參數列表包括函數參數的類型、順序、數量。參數是可選的,也就是說,函數可能不包含參數。
函數主體:函數主體包含一組定義函數執行任務的語句。
2.內置函數
在Frutta中,內置以下函數:
| 函數 |
函數定義 |
函數說明 |
| abs |
intxxx abs(intxxx) |
取絕對值,輸入輸出為有符整數。如下:int32 y = abs(x) |
| min |
(u)intxxx min((u)intxxx, (u)intxxx) |
取兩個數中的最小值,輸入可以為有符和無符整數,如果同時有有符和無符,有符自動轉換為無符,返回值為無符。如下:int16 min(x, y) |
| max |
(u)intxxx max((u)intxxx, (u)intxxx) |
取兩個數中的最大值,輸入可以為有符和無符整數,如果同時有有符和無符,有符自動轉換為無符,返回值為無符。如下:int16 max (x, y) |
更多內容可以參考視頻:http://edu.51cto.com/course/14087.html
產品實操請訪問:https://jugo.juzix.net/home
