Skip to content
This repository has been archived by the owner on Sep 15, 2019. It is now read-only.

Latest commit

 

History

History
266 lines (214 loc) · 8.32 KB

Bogo.adoc

File metadata and controls

266 lines (214 loc) · 8.32 KB

Bogo-logic

1. Quy ước

Để nhất quán trong việc xử lý tiếng Việt, chúng ta thống nhất ý nghĩa của các thuật ngữ sau trong Bogo-logic:

  • Dấu thanh là các dấu thay đổi thanh điệu của nguyên âm. Tiếng Việt có 6 loại dấu thanh:

    • Thanh ngang (không dấu)

    • Thanh huyền (grave)

    • Thanh sắc (acute)

    • Thanh hỏi (hook)

    • Thanh ngã (tilde)

    • Thanh nặng (dot)

code::tone-defs
local TONE_GRAVE,
      TONE_ACUTE,
      TONE_HOOK,
      TONE_TILDE,
      TONE_DOT,
      TONE_NONE = 1, 2, 3, 4, 5, 6
  • Dấu chữ cái là các dấu để tạo chữ cái mới từ chữ cái sẵn có:

    • Dấu mũ (hat, như trong chữ cái â)

    • Dấu móc (horn, như trong chữ cái ơ)

    • Dấu trăng (breve, như trong chữ cái ă)

    • Dấu ngang (dash, như trong chữ cái đ)

code::mark-defs
local MARK_BREVE,
      MARK_HAT,
      MARK_HORN,
      MARK_DASH,
      MARK_NONE = 1, 2, 3, 4, 5
  • Nguyên âm (vowel) là một âm thanh trong ngôn ngữ nói, được phát âm với thanh quản mở. Ví dụ: a, e, u, ư, uyê, …​

    Phụ âm (consonant) là một âm thanh trong ngôn ngữ nói, được phát âm với thanh quản đóng. Ví dụ: b, c, ph, tr, …​

Warning
Khi ký âm (chuyển từ âm thanh sang chữ viết), nguyên âm hay phụ âm có thể được tạo thành từ một hoặc nhiều chữ cái. Khái niệm nguyên âm, phụ âm thường bị hiểu sai là một chữ cái đơn lẻ.
Note
Khi nói về nguyên âm hoặc phụ âm chỉ được cấu tạo bởi một chữ cái, chúng tôi sẽ dùng chữ cái nguyên âmchữ cái phụ âm tương ứng.
  • Âm tiết (syllable) là đơn vị cấu tạo từ cơ bản. Ví dụ:

    • Từ nhà có một âm tiết: nhà.

    • Từ la-tinh có 2 âm tiết: latinh.

    • Từ hợp tác xã có 3 âm tiết: hợp, tác, và .

2. Xử lý tiếng Việt

Như mọi bộ gõ khác, phần quan trọng nhất của Bogo-logic là xử lý tiếng Việt. Công việc này được chia thành 2 thao tác lớn:

  • Thêm dấu chữ cái

  • Thêm dấu thanh

Để thêm dấu thanh, trước hết chúng ta sẽ định nghĩa một bảng tra cứu tuần hoàn tất cả các chữ cái nguyên âm trong tiếng Việt với tất cả các tổ hợp dấu thanh có thể.

code::vowel-chars-defs
local VOWEL_CHARS = {
    "à", "á", "", "ã", "", "a", "", "", "", "", "", "ă",
    "", "", "", "", "", "â", "è", "é", "", "", "", "e",
    "", "ế", "", "", "", "ê", "ì", "í", "", "ĩ", "", "i",
    "ò", "ó", "", "õ", "", "o", "", "", "", "", "", "ô",
    "", "", "", "", "", "ơ", "ù", "ú", "", "ũ", "", "u",
    "", "", "", "", "", "ư", "", "ý", "", "", "", "y"
}

Để ý kỹ có thể thấy ngay các chữ cái nguyên âm trong bảng được sắp xếp theo trật tự huyền, sắc, hỏi, ngã, nặng, ngang, đúng với trật tự chúng ta đã định nghĩa các mã dấu thanh ở trên. Nhờ vậy, chúng ta có thể định nghĩa thuật toán thêm dấu thanh đơn giản là tìm vị trí nhóm nguyên âm tương ứng với nguyên âm cần thêm dấu trong bảng và sử dụng mã dấu thanh cần thêm làm tọa độ tương đối so với nhóm.

code::add-tone-char
-- include::vowel-chars-defs

function add_tone_char (char, tone)
    for i, v in pairs(VOWEL_CHARS) do
        if char == v then
            return VOWEL_CHARS[math.floor((i - 1) / 6) * 6 + tone]
        end
    end

    return char
end

describe("add_tone_char", function ()
    it("adds a tone to a char", function ()
        assert.are.equal(add_tone_char ("a", TONE_ACUTE), "á")
        assert.are.equal(add_tone_char ("", TONE_HOOK), "")
        assert.are.equal(add_tone_char ("", TONE_NONE), "u")
    end)
end)

Việc thêm dấu chữ cái cũng cơ bản chỉ là thao tác tra cứu bảng. Chúng ta định nghĩa một bảng chứa tất cả các chữ cái có thể thêm dấu chữ cái và kết quả khi thêm dấu. Tuy nhiên, để đơn giản, chúng ta sẽ chỉ định nghĩa phiên bản không dấu thanh của các chữ cái đó. Thay vì định nghĩa hết, chúng ta sẽ lưu dấu thanh của chữ cái đầu vào, xóa dấu đó đi, tra bảng để thêm dấu chữ cái, sau đó trả lại dấu thanh như ban đầu.

code::add-mark-char
-- include::get-tone-char

function add_mark_char (char, mark)
    local tone = get_tone_char(char)

    if tone ~= TONE_NONE then
        char = add_tone_char(char, TONE_NONE)
    end

    local mapping = {
        ["a"] = {[MARK_HAT] = "â", [MARK_BREVE] = "ă", [MARK_NONE] = "a"},
        ["ă"] = {[MARK_HAT] = "â", [MARK_BREVE] = "ă", [MARK_NONE] = "a"},
        ["â"] = {[MARK_HAT] = "â", [MARK_BREVE] = "ă", [MARK_NONE] = "a"},
        ["e"] = {[MARK_HAT] = "ê", [MARK_NONE] = "e"},
        ["ê"] = {[MARK_HAT] = "ê", [MARK_NONE] = "e"},
        ["o"] = {[MARK_HAT] = "ô", [MARK_HORN] = "ơ", [MARK_NONE] = "o"},
        ["ô"] = {[MARK_HAT] = "ô", [MARK_HORN] = "ơ", [MARK_NONE] = "o"},
        ["ơ"] = {[MARK_HAT] = "ô", [MARK_HORN] = "ơ", [MARK_NONE] = "o"},
        ["u"] = {[MARK_HORN] = "ư", [MARK_NONE] = "u"},
        ["ư"] = {[MARK_HORN] = "ư", [MARK_NONE] = "u"},
        ["d"] = {[MARK_DASH] = "đ", [MARK_NONE] = "d"},
        ["đ"] = {[MARK_DASH] = "đ", [MARK_NONE] = "d"},
    }

    if mapping[char] and mapping[char][mark] then
        char = mapping[char][mark]
    end

    if tone ~= TONE_NONE then
        char = add_tone_char(char, tone)
    end

    return char
end

describe("add_mark_char", function ()
    it("adds mark to a char", function ()
        assert.are.equal(add_mark_char("e", MARK_HAT), "ê")
        assert.are.equal(add_mark_char("e", MARK_NONE), "e")
        assert.are.equal(add_mark_char("", MARK_HORN), "")
    end)
end)

Và hàm hỗ trợ get_tone_char.

code::get-tone-char
function get_tone_char (char)
    for i, v in pairs(VOWEL_CHARS) do
        if char == v then
            return (i - 1) % 6 + 1
        end
    end

    return TONE_NONE
end

describe("get_tone_char", function ()
    it("returns the tone", function ()
        assert.are.equal(TONE_ACUTE, get_tone_char("á"))
        assert.are.equal(TONE_HOOK, get_tone_char(""))
        assert.are.equal(TONE_NONE, get_tone_char("e"))
    end)
end)

Công việc đầu tiên cần làm là định nghĩa cấu trúc dữ liệu tốt, thuận tiện cho việc xử lý tiếng Việt.

2.1. Cấu trúc dữ liệu cho âm tiết

file::/tmp/tmp.lua

3. Các công cụ hỗ trợ

Kiểm tra chữ cái đầu vào có phải một chữ cái nguyên âm không.

code::is-vowel-char
function is_vowel_char (char)
    if string.find("aeiouy", char) then
        return true
    else
        return false
    end
end

Chương trình của chúng ta sử dụng bộ kiểm thử busted cho Lua. Nếu chạy file Lua thông qua bộ kiểm thử thì nó sẽ định nghĩa sẵn các hàm describe, it,…​ còn nếu không thì sẽ không có và không thể thực thi chương trình. Vậy chúng ta sẽ định nghĩa một hàm describe giả trong trường hợp chương trình không chạy qua bộ kiểm thử.

code::fake-describe
if describe == nil then
    describe = function () end
end

4. Tổng thể chương trình

Sau khi đã định nghĩa tất cả các thành phần cần thiết, chúng ta đã có thể ghép lại thành một thư viện hoàn chỉnh.

file::bogo.lua
-- include::fake-describe

-- include::tone-defs
-- include::mark-defs

-- include::add-tone-char
-- include::add-mark-char