Yuescript
เป็นภาษาที่พัฒนาต่อยอดจาก MoonScript ซึ่งเป็นภาษาที่แปลงเป็นภาษา Lua อีกทีหนึ่งเช่นเดียวกับ Fennel ในบทความก่อน โดยแต่เดิมใช้ชื่อว่า MoonPlus หรือ Moon+ ภายหลังเปลี่ยนชื่อเป็น Yuescript โดยคำว่า Yue มาจากเสียงอ่านคำว่าดวงจันทร์ในภาษาจีน ( 月 ) ซึ่งใน Logo ก็ใช้สัญลักษณ์ตัวอักษร Yue กับรูปจันทร์เสี้ยวกลางหมู่เมฆให้อารมณ์จีนดี เป็นการออกแบบที่ลงตัวผมชอบนะ
MoonScript กับ Lua
มาทำความรู้จักกับ MoonScript ซึ่งเป็นภาษาต้นแบบก่อน แต่เดิมภาษา Lua นั้นเป็นภาษาที่มีโครงสร้างเรียบง่ายและมีความยืดหยุ่นแต่ในขณะเดียวกันก็ดูเยิ่นเย้อ MoonScript จึงถูกสร้างขึ้นเพื่อทำให้การเขียนโปรแกรมสะดวกขึ้น เหมือนอย่าง CoffeeScript ที่เคยนิยมเขียนแทน JavaScript อยู่พักหนึ่ง โดยเอาคุณลักษณะของหลายๆ ภาษามาใช้เช่นเปลี่ยนการใช้ keyword กำหนดบล๊อก มาใช้การย่อหน้าเหมือน Python แทน ลดการใช้วงเล็บเหมือนใน Ruby เป็นต้น นอกจากนี้ยังเพิ่มการอำนวยความสะดวกเช่น
- ให้ตัวแปรเป็น Local โดยปริยายกลับกันกับ Lua
- เพิ่ม Update assignment operator เช่น " += ", " -= ", " *= ", " /= ", " %= ", " ..= ", " or= ", " and= "
- มี string interpolation เหมือนใน Ruby
print "1 + 1 = #{1+1}" -- print("1 + 1 = " .. tostring(1 + 1)) -- "1 + 1 = 2" name = "Sam" print "My name is #{name}." -- print("My name is " .. tostring(name) .. ".") -- "My name is Sam."
- มีการจัดการการเขียนแบบ OOP ที่เป็นระบบมาตรฐาน มีระบบ class ให้ใช้ไม่ต้องมากำหนด metatable เอง
class Animal new: (color) => @color = color -- constructor method class Cat extend Animal -- สืบทอดจากคลาส Animal leg: 4 -- static property new: (name, color) => -- constructor method super color -- เรียก constructor method ของ Animal @name = name getname: => @name mao = Cat "Mao", "black" print mao\getname! -- "Mao"
- เปลี่ยนรูปแบบของ function และ method ให้กระชับ
add = (a, b) -> a + b -- local add -- add = function(a, b) -- return a + b -- end fact = (n) -> -- local fact f = 1 -- fact = function(n) for i = 2, n -- local f = 1 f *= n -- for i = 2, n do f -- f = f * n -- end -- return f -- end mod.set_x = (x) => @x = x -- mod.set_x = function(self, x) -- self.x = x -- end mod\set_x 10 -- mod:set_x(10) mod.set_x mod, 10 -- mod.set_x(mod, 10)
- คืนค่านิพจน์สุดท้ายในฟังก์ชันเสมอโดยไม่ต้องกำหนด return แต่ยังสามารถใส่ return เพื่อคืนค่าในตำแหน่งอื่นที่ต้องการได้
fact = (n) -> -- local fact if n < 0 then return nil -- fact = function(n) if n == 0 then return 1 -- if n < 0 then if n < 3 -- return nil return n -- end else -- if n == 0 then n * fact n - 1 -- return 1 -- end -- if n < 3 then -- return n -- else -- return n * fact(n - 1) -- end -- end
- if และ for สามารถใช้เป็นนิพจน์ได้
if a > b -- if a > b then print "A" -- print("A") else -- else print "B" -- print("B") -- end -- สามารถเขียนเป็นนิพจน์ได้ print if a > b then "A" else "B" -- print((function() -- if a > b then -- return "A" -- else -- return "B" -- end -- end)()) -- แต่ถ้าเป็นเงื่อนไขเดียว local res -- local res if a > b -- if a > b then res = "A" -- res = "A" -- end -- สามารถเขียนสองบรรทัด local res if a > b then res = "A" -- หรือสั้นกว่านั้น res = if a > b then "A" -- หรือสั้นกว่านั้นอีก res = "A" if a > b -- for ก็เป็นนิพจน์ได้เช่นกัน table1 = for i=2, 4 do i*3 -- table1 == {6, 9, 12} table2 = i*3 for i=2, 4 -- table2 == 12 table3 = [i*3 for i=2, 4] -- table3 == {6, 9, 12} print i, v for i, v in ipairs(table1) -- for i, v in ipairs(table1) do -- print(i, v) -- end -- ผลลัพธ์ -- 1 6 -- 2 9 -- 3 12
- สามารถกำหนดค่าในเงื่อนไขได้
if f = io.open "abc.txt" -- do text = f\read! -- local f = io.open("abc.txt") f.close! -- if f then -- local text = f:read() -- f.close() -- end -- end
- เพิ่ม unless เหมือนการใช้ if not และเพิ่ม continue ในลูป
- มี switch case
switch exp -- local _exp_0 = exp when 2 -- if 2 == _exp_0 then print "Two" -- print "Two" when "greet" -- elseif "greet" == _exp_0 then print "Hello" -- print "Hello" else -- else print "What?" -- print "What?" -- end -- เช่นเดียวกับ if และ for ที่สามารถเป็นนิพจน์ได้ grade = switch score -- local grade when 80 -- local _exp_1 = score "A" -- if 80 == _exp_1 then when 70 -- grade = "A" "B" -- elseif 70 == _exp_1 then else -- grade = "B" "F" -- else -- grade = "C" -- end
- มี list comprehension, table comprehension และ slicing คล้ายใน Python
a = {2, 3, 4, 5, 6, 7} b = [x for x in *a when x % 2 == 0] -- b == {2, 4, 6} c = [x for x in *a[2,6,2]] -- c == {3, 5, 7} d = [a[i]*2 for i=1,#a] -- d == {4, 6, 8, 10, 12, 14} e = {i,a[i] for i=1,#a,2} -- e == {2, [3]=4, [5]=6} f = {i,v for i,v in ipairs(a) when i<4} -- f == {2, 3, 4}
- มี destructuring คล้ายใน JavaScript
{axis: {x, y}} = axis: {3, 5, 8}, v: 5 -- x == 3, y == 5
- มี with statement
with Person "John", "Hello " -- do print \getname! -- local _with_0 = Person("John", "Hello ") print \say "World." -- print(_with_0:getname()) print .word -- print(_with_0:say("World.")) -- print(_with_0.word) -- end
- มี import คล้าย Python แต่ไม่ได้ใช้โหลดมอดูลแทน require แต่ใช้ช่วยให้แตก method มาเก็บในตัวแปร local
lpeg = require "lpeg" -- local lpeg = require("lpeg") import P, S, R from lpeg -- local P, S, R -- P, S, R = lpeg.P, lpeg.S, lpeg.R import sub, rep from string -- local sub, rep -- do -- local _obj_0 = string -- sub, rep = _obj_0.sub, _obj_0.rep -- end import C, Ct from require "lpeg" -- local C, Ct -- do -- local _obj_0 = require("lpeg") -- C, Ct = _obj_0.C, _obj_0.Ct -- end -- ใช้กับ method ใน class หรือ instance ก็ได้ import \say from Person "John", "Hi." -- local say -- do -- local _base_0 = Person("John", "Hi.") -- local _fn_0 = _base_0.say -- say = function(...) -- return _fn_0(_base_0, ...) -- Person.say(self, ...) -- end -- end
สิ่งที่เปลี่ยนไปจาก MoonScript
- เขียนด้วยภาษา C++ แทนการใช้ Lua เพื่อเพิ่มประสิทธิภาพในการแปลภาษา
- ปรับปรุงการแปลงโค้ดให้กระชับขึ้น
- เปลี่ยนรูปแบบคำสั่ง import สามารถใช้โหลดมอดูลได้เหมือน require ใน Lua
import "lpeg" -- local lpeg = require("lpeg") import P, S, R from lpeg -- local P, S, R = lpeg.P, lpeg.S, lpeg.R import sub, rep from string -- local sub, rep = string.sub, string.rep import "inspect" as insp -- local insp = require("inspect") import "lpeg" as :C -- local C = require("lpeg").C import "lpeg" as {:Ct, Cmt:cmt} -- local Ct, cmt -- do -- local _obj_0 = require("lpeg") -- Ct, cmt = _obj_0.Ct, _obj_0.Cmt -- end import "person" as {:Person:{:word}} -- local word -- do -- local _obj_0 = require("person") -- word = _obj_0.Person.word -- end import V from require "lpeg" -- local V -- do -- local _obj_0 = require("lpeg") -- V = _obj_0.V -- end
- เพิ่ม operator ใหม่ๆ เช่น metatable, existence, และ piping
-- Metatable และ Metamethod a = #: m -- local a = setmetatable({}, m) b = #: m, key1: "val1", key2: "val2" -- local b = setmetatable({key1 = "val1", key2 = "val2"}, m) c = :add# -- local c = setmetatable({}, {__add = add}) d = :add#, :add, sub#: fn1, sub: fn2 -- local d = setmetatable({ -- add = add, -- sub = fn2 -- }, { -- __add = add, -- __sub = fn1 -- } e = ["key"]: "val" -- local e = setmetatable({}, {key = "val"}) f = a.# -- local f = getmetatable(a) ( f == m ) g = c.add# -- local g = getmetatable(a).__add -- Existence ก็คือ nil safe operator เพื่อป้องกันข้อผิดพลาดเวลาเรียกฟังก์ชันหรือสมาชิกใน table ที่ไม่มีอยู่ func! -- func() func?! -- if func ~= nil then -- func() -- end h = tab?.val -- local h -- if tab ~= nil then -- h = tab.val -- end -- Piping เหมือนใน Fennel ใช้จัดลำดับการทำงานเพื่อให้โค้ดที่มีการเรียกฟังก์ชันซ้อนกันดูง่ายเป็นระเบียบขึ้นไม่ซับซ้อน a |> print 2 -- print(a, 2) 2 |> print 1, _, 3 -- print(1, 2, 3) 1 |> math.sin -- print(math.atan(2, math.sin(1)), "asdf") |> math.atan 2, _ |> print "asdf"
- เปลี่ยนใช้ global แทน export สำหรับตัวแปร global แล้วไปใช้ export สำหรับการส่งออก module แทน
-- ของเดิมใน MoonScript MY_CONSTANT = "hello" export GLOBAL_CONSTANT = 10 -- ใช้ export ประกาศตัวแปรแบ global private_function = -> print "private" my_function1 = -> print "the function" my_function2 = -> private_function! print "another function" -- เอาตัวแปรที่ต้องการส่งออกใส่ใน table { :my_function1, my_func2: my_function2, :MY_CONSTANT}
-- แปลงเป็น Lua local MY_CONSTANT = "hello" GLOBAL_CONSTANT = 10 local private_function private_function = function() return print("private") end local my_function1 my_function = function() return print("the function") end local my_function2 my_second_function = function() private_function() return print("another function") end return { my_function1 = my_function1, my_func2 = my_function2, MY_CONSTANT = MY_CONSTANT }
-- ของใหม่ใน Yuescript export MY_CONSTANT = "hello" -- ใช้ export ในการส่งออกตัแปรในมอดูล global GLOBAL_CONSTANT = 10 -- ใช้ global สำหรับตัวแปรแบ global private_function = -> print "private" export my_function1 = -> print "the function" export my_function2 = -> private_function! print "another function"
-- แปลงเป็น Lua local _module_0 = { } local MY_CONSTANT = "hello" _module_0["MY_CONSTANT"] = MY_CONSTANT GLOBAL_CONSTANT = 10 local private_function private_function = function() return print("private") end local my_function1 my_function = function() return print("the function") end _module_0["my_function1"] = my_function1 local my_second_function my_second_function = function() private_function() return print("another function") end _module_0["my_function2"] = my_function2 return _module_0
- เพิ่มระบบ macro ช่วยดัดแปลง syntax ของภาษาเหมือนที่มีใน Fennel
macro or_not = (...)-> "not (#{ table.concat {...}, ' or ' })" if $or_not f1!, f2!, f3! print "OK"
-- แปลงเป็น Lua if not(f1() or f2() or f3()) then print "OK" end
- เพิ่ม repeat until และ goto syntax เหมือนที่มีใน Lua
- ปรับปรุง syntax ในการสร้าง table
-- ของเดิมใน MoonScript inventory = equipment: { -- table แบบ hash ไม่ต้องใส่วงเล็บ "sword" -- table แบบ list ต้องใส่วงเล็บ "shield" } items: { { -- ถ้ามี table ซ้อนก็ต้องใส่วงเล็บ name: "potion" count: 10 }, { -- บรรทัดเดียวกันใส่ "," คั่น name: "bread" count: 3 } } -- ของใหม่ใน Yuescript inventory = equipment: * "sword" -- ใส่ "*" หน้าแต่ละรายการใน list ย่อย * "shield" items: * name: "potion" -- ถ้าเป็น hash ใส่เฉพาะรายการแรก count: 10 * name: "bread" count: 3
-- แปลงเป็น Lua inventory = { equipment = { "sword", "shield" }, items = { { name = "potion", count = 10 }, { name = "bread", count = 3 } } }
- รองรับ attribute cons และ close ใน Lua 5.4
- Fixed bug และอื่นๆ
จบไปอีกหนึ่งภาษาสำหรับภาษาที่แปลงเป็น Lua นะครับพบกันใหม่คราวหน้า
ความคิดเห็น
แสดงความคิดเห็น