Clem là một ngôn ngữ lập trình dựa trên ngăn xếp tối thiểu có các chức năng hạng nhất. Mục tiêu của bạn là viết một trình thông dịch cho ngôn ngữ Clem. Nó nên thực hiện đúng tất cả các ví dụ có trong triển khai tham chiếu, có sẵn ở đây .
- Như thường lệ, sơ hở tiêu chuẩn áp dụng.
- Mục nhỏ nhất bằng số byte thắng.
Ngôn ngữ Clem
Clem là một ngôn ngữ lập trình dựa trên ngăn xếp với các chức năng hạng nhất. Cách tốt nhất để học Clem là chạy trình clem
thông dịch không có đối số. Nó sẽ bắt đầu trong chế độ tương tác, cho phép bạn chơi với các lệnh có sẵn. Để chạy các chương trình ví dụ, nhập clem example.clm
ví dụ là tên của chương trình. Hướng dẫn ngắn gọn này là đủ để bạn bắt đầu.
Có hai lớp chức năng chính. Hàm nguyên tử và hàm ghép. Hàm hợp chất là danh sách gồm các hàm ghép và hàm nguyên tử khác. Lưu ý rằng một hàm ghép không thể chứa chính nó.
Chức năng nguyên tử
Loại đầu tiên của hàm nguyên tử là hằng số . Một hằng số chỉ đơn giản là một giá trị nguyên. Ví dụ: -10. Khi trình thông dịch gặp một hằng số , nó sẽ đẩy nó vào ngăn xếp. Chạy clem
ngay đi. Gõ -10
tại dấu nhắc. Bạn nên thấy
> -10
001: (-10)
>
Giá trị 001
mô tả vị trí của hàm trong ngăn xếp và (-10)
là hằng số bạn vừa nhập. Bây giờ nhập +11
tại dấu nhắc. Bạn nên thấy
> +11
002: (-10)
001: (11)
>
Lưu ý rằng (-10)
đã di chuyển đến vị trí thứ hai trong ngăn xếp và (11)
bây giờ chiếm vị trí đầu tiên. Đây là bản chất của một chồng! Bạn sẽ nhận thấy đó -
cũng là lệnh giảm dần. Bất cứ khi nào -
hoặc +
trước một số, họ biểu thị dấu hiệu của số đó và không phải là lệnh tương ứng. Tất cả các chức năng nguyên tử khác là các lệnh . Có tổng cộng 14 cái:
@ Rotate the top three functions on the stack
# Pop the function on top of the stack and push it twice
$ Swap the top two functions on top of the stack
% Pop the function on top of the stack and throw it away
/ Pop a compound function. Split off the first function, push what's left,
then push the first function.
. Pop two functions, concatenate them and push the result
+ Pop a function. If its a constant then increment it. Push it
- Pop a function. If its a constant then decrement it. Push it
< Get a character from STDIN and push it to the stack. Pushes -1 on EOF.
> Pop a function and print its ASCII character if its a constant
c Pop a function and print its value if its a constant
w Pop a function from the stack. Peek at the top of the stack. While it is
a non-zero constant, execute the function.
Nhập lệnh tại dấu nhắc sẽ thực thi lệnh. Gõ #
tại dấu nhắc (lệnh trùng lặp). Bạn nên thấy
> #
003: (-10)
002: (11)
001: (11)
>
Lưu ý rằng (11) đã được nhân đôi. Bây giờ gõ %
tại dấu nhắc (lệnh thả). Bạn nên thấy
> %
002: (-10)
001: (11)
>
Để đẩy một lệnh vào ngăn xếp, chỉ cần đặt nó trong ngoặc đơn. Gõ (-)
tại dấu nhắc. Điều này sẽ đẩy toán tử giảm dần đến ngăn xếp. Bạn nên thấy
> (-)
003: (-10)
002: (11)
001: (-)
>
Hàm hợp chất
Bạn cũng có thể đặt nhiều hàm nguyên tử trong ngoặc đơn để tạo thành hàm ghép. Khi bạn nhập một hàm ghép tại dấu nhắc, nó sẽ được đẩy đến ngăn xếp. Gõ ($+$)
tại dấu nhắc. Bạn nên thấy
> ($+$)
004: (-10)
003: (11)
002: (-)
001: ($ + $)
>
Về mặt kỹ thuật, mọi thứ trên stack là một hàm ghép. Tuy nhiên, một số hàm tổng hợp trên ngăn xếp bao gồm một hàm nguyên tử duy nhất (trong trường hợp này, chúng tôi sẽ coi chúng là các hàm nguyên tử vì mục đích thuận tiện). Khi thao tác các hàm ghép trên ngăn xếp, .
lệnh (nối) thường hữu ích. Gõ .
ngay. Bạn nên thấy
> .
003: (-10)
002: (11)
001: (- $ + $)
>
Lưu ý rằng các hàm thứ nhất và thứ hai trên ngăn xếp được nối với nhau và hàm thứ hai trên ngăn xếp đứng đầu trong danh sách kết quả. Để thực thi một chức năng trên ngăn xếp (cho dù đó là nguyên tử hay hợp chất), chúng ta phải ban hành w
lệnh (while). Các w
lệnh sẽ bật chức năng đầu tiên trên stack và thực hiện nó lặp đi lặp lại quá lâu như chức năng thứ hai trên stack là một hằng số khác không. Cố gắng dự đoán những gì sẽ xảy ra nếu chúng ta gõ w
. Bây giờ, gõ w
. Bạn nên thấy
> w
002: (1)
001: (0)
>
Đó có phải là những gì bạn mong đợi? Hai số ngồi trên cùng của ngăn xếp đã được thêm vào và tổng của chúng vẫn còn. Hãy thử lại. Đầu tiên chúng ta sẽ bỏ số 0 và đẩy số 10 bằng cách gõ %10
. Bạn nên thấy
> %10
002: (1)
001: (10)
>
Bây giờ chúng ta sẽ nhập toàn bộ chức năng trong một lần chụp, nhưng chúng ta sẽ thêm một phần bổ sung %
vào cuối để loại bỏ số không. Gõ (-$+$)w%
tại dấu nhắc. Bạn nên thấy
> (-$+$)w%
001: (11)
>
(Lưu ý thuật toán này chỉ hoạt động nếu hằng số đầu tiên trên ngăn xếp là dương).
Dây
Chuỗi cũng có mặt. Chúng chủ yếu là đường cú pháp, nhưng có thể khá hữu ích. Khi trình thông dịch gặp một chuỗi, nó sẽ đẩy từng ký tự từ cuối đến trước trên ngăn xếp. Nhập %
để thả 11 từ ví dụ trước. Bây giờ, gõ 0 10 "Hi!"
vào dấu nhắc. Các 0
sẽ chèn một terminator NULL và 10
sẽ chèn một kí tự xuống dòng. Bạn nên thấy
> 0 10 "Hi!"
005: (0)
004: (10)
003: (33)
002: (105)
001: (72)
>
Nhập (>)w
để in các ký tự từ ngăn xếp cho đến khi chúng ta gặp bộ kết thúc NULL. Bạn nên thấy
> (>)w
Hi!
001: (0)
>
Kết luận
Hy vọng rằng điều này là đủ để bạn bắt đầu với thông dịch viên. Thiết kế ngôn ngữ nên tương đối đơn giản. Hãy cho tôi biết nếu có bất cứ điều gì không rõ ràng khủng khiếp :) Một vài điều còn bị cố ý mơ hồ: các giá trị phải được ký và ít nhất 16 bit, ngăn xếp phải đủ lớn để chạy tất cả các chương trình tham chiếu, v.v. Nhiều chi tiết chưa được khắc ra khỏi đây bởi vì một đặc điểm kỹ thuật ngôn ngữ đầy đủ sẽ rất lớn để đăng (và tôi chưa viết một thứ nào: P). Khi nghi ngờ, bắt chước thực hiện tham chiếu.