MyLang เป็นภาษาโปรแกรมเพื่อการศึกษาแบบง่ายๆ ที่ได้รับแรงบันดาลใจจาก Python
, JavaScript
และ C
เขียนขึ้นเพื่อเป็นความท้าทายส่วนตัวในระยะเวลาอันสั้น โดยส่วนใหญ่จะมีความสนุกสนานในการเขียน ตัวแยกวิเคราะห์แบบเรียกซ้ำ และสำรวจโลกของล่าม อย่าคาดหวังว่าจะได้ภาษาสคริปต์เต็มรูปแบบพร้อมไลบรารีและเฟรมเวิร์กที่พร้อมใช้งานจริง อย่างไรก็ตาม MyLang
มีชุดบิวด์อินขั้นต่ำและ สามารถ ใช้เพื่อวัตถุประสงค์ในทางปฏิบัติได้เช่นกัน
MyLang เขียนด้วยภาษา C++17 แบบพกพา : ในขณะนี้ โปรเจ็กต์ไม่มีการขึ้นต่อกันอื่นใดนอกจากไลบรารี C++ มาตรฐาน หากต้องการสร้างมัน หากคุณติดตั้ง GNU make
ไว้ ให้รัน:
$ make -j
มิฉะนั้น เพียงส่งไฟล์ .cpp ทั้งหมดไปยังคอมไพเลอร์ของคุณ และเพิ่มไดเร็กทอรี src/
ลงในพาธการค้นหารวม สิ่งที่ดีที่สุดประการหนึ่งเกี่ยวกับการ ไม่ ต้องพึ่งพาก็คือ ไม่จำเป็นต้องมี ระบบบิลด์ สำหรับบิลด์ครั้งเดียว
เพียงผ่านตัวเลือก BUILD_DIR
เพื่อ make
:
$ make -j BUILD_DIR=other_build_directory
หากคุณต้องการรันการทดสอบของ MyLang เช่นกัน คุณต้องคอมไพล์ด้วย TESTS=1 และปิดการใช้งานการปรับให้เหมาะสมด้วย OPT=0 เพื่อประสบการณ์การแก้ไขข้อบกพร่องที่ดีขึ้น:
$ make -j TESTS=1 OPT=0
จากนั้นรันการทดสอบทั้งหมดด้วย:
$ ./build/mylang -rt
เป็นที่น่าสังเกตว่า แม้ว่าเฟรมเวิร์กการทดสอบอย่าง GoogleTest และ Boost.Test จะมีประสิทธิภาพและยืดหยุ่นมากกว่าเครื่องมือทดสอบเล็กๆ น้อยๆ ที่เรามีใน src/tests.cpp
มากอย่างไม่มีที่สิ้นสุด แต่ก็เป็นการพึ่งพา ภายนอก ยิ่งพึ่งพาน้อยก็ยิ่งดีใช่ไหม? -
วิธีที่สั้นที่สุดในการอธิบาย MyLang
คือ: ภาษา Python-ish ไดนามิกที่ดูเป็น C อาจเป็นวิธีที่เร็วที่สุดในการเรียนรู้ภาษานี้คือการตรวจสอบสคริปต์ในไดเร็กทอรี samples/
ขณะที่ดูเอกสารประกอบสั้นๆ ด้านล่าง
MyLang
เป็นภาษาพิมพ์เป็ดแบบไดนามิก เช่น Python
หากคุณรู้จัก Python
และคุณยินดีที่จะใช้เครื่องหมายปีกกา { }
คุณจะสามารถใช้งานได้โดยอัตโนมัติ ไม่มีความประหลาดใจ สตริงไม่เปลี่ยนรูปเหมือนใน Python
สามารถกำหนดอาร์เรย์ได้โดยใช้ [ ]
เช่นเดียวกับใน Python
และพจนานุกรมสามารถกำหนดได้โดยใช้ { }
เช่นกัน ภาษายังรองรับอาร์เรย์สไลซ์โดยใช้ไวยากรณ์ [start:end]
แบบเดียวกับที่ใช้โดย Python
กล่าวอีกนัยหนึ่ง MyLang
แตกต่างจาก Python
และภาษาสคริปต์อื่นๆ ในหลายด้าน:
มีการสนับสนุนค่าคงที่ การแยกวิเคราะห์เวลา ประกาศโดยใช้ const
ตัวแปรทั้งหมด จะต้อง ประกาศโดยใช้ var
ตัวแปรมีขอบเขตเหมือนใน C
รองรับการแชโดว์เมื่อมีการประกาศตัวแปรอีกครั้งอย่างชัดเจนโดยใช้ var
ในบล็อกที่ซ้อนกัน
คำสั่งนิพจน์ทั้งหมดจะต้องลงท้ายด้วย ;
เช่นเดียวกับใน C
, C++
และ Java
คำหลัก true
และ false
แต่ไม่มีประเภท boolean
เช่นเดียวกับใน C 0
เป็นเท็จ ส่วนอย่างอื่นเป็น true
อย่างไรก็ตาม สตริง อาร์เรย์ และพจนานุกรมมีค่าบูลีน เหมือนกับใน Python
ทุกประการ (เช่น อาร์เรย์ว่างจะถือว่าเป็น false
) บิวด์อิน true
เป็นเพียงนามแฝงสำหรับจำนวนเต็ม 1
ตัวดำเนินการมอบหมาย =
สามารถใช้เหมือนกับใน C
ในนิพจน์ภายใน แต่ไม่มีตัวดำเนินการลูกน้ำ เนื่องจากคุณสมบัติการขยายอาร์เรย์
MyLang รองรับทั้ง for
loop แบบคลาสสิกและ foreach
loop ที่ชัดเจน
MyLang ไม่รองรับประเภทที่กำหนดเองในขณะนี้ อย่างไรก็ตาม พจนานุกรมสนับสนุนน้ำตาลวากยสัมพันธ์ที่ดี: นอกเหนือจากไวยากรณ์หลัก d["key"]
แล้ว สำหรับคีย์สตริงแล้ว ไวยากรณ์ d.key
ก็ได้รับการสนับสนุนเช่นกัน
ตัวแปรจะถูกประกาศด้วย var
เสมอและอยู่ในขอบเขตที่ได้รับการประกาศ (ในขณะที่มองเห็นได้ในขอบเขตที่ซ้อนกัน) ตัวอย่างเช่น:
# Variable declared in the global scope
var a = 42 ;
{
var b = 12 ;
# Here we can see both `a` and `b`
print ( a , b ) ;
}
# But here we cannot see `b` .
เป็นไปได้ที่จะประกาศตัวแปรหลายตัวโดยใช้ไวยากรณ์ที่คุ้นเคยต่อไปนี้:
var a , b , c ;
แต่มีข้อแม้ ซึ่งอาจเป็นฟีเจอร์เดียวที่ "น่าประหลาดใจ" ของ MyLang
: การเริ่มต้นตัวแปรไม่ทำงานเหมือนใน C ลองพิจารณาคำสั่งต่อไปนี้:
var a , b , c = 42 ;
ในกรณีนี้ แทนที่จะประกาศ a
และ b
และกำหนดค่าเริ่มต้นเป็น c
ถึง 42 เรากำลังกำหนดค่าเริ่มต้นให้กับตัวแปร ทั้ง สามให้เป็นค่า 42 หากต้องการกำหนดค่าเริ่มต้นให้กับตัวแปรแต่ละตัวด้วยค่าที่แตกต่างกัน ให้ใช้ไวยากรณ์การขยายอาร์เรย์:
var a , b , c = [ 1 , 2 , 3 ] ;
ค่าคงที่จะถูกประกาศในลักษณะเดียวกันกับตัวแปร แต่ ไม่สามารถถูกบดบังในขอบเขตที่ซ้อนกันได้ ตัวอย่างเช่น:
const c = 42 ;
{
# That's not allowed
const c = 1 ;
# That's not allowed as well
var c = 99 ;
}
ใน MyLang
ค่าคงที่จะถูกประเมินที่ parse-time ในลักษณะเดียวกันกับการประกาศ constexpr ของ C++
(แต่ที่นั่นเราพูดถึง การคอมไพล์ time ) ในขณะที่เริ่มต้น const
สามารถใช้ตัวอักษรชนิดใดก็ได้นอกเหนือจากชุด const บิวด์อินทั้งชุด ตัวอย่างเช่น:
const val = sum ( [ 1 , 2 , 3 ] ) ;
const x = " hello " + " world " + " " + join ( [ " a " , " b " , " c " ] , " , " ) ;
เพื่อให้เข้าใจว่าค่าคงที่ได้รับการประเมินอย่างไร ให้รันล่ามด้วยตัวเลือก -s
เพื่อดัมพ์ แผนผังไวยากรณ์นามธรรม ก่อนที่จะรันสคริปต์ สำหรับตัวอย่างข้างต้น:
$ cat > t
const val = sum([1,2,3]);
const x = "hello" + " world" + " " + join(["a","b","c"], ",");
$ ./build/mylang t
$ ./build/mylang -s t
Syntax tree
--------------------------
Block(
)
--------------------------
น่าประหลาดใจ? ค่าคงที่อื่นที่ไม่ใช่อาร์เรย์และพจนานุกรมจะไม่ถูกสร้างอินสแตนซ์เป็นตัวแปรด้วยซ้ำ พวกมันไม่อยู่ที่ รันไทม์ มาเพิ่มคำสั่งโดยใช้ x
:
$ cat >> t
print(x);
$ cat t
const val = sum([1,2,3]);
const x = "hello" + " world" + " " + join(["a","b","c"], ",");
print(x);
$ ./build/mylang -s t
Syntax tree
--------------------------
Block(
CallExpr(
Id("print")
ExprList(
"hello world a,b,c"
)
)
)
--------------------------
hello world a,b,c
ตอนนี้ทุกอย่างควรจะสมเหตุสมผล เกือบจะสิ่งเดียวกันนี้เกิดขึ้นกับอาร์เรย์และพจนานุกรม ยกเว้นว่าอันหลังจะถูกแสดงทันทีที่รันไทม์เช่นกัน เพื่อหลีกเลี่ยงไม่ให้มีตัวอักษรขนาดใหญ่ทุกที่ ลองพิจารณาตัวอย่างต่อไปนี้:
$ ./build/mylang -s -e 'const ar=range(4); const s=ar[2:]; print(ar, s, s[0]);'
Syntax tree
--------------------------
Block(
ConstDecl(
Id("ar")
Op '='
LiteralArray(
Int(0)
Int(1)
Int(2)
Int(3)
)
)
ConstDecl(
Id("s")
Op '='
LiteralArray(
Int(2)
Int(3)
)
)
CallExpr(
Id("print")
ExprList(
Id("ar")
Id("s")
Int(2)
)
)
)
--------------------------
[0, 1, 2, 3] [2, 3] 2
อย่างที่คุณเห็น การดำเนิน การแบ่งส่วน ได้รับการประเมินใน เวลาแยกวิเคราะห์ ในขณะที่กำหนดค่าเริ่มต้นค่าคง s
แต่ทั้งสองอาร์เรย์ก็มีอยู่ที่รันไทม์เช่นกัน การดำเนินการตัวห้อยในนิพจน์ const จะถูกแปลงเป็นตัวอักษรแทน ดูเหมือนว่าจะเป็นการแลกเปลี่ยนประสิทธิภาพที่ดี: ค่าเล็กๆ เช่น จำนวนเต็ม จำนวนลอย และสตริงจะถูกแปลงเป็นตัวอักษรในระหว่าง การประเมิน const ในขณะที่อาร์เรย์และพจนานุกรม (อาจมีขนาดใหญ่) จะถูกปล่อยให้เป็นสัญลักษณ์แบบอ่านอย่างเดียวในขณะรันไทม์ แต่ยังคงอนุญาต การดำเนินการบางอย่างกับพวกเขา (เช่น [index]
และ len(arr)
) ที่จะได้รับการประเมินแบบ const
ในขณะนี้ MyLang
รองรับเฉพาะประเภท (บิวท์อิน) ต่อไปนี้:
none ประเภท none
ซึ่งเทียบเท่ากับ Python's None
ตัวแปรที่เพิ่งประกาศโดยไม่มีค่ากำหนด ไม่มีค่า none
(เช่น var x;
) เช่นเดียวกับฟังก์ชันที่ไม่มีค่าส่งคืน นอกจากนี้ยังใช้เป็นค่าพิเศษโดยบิวด์อินเช่น find()
ในกรณีที่เกิดความล้มเหลว
จำนวนเต็ม จำนวนเต็มขนาดพอยน์เตอร์ ที่มีลายเซ็น (เช่น 3
)
Float จำนวนจุดลอยตัว (เช่น 1.23
) ภายในเป็นคู่ยาว
สตริง สตริงเช่น "สวัสดี" สตริงไม่เปลี่ยนรูปและรองรับสไลซ์ (เช่น s[3:5]
หรือ s[3:]
หรือ s[-2:]
ซึ่งมีความหมายเหมือนกับใน Python
)
Array ประเภทที่ไม่แน่นอนสำหรับอาร์เรย์และสิ่งอันดับ (เช่น [1,2,3]
) สามารถมีรายการประเภทต่างๆ และรองรับส่วนที่เขียนได้ ชิ้นอาร์เรย์ทำงานเหมือนสำเนาในขณะที่ใช้เทคนิคการคัดลอกเมื่อเขียนภายใต้ประทุน
พจนานุกรม พจนานุกรม เป็นแผนที่แฮชที่กำหนดโดยใช้ไวยากรณ์ของ Python
: {"a": 3, "b": 4}
เข้าถึงองค์ประกอบต่างๆ ด้วยไวยากรณ์ที่คุ้นเคย d["key-string"]
หรือ d[23]
สามารถตรวจสอบได้โดยใช้ find()
และลบด้วย erase()
ในขณะนี้ เฉพาะสตริง จำนวนเต็ม และทศนิยมเท่านั้นที่สามารถใช้เป็นคีย์ของพจนานุกรมได้ สิทธิพิเศษ : สามารถเข้าถึงสตริงคีย์ที่เหมือนตัวระบุได้ด้วยไวยากรณ์ "สมาชิกของ": d.key
ฟังก์ชัน ทั้งฟังก์ชันสแตนด์อโลนและแลมบ์ดามีวัตถุประเภทเดียวกันและสามารถส่งผ่านไปมาได้เหมือนกับวัตถุอื่นๆ (ดูด้านล่าง) แต่มีเพียงแลมบ์ดาเท่านั้นที่สามารถมีรายชื่อการจับได้ ฟังก์ชันปกติไม่สามารถดำเนินการได้ในระหว่างการประเมิน const ในขณะที่ฟังก์ชัน pure
สามารถทำได้ ฟังก์ชัน Pure สามารถดูได้เฉพาะ consts และอาร์กิวเมนต์เท่านั้น
ข้อยกเว้น วัตถุประเภทเดียวที่สามารถขว้างได้ หากต้องการสร้าง ให้ใช้ exception()
บิวด์อินหรือทางลัด ex()
คำสั่งแบบมีเงื่อนไขทำงานเหมือนกับใน C
ทุกประการ ไวยากรณ์คือ:
if ( conditionExpr ) {
# Then block
} else {
# Else block
}
และเครื่องหมายปีกกา { }
สามารถละเว้นได้เช่นเดียวกับใน C
ในกรณีของบล็อกคำสั่งเดียว conditionExpr
สามารถเป็นนิพจน์ใดก็ได้ ตัวอย่างเช่น: (a=3)+b >= c && !d
เมื่อ conditionExpr
เป็นนิพจน์ที่สามารถประเมินแบบ const ได้ คำสั่ง if ทั้งหมดจะถูกแทนที่ด้วย true-branch ในขณะที่ false-branch จะถูกยกเลิกไป ตัวอย่างเช่น พิจารณาสคริปต์ต่อไปนี้:
const a = 3 ;
const b = 4 ;
if ( a < b ) {
print ( " yes " ) ;
} else {
print ( " no " ) ;
}
ไม่เพียงแต่จะพิมพ์ว่า "ใช่" เสมอ แต่ยังไม่จำเป็นต้องตรวจสอบอะไรก่อนดำเนินการด้วยซ้ำ ตรวจสอบแผนผังไวยากรณ์นามธรรม:
$ ./build/mylang -s t
Syntax tree
--------------------------
Block(
Block(
CallExpr(
Id("print")
ExprList(
"yes"
)
)
)
)
--------------------------
yes
MyLang
รองรับ while
และ for
loops แบบคลาสสิก
while ( condition ) {
# body
if ( something )
break ;
if ( something_else )
continue ;
}
for ( var i = 0 ; i < 10 ; i += 1 ) {
# body
if ( something )
break ;
if ( something_else )
continue ;
}
ในที่นี้ คุณสามารถละเว้นเครื่องหมายปีกกา { }
ได้เหมือนในกรณีข้างต้น มีความแตกต่างเพียงเล็กน้อยจาก C
ที่ควรค่าแก่การชี้ให้เห็น:
ตัวดำเนินการ ++
และ --
ไม่มีอยู่ใน MyLang
ในขณะนี้
หากต้องการประกาศตัวแปรหลายตัว ให้ใช้ไวยากรณ์: var a, b = [3,4];
หรือเพียงแค่ var a,b,c,d = 0;
หากคุณต้องการให้ตัวแปรทั้งหมดมีค่าเริ่มต้นเหมือนกัน
ในการเพิ่มค่าของตัวแปรหลายตัว ให้ใช้ไวยากรณ์: a, b += [1, 2]
ในกรณีที่หายากและซับซ้อนอย่างยิ่ง เมื่ออยู่ในคำสั่ง การเพิ่ม ของ for-loop เราจำเป็นต้องกำหนดตัวแปรใหม่ให้กับตัวแปรแต่ละตัวโดยใช้นิพจน์ที่แตกต่างกัน ใช้ประโยชน์จากไวยากรณ์การขยายในการมอบหมาย: i, j = [i+2, my_next(i, j*3)]
.
MyLang
รองรับ foreach
loop โดยใช้ไวยากรณ์ที่คุ้นเคย:
var arr = [ 1 , 2 , 3 ] ;
foreach ( var e in arr ) {
print ( " elem: " , e ) ;
}
Foreach loop สามารถใช้กับอาร์เรย์ สตริง และพจนานุกรมได้ ตัวอย่างเช่น การวนซ้ำคู่ <key, value>
แต่ละคู่ในพจนานุกรมนั้นง่ายดายดังนี้:
var d = { " a " : 3 , " b " : 10 , " c " : 42 } ;
foreach ( var k , v in d ) {
print ( k + " => " + str ( v ) ) ;
}
หากต้องการวนซ้ำเฉพาะแต่ละคีย์ ให้ใช้ var k in d
แทน
MyLang
รองรับการแจงนับใน foreach loop เช่นกัน ตรวจสอบตัวอย่างต่อไปนี้:
var arr = [ " a " , " b " , " c " ] ;
foreach ( var i , elem in indexed arr ) {
print ( " elem[ " + str ( i ) + " ] = " + elem ) ;
}
กล่าวอีกนัยหนึ่ง เมื่อชื่อของคอนเทนเนอร์นำหน้าด้วยคีย์เวิร์ด indexed
ตัวแปรแรกจะได้รับหมายเลขก้าวหน้าในการวนซ้ำแต่ละครั้ง
ในขณะที่วนซ้ำอาร์เรย์ของอาร์เรย์ขนาดคงที่ขนาดเล็ก (ลองนึกถึงสิ่งอันดับ) เป็นไปได้ที่จะขยาย "สิ่งอันดับ" เหล่านั้นโดยตรงใน foreach loop:
var arr = [
[ " hello " , 42 ] ,
[ " world " , 11 ]
] ;
foreach ( var name , value in arr ) {
print ( name , value ) ;
}
# This is a shortcut for :
foreach ( var elem in arr ) {
# regular array expansion
var name , value = elem ;
print ( name , value ) ;
}
# Which is a shortcut for :
foreach ( var elem in arr ) {
var name = elem [ 0 ] ;
var value = elem [ 1 ] ;
print ( name , value ) ;
}
การประกาศฟังก์ชันทำได้ง่ายดังนี้:
func add ( x , y ) {
return x + y ;
}
แต่รองรับทางลัดหลายรายการเช่นกัน ตัวอย่างเช่น ในกรณีของฟังก์ชันคำสั่งเดียวเหมือนกับที่กล่าวมาข้างต้น คุณสามารถใช้ไวยากรณ์ต่อไปนี้ได้:
func add ( x , y ) => x + y ;
นอกจากนี้ แม้ว่าการเขียน ()
สำหรับฟังก์ชันที่ไม่มีพารามิเตอร์เสมอๆ เป็นวิธีปฏิบัติที่ดี แต่จริงๆ แล้วฟังก์ชันเหล่านี้เป็นทางเลือกในภาษานี้:
func do_something { print ( " hello " ) ; }
ฟังก์ชันต่างๆ จะถือเป็นสัญลักษณ์ปกติใน MyLang
และไม่มีความแตกต่างอย่างมีนัยสำคัญระหว่างฟังก์ชันสแตนด์อโลนและ lambdas ในภาษานี้ ตัวอย่างเช่น เราสามารถประกาศฟังก์ชัน add
(ด้านบน) เป็น lambda ได้ดังนี้:
var add = func ( x , y ) => x + y ;
หมายเหตุ: เมื่อสร้างวัตถุฟังก์ชันในนิพจน์ เราไม่ได้รับอนุญาตให้ตั้งชื่อให้กับวัตถุเหล่านั้น
Lambdas รองรับรายการจับภาพด้วยเช่นกัน แต่ไม่รองรับการบันทึกโดยนัย เพื่อบังคับใช้ความชัดเจน แน่นอนว่าแลมบ์ดาสามารถส่งคืนเป็นวัตถุอื่นได้ ตัวอย่างเช่น:
func create_adder_func ( val ) =>
func [ val ] ( x ) => x + val ;
var f = create_adder_func ( 5 ) ;
print ( f ( 1 ) ) ; # Will print 6
print ( f ( 10 ) ) ; # Will print 15
Lambdas ที่มีการจับจะมี สถานะ อย่างที่ใครๆ ก็คาดหวัง พิจารณาสคริปต์ต่อไปนี้:
func gen_counter ( val ) => func [ val ] {
val += 1 ;
return val ;
} ;
var c1 = gen_counter ( 5 ) ;
for ( var i = 0 ; i < 3 ; i += 1 )
print ( " c1: " , c1 ( ) ) ;
# Clone the `c1` lambda object as `c2` : now it will have
# its own state , indipendent from `c1` .
var c2 = clone ( c1 ) ;
print ( ) ;
for ( var i = 0 ; i < 3 ; i += 1 )
print ( " c2: " , c2 ( ) ) ;
print ( ) ;
for ( var i = 0 ; i < 3 ; i += 1 )
print ( " c1: " , c1 ( ) ) ;
มันสร้างผลลัพธ์:
c1: 6
c1: 7
c1: 8
c2: 9
c2: 10
c2: 11
c1: 9
c1: 10
c1: 11
ออบเจ็กต์ฟังก์ชันปกติที่ผู้ใช้กำหนด (รวมถึงแลมบ์ดา) จะไม่ถือเป็น const
ดังนั้นจึง ไม่สามารถ รันได้ในระหว่างการประเมิน const นั่นเป็นข้อจำกัดที่ค่อนข้างแข็งแกร่ง ลองพิจารณาตัวอย่างต่อไปนี้:
const people = [
[ " jack " , 3 ] ,
[ " alice " , 11 ] ,
[ " mario " , 42 ] ,
[ " bob " , 38 ]
] ;
const sorted_people = sort ( people , func ( a , y ) => a [ 0 ] < b [ 0 ] ) ;
ในกรณีนี้ สคริปต์ ไม่สามารถ สร้างอาร์เรย์ const sorted_people
ได้ เนื่องจากเราส่งฟังก์ชันออบเจ็กต์ไปยัง const sort()
บิวด์อิน เราจึงได้รับข้อผิดพลาด ExpressionIsNotConstEx
แน่นอนว่าหาก sorted_people
ถูกประกาศเป็น var
สคริปต์จะทำงาน แต่อาร์เรย์จะไม่เป็น const อีกต่อไป และเราจะไม่สามารถได้รับประโยชน์จากการเพิ่มประสิทธิภาพ เวลาแยกวิเคราะห์ ใดๆ ดังนั้น แม้ว่า sort()
บิวด์อินจะสามารถเรียกใช้ได้ในระหว่างการประเมิน const แต่เมื่อมันมีพารามิเตอร์ compare func
แบบกำหนดเอง นั่นก็เป็นไปไม่ได้อีกต่อไป
เพื่อเอาชนะข้อจำกัดที่อธิบายไว้ MyLang
มีไวยากรณ์พิเศษสำหรับฟังก์ชัน ล้วนๆ เมื่อฟังก์ชันถูกประกาศด้วยคีย์เวิร์ดที่นำหน้า func
pure
จะปฏิบัติต่อฟังก์ชันในลักษณะพิเศษ: สามารถ เรียกได้ตลอดเวลา ทั้ง ในระหว่างการประเมิน const และระหว่างรันไทม์ แต่ ฟังก์ชันไม่สามารถมองเห็นตัวแปรส่วนกลางหรือบันทึกสิ่งใด ๆ : สามารถใช้ได้เท่านั้น ค่าคงที่และค่าของพารามิเตอร์ นั่นคือสิ่งที่เราต้องการในระหว่างการประเมิน const ตัวอย่างเช่น หากต้องการสร้าง sorted_people
ในระหว่างการประเมิน const ก็เพียงพอที่จะเขียน:
const sorted_people = sort ( people , pure func ( a , b ) => a [ 0 ] < b [ 0 ] ) ;
ฟังก์ชัน Pure สามารถกำหนดเป็นฟังก์ชันแบบสแตนด์อโลนและ สามารถ ใช้กับพารามิเตอร์ที่ไม่ใช่ const ได้เช่นกัน ดังนั้น หากสามารถประกาศฟังก์ชันเป็น pure
ได้ ก็ควรประกาศในลักษณะนั้นเสมอ ตัวอย่างเช่น พิจารณาสคริปต์ต่อไปนี้:
pure func add2 ( x ) => x + 2 ;
var non_const = 25 ;
print ( add2 ( non_const ) ) ;
print ( add2 ( 5 ) ) ;
แผนผังไวยากรณ์นามธรรมที่เอ็นจิ้นภาษาจะใช้ในขณะรันไทม์จะเป็น:
$ ./build/mylang -s t
Syntax tree
--------------------------
Block(
FuncDeclStmt(
Id("add2")
<NoCaptures>
IdList(
Id("x")
)
Expr04(
Id("x")
Op '+'
Int(2)
)
)
VarDecl(
Id("non_const")
Op '='
Int(25)
)
CallExpr(
Id("print")
ExprList(
CallExpr(
Id("add2")
ExprList(
Id("non_const")
)
)
)
)
CallExpr(
Id("print")
ExprList(
Int(7)
)
)
)
--------------------------
27
7
อย่างที่คุณเห็น ในกรณีแรกการเรียกใช้ฟังก์ชันจริงเกิดขึ้นเนื่องจาก non_const
ไม่ใช่ค่าคงที่ ในขณะที่ในกรณีที่สอง จะเป็นเหมือนกับว่าเราส่งจำนวนเต็มตามตัวอักษรไป print()
เช่นเดียวกับโครงสร้างอื่นๆ MyLang
มีการจัดการข้อยกเว้นที่คล้ายกับ Python
แต่ใช้ไวยากรณ์ที่คล้ายกับ C++
โครงสร้างพื้นฐานคือคำสั่ง try-catch
ลองดูตัวอย่าง:
try {
var input_str = " blah " ;
var a = int ( input_str ) ;
} catch ( TypeErrorEx ) {
print ( " Cannot convert the string to integer " ) ;
}
หมายเหตุ: หากข้อยกเว้นถูกสร้างขึ้นโดยนิพจน์คงที่ (เช่น int("blah")
) ในระหว่างการประเมิน const ข้อผิดพลาดจะถูกรายงานโดยตรง ข้ามตรรกะการจัดการข้อยกเว้นใดๆ เหตุผลก็คือเพื่อบังคับใช้ ความล้มเหลวตั้งแต่เนิ่นๆ
อนุญาตให้ใช้คำสั่ง catch
หลายรายการได้เช่นกัน:
try {
# body
} catch ( TypeErrorEx ) {
# error handling
} catch ( DivisionByZeroEx ) {
# error handling
}
และในกรณีที่สามารถจัดการข้อยกเว้นหลายประการด้วยโค้ดเดียวกัน ก็สามารถใช้ไวยากรณ์ที่สั้นกว่าได้เช่นกัน:
try {
# body
} catch ( TypeErrorEx , DivisionByZeroEx as e ) {
# error handling
print ( e ) ;
} catch ( OutOfBoundsEx ) {
# error handling
}
ข้อยกเว้นอาจมีข้อมูล แต่ไม่มีข้อยกเว้นในตัวในปัจจุบัน รายการข้อยกเว้น รันไทม์ บิวท์อินที่สามารถตรวจจับได้ด้วยบล็อก try-catch
คือ:
ไม่สามารถตรวจพบข้อยกเว้นอื่นๆ เช่น SyntaxErrorEx
แทน ยังเป็นไปได้ใน MyLang
ที่จะตรวจจับข้อยกเว้นใดๆ โดยใช้บล็อก catch-anything:
try {
# body
} catch {
# Something went wrong .
}
ภาษานี้ไม่รองรับประเภทที่กำหนดเองในขณะนี้ ดังนั้นจึงเป็นไปไม่ได้ที่จะ โยน วัตถุประเภทใดเหมือนในภาษาอื่นบางภาษา หากต้องการส่งข้อยกเว้น คุณจำเป็นต้องใช้ฟังก์ชันพิเศษในตัวข้อ exception()
หรือทางลัด ex()
ลองพิจารณาตัวอย่างต่อไปนี้:
try {
throw ex ( " MyError " , 1234 ) ;
} catch ( MyError as e ) {
print ( " Got MyError, data: " , exdata ( e ) ) ;
}
ตามสัญชาตญาณแนะนำ ด้วย ex()
เราได้สร้างและต่อมาโยนวัตถุข้อยกเว้นที่เรียกว่า MyError
โดยมี 1234
เป็นข้อมูลเพย์โหลด ต่อมาในบล็อก catch
เราตรวจพบข้อยกเว้น และเราได้แยกข้อมูลเพย์โหลดโดยใช้ exdata()
ในตัว
ในกรณีที่ข้อยกเว้นที่กำหนดไม่จำเป็นต้องมีเพย์โหลด คุณสามารถบันทึกผลลัพธ์ของ ex()
ไว้ในตัวแปรแล้วโยนทิ้งในภายหลังโดยใช้ไวยากรณ์ที่น่าพึงพอใจมากกว่า:
var MyError = ex ( " MyError " ) ;
throw MyError ;
MyLang
รองรับการโยนข้อยกเว้นใหม่ในเนื้อหาของคำสั่ง catch โดยใช้คีย์เวิร์ด rethrow
เฉพาะ:
try {
do_something ( ) ;
} catch {
print ( " Something went wrong!! " ) ;
rethrow ;
}
ในบางกรณี อาจจำเป็นต้องทำการล้างข้อมูล หลังจากดำเนินการบล็อกโค้ดที่อาจทำให้เกิดข้อยกเว้น ในกรณีเหล่านี้ MyLang
สนับสนุนประโยค finally
ที่รู้จักกันดี ซึ่งทำงานเหมือนกับใน C#
:
try {
step1_might_throw ( ) ;
step2_might_throw ( ) ;
step3_might_throw ( ) ;
step4_might_throw ( ) ;
} catch ( TypeErrorEx ) {
# some error handling
} finally {
# clean - up
}
เป็นที่น่าสังเกตว่า try-finally
(โดยไม่มีส่วนคำสั่ง catch
) ก็ได้รับอนุญาตเช่นกัน
ฟังก์ชันในตัวต่อไปนี้จะได้รับการประเมินในช่วง เวลาแยกวิเคราะห์ เมื่อมีการส่งผ่านอาร์กิวเมนต์ const ไปให้ฟังก์ชันเหล่านั้น
defined(symbol)
ตรวจสอบว่ามีการกำหนด symbol
หรือไม่ ส่งคืน 1 ถ้ามีการกำหนดสัญลักษณ์ มิฉะนั้นจะเป็น 0
len(container)
ส่งกลับจำนวนองค์ประกอบในภาชนะที่กำหนด
str(value, [decimal_digits])
แปลงค่าที่กำหนดให้เป็นสตริง หาก value
เป็นแบบทศนิยม พารามิเตอร์ตัวที่ 2 จะระบุจำนวนหลักทศนิยมที่ต้องการในสตริงเอาต์พุต
int(value)
แปลงสตริงที่กำหนดให้เป็นจำนวนเต็ม หากค่าเป็นแบบลอยตัว ค่านั้นจะถูกตัดทอน หากค่าเป็นสตริง ค่านั้นจะถูกแยกวิเคราะห์และแปลงเป็นจำนวนเต็ม หากเป็นไปได้ หากค่าเป็นจำนวนเต็มอยู่แล้ว ค่านั้นจะถูกส่งกลับเหมือนเดิม
float(value)
แปลงค่าที่กำหนดให้เป็นทศนิยม หากค่าเป็นจำนวนเต็ม ค่านั้นจะถูกแปลงเป็นตัวเลขทศนิยม หากค่าเป็นสตริง ค่านั้นจะถูกแยกวิเคราะห์และแปลงเป็นแบบทศนิยม หากเป็นไปได้ หากค่าเป็นแบบทศนิยมอยู่แล้ว ค่านั้นจะถูกส่งกลับเหมือนเดิม
clone(obj)
โคลนวัตถุที่กำหนด มีประโยชน์สำหรับวัตถุที่ไม่สำคัญ เช่น อาร์เรย์ พจนานุกรม และแลมบ์ดาพร้อมการจับ
type(value)
ส่งกลับชื่อของประเภทของค่าที่กำหนดในรูปแบบสตริง มีประโยชน์สำหรับการดีบัก
hash(value)
ส่งคืนค่าแฮชที่ใช้โดยพจนานุกรมภายในเมื่อ value
ถูกใช้เป็นคีย์ ในขณะนี้ เฉพาะจำนวนเต็ม ทศนิยม และสตริงเท่านั้นที่รองรับ hash()
array(N)
ส่งกลับอาร์เรย์ที่ none