# Quick reference ## Overview Mei is a strongly typed, polymorphic purely functional programming language inspired by Hope, Miranda and Haskell. ### Logo The Mei programming language logo ### Mascot Mei-chan, the Mei mascot ### Downloads Please note that at this moment the Mei interpreter is still in α-stage, so there may be bugs, crashes, undefined behavior, holocaust, data destruction, the heat death of the Universe etc. #### Linux - [x86_64](https://files.txlyre.website/d/software/mei/latest_x86_64/mei) (updated more frequently than others) - [aarch64](https://files.txlyre.website/d/software/mei/latest_aarch64/mei) - [armv7l](https://files.txlyre.website/d/software/mei/latest_armv7l/mei) (not updated for a long time!) - [e2k](https://files.txlyre.website/d/software/mei/latest_e2k/mei) (not updated for a long time!) #### Windows - [x86_64](https://files.txlyre.website/d/software/mei/Windows_x86_64_0.9a_Jul_11_2023/Windows_x86_64_0.9a_Jul_11_2023.zip) (WIP; may have a lot of unexpected bugs; rarely updated) #### Package manager Also check the [Mei Package Manager](https://files.txlyre.website/d/software/mpm/) (requires Python 3.5+ and Git installed to work). #### Automatic installer (Linux-only!) Alternatively, you could use [this Bash script](https://files.txlyre.website/d/software/mei_installer/mei_installer.sh) that tries to install the interpreter and package manager automatically (requires to be run as **root**). #### Motools (a kind of FFI for Mei) You may be interested to see [motools](https://files.txlyre.website/d/software/motools/) – it's a C header that is used to create dynamic-link libraries loadable by the Mei interpreter. #### Usage ``` usage: mei [-?hir] [-l file] [-c expr] file -?h print this message and exit -i read and execute a program from the standard input (stdin) -r enter the interactive mode (a.k.a. Read-Eval-Print-Loop) -s disable file system I/O functions (safe-mode) -l file load the specified file first -c expr execute the specified argument as an expression file execute the specified file -c help print the quick reference (or `-c help!' to display using the pager) ``` ## Syntax + Comments (single line / multiple lines): -- bla -[ bla bla bla ]- + Number literals: 42; 42.5; 3.1415 -- Number literal (arbitrary precision). Type: num. 4_000; 40_000; 4_000_000 -- Underscores in numeric literals. 42I -- Integer. Type: int. 0xff; 0o777; 0b1011 -- Non-decimal integer literals. 3J -- Imaginary number. Type: imag. 3J+:3 -- Complex number. Type: com. + Other common scalar types: '何'; 'a'; 'ж' -- Character (Unicode codepoint). Type: char. B'q'; B'\x00'; B33 -- Byte (0-255 value). Type: byte. True; False -- Boolean. Type: bool. + Collections: []; [1]; [1,2,3] -- List. Type: [a]; [num]; [num]. (); (1,); (1, 'a', "hi") -- Tuple. Type: (); (num,); (num, char, string). -- String. Type: string or [char]. "Hello, World!" -- Syntactic sugar for a list of characters. "Hello\nWorld!" "Hello\xaWorld!" R"(\w+)" -- Raw-string (no escape sequences). M"Hello world!" -- Multiline string. W"Hello\n world!" -- Multiline raw-string. ['a', 'b', 'c'] -- Bytestring. Type: bytes or [byte]. B"Hello,\x00World!" -- Syntactic sugar for a list of bytes. -- ^ notice that bytestrings may contain NUL-bytes. [B'a', B'c', B'd'] + Structures: Just 4; Error "too long" -- Algrebraic type instance. Type: maybe num; result a. {x=4, y=5} -- Record. Type: {x :: num, y :: num}. + Function: -- Application. succ x prime? 6 filter prime? [1,2,3,5,6,7,8] -- Section (partial application). (+ 3) (2 *) map (** 3) -- Lambda-function (a.k.a. anonymous function). \x -> x*(n-2)+1 -- Type: num -> num. \(l, _) -> l -- Type: (a, b) -> a + Various expressions: -- Integer range (inclusive). Type: [int]. [..5] [4..9] [8..(-1)] [1..] -- List comprehension (generators). [x | x <- [..10], odd? x] [(x, y) | x <- [..5], y <- "abcde"] [(x, y) | x <- "abc" | y <- "def"] -- Equivalent to `zip "abc" "def"`. [x | let k = 5, x <- map (*2) [..k]] -- Generator-local declaration. -- 'do'-expression. do print "Hello!"; 5+5; 'a' -- 'let'-expression (expression-local declarations). map (\x -> let x' = random 0 x in x') [..10] -- 'if'-expression. if x <= 0 then 0 else x -- 'caseof'-expression (syntactic sugar for `\((h:t) -> h | [] -> error "empty") x)`. case x of (h:t) -> h | [] -> error "empty" -- Using function as an infix operator. (* 2) `map` [..5] + Function declaration: -- Type signature declaration. f :: num -> num -> num f x y = x * y -- Function clause declaration. f :: num => num -> num -- Syntactic sugar for `(num, num) -> num`. -- Multiple clauses declaration. fac 0 = 1 fac n = n * fac (n-1) -- Guarded clause declaration. fac n | n == 0 = 1 | otherwise = n * fac (n-1) -- Polymorphic function. left :: a -> b -> a left l _ = l -- Clause-local declarations using 'where'. f n xs = r ++ l where (l, r) = splitAt n xs + Patterns: -- List deconstruction. f (x:xs) = (x, xs) -- Algrebraic data type deconstruction. f (Com x _) = x -- Tuple deconstruction. f ((x, _), _) = x -- Alias. f (l@x:xs) = x:xs:l + Operator declaration: -- Operator declaration. infixr 3 *! (x *! y) = [x | _ <- [..y]] + Algrebraic data type (ADT) declaration: data tree a = Leaf a | Tree (tree a) (tree a) -- Record. type point = {x :: num, y :: num} -- Type synonym. type pair a b = (a, b) + Including other source files: import utils import "../utils" import "txlyre/mbase64" -- Load package installed via MPM. import "lib.mo" -- Loading MO library (see `Motools`). ### Examples **Note**: these example are intended to be executed by the latest version of interpreter. #### Hello World ``` main = print "Hello, World!" ``` #### Sierpinski triangle ``` sierpinski :: int -> strings sierpinski n = foldl aggregate ["*"] [0..n-1] where aggregate t i = [sp ++ x ++ sp | x <- t] ++ [x ++ " " ++ x | x <- t] where sp = replicate (2 ** i) ' ' main = doEach print $ sierpinski 4 ``` #### Mandelbrot ``` mandelbrot :: strings mandelbrot = [[mandelbrot' x y 0 0 0 | x <- range (-2.1) 1.1 0.04] | y <- range (-1.3) 1.3 0.1] where mandelbrot' x y zr zi i = if i >= 100 || zi*zi+zr*zr >= 4 then " .:-=+*#%@" ! (i%10) else mandelbrot' x y (zr*zr-zi*zi+x) (2*zr*zi+y) (i+1) main = doEach putStrLn mandelbrot ``` #### Conway's Game of Life ``` type life_board = [(int, int)] lifeStep :: life_board -> life_board lifeStep board = [head g | g <- group $ sort $ concatMap neighbors board, viable? g] where neighbors (x, y) = [(x+dx, y+dy) | dx <- [-1..1], dy <- [-1..1], (dx, dy) != (0, 0)] viable? [_, _, _] = True viable? [cell, _] = board ? cell viable? _ = False printBoard :: life_board -> none printBoard board = doEach print [[showCell (x, y) | x <- [l..g]] | y <- [l..g]] where showCell cell = if board ? cell then '#' else '.' (l, g) = (minimum x y, maximum x' y') where (x, y) = head board (x', y') = last board main = iterate lifeStep glider |> take 10 |> doEach print' where print' board = do printBoard board; putChar '\n' glider = [(1, 0), (2, 1), (0, 2), (1, 2), (2, 2)] ``` #### Brainfuck interprerer ``` data bf_state = BFState int [int] bfNew :: bf_state bfNew = BFState 0 (replicate 30_000 0) bfChangeCell :: bf_state -> (int -> int) -> bf_state bfChangeCell (BFState pointer memory) f = BFState pointer (change memory pointer $ f $ memory ! pointer) bfGetCell :: bf_state -> int bfGetCell (BFState pointer memory) = memory ! pointer bfGoRight :: bf_state -> bf_state bfGoRight (BFState pointer memory) | pointer >= 30_000 = error "bfGoRight: can't go further to the right" | otherwise = BFState (pointer + 1) memory bfGoLeft :: bf_state -> bf_state bfGoLeft (BFState pointer memory) | pointer == 0 = error "bfGoLeft: can't go further to the left" | otherwise = BFState (pointer - 1) memory bfExtractLoop :: string -> int -> string bfExtractLoop _ 0 = "" bfExtractLoop "" _ = error "bfExtractLoop: missing ]" bfExtractLoop (c : cs) level = c : bfExtractLoop cs level' where level' = case c of | '[' -> level + 1 | ']' -> level - 1 | _ -> level bfRunLoop :: string -> bf_state -> bf_state bfRunLoop code state = if bfGetCell state == 0 then state else bfRunLoop code $ bfRun state code bfRun :: bf_state -> string -> bf_state bfRun state "" = state bfRun state (c : cs) = case c of | '+' -> bfRun (bfChangeCell state succ) cs | '-' -> bfRun (bfChangeCell state pred) cs | '>' -> bfRun (bfGoRight state) cs | '<' -> bfRun (bfGoLeft state) cs | '.' -> do putChar $ fromCharCode $ bfGetCell state; bfRun state cs | ',' -> bfRun (bfChangeCell state (\_ -> charCode? getChar)) cs | '[' -> bfRun (bfRunLoop loop' state) $ drop (succ $ length loop') cs | _ -> bfRun state cs where loop' = init $ bfExtractLoop cs 1 main = bfRun bfNew getLines ``` #### HTTP GET request ``` main = getRequest "https://api.ipify.org" |> explode B"\r\n" |> last |> bytesToString |> print ``` #### Dead simple HTTP server ``` response = "Hello World!" main = forever listen $ makeSocketServer False 8080 where listen s = respond $ acceptSocketConnection s respond s = do print $ "< " ++ (first $ socketGetAddress s); writeSocket s B"HTTP/1.1 200 OK\r\n"; writeSocket s B"Content-Type: text/html\r\n"; writeSocket s $ B"Content-length: " ++ (stringToBytes $ show $ length? response') ++ B"\r\n"; writeSocket s B"\r\n"; writeSocket s response'; closeSocket s response' = stringToBytes response ``` #### Regular expressions **Note**: Mei uses a custom regular expression engine. Check the end of this document for details. ``` main = do if s ~= "[[:o:]][[:w:]]*" then print "This is a valid identifier." else print "This is not a valid identifier."; print $ "Numbers: " ++ (intercalate ", " $ s ~~ R"(\d+)"); print $ "Splitted by punctuation: " ++ (intercalate " " $ s ~/ "[[:p:]]"); print $ "Make all numbers negative: " ++ (rewrite R"(? getRequest |> bytesToString |> explode "\r\n\r\n" |> last |> jsonRead |> validate where encodeQS qs = "?" ++ (intercalate "&" $ encodeQS' qs []) encodeQS' [] b = b encodeQS' ((k, v):qss) b = encodeQS' qss $ b <: (k # "=" # v) validate (JObject [("ok", JBool True), resp]) = resp validate (JObject [("ok", JBool False), ("error_code", JNum code), ("description", JString desc)]) = error $ "telegramAPIRequest: API error (code " # code # "): " # desc validate _ = error "telegramAPIRequest: unknown error" main = while (const True) ( id -> processUpdates $ telegramAPIRequest token "getUpdates" [("offset", id)] ) 0 where processUpdates ("result", JArray updates) = do doEach processUpdate updates; if empty? updates then 0 else updates |> last |> (\JObject a -> a) |> lookup "update_id" |> (\Just (JNum x) -> x) |> succ processUpdate (JObject update) = case update of [("update_id", _), ("message", JObject message)] -> processMessage message | _ -> None processMessage message = case getMessageText message of "/ping" -> replyTo message "pong" | _ -> None replyTo message text = telegramAPIRequest token "sendMessage" [ ("chat_id", showNum $ getMessageChatId message), ("reply_to_message_id", showNum $ getMessageId message), ("text", text) ] |> return getMessageChatId message = lookup "chat" message |> (\Just (JObject a) -> a) |> lookup "id" |> (\Just (JNum chat_id) -> chat_id) getMessageId message = lookup "message_id" message |> (\Just (JNum message_id) -> message_id) getMessageText message = lookup "text" message |> (\Just (JString s) -> s) token = getEnv "BOT_TOKEN" ``` ### Builtins (this section is WIP) #### (+) :: num -> num -> num (left 4) #### (-) :: num -> num -> num (left 4) #### (\*) :: num -> num -> num (left 3) #### (/) :: num -> num -> num (left 3) #### (//) :: num -> num -> num (left 3) #### (\*\*) :: num -> int -> num (right 2) #### (%) :: num -> num -> num (left 3) #### (==) :: a -> a -> bool (right 5) #### (!=) :: a -> a -> bool (left 5) #### (<=) :: a -> a -> bool (left 5) #### (>=) :: a -> a -> bool (left 5) #### (<) :: a -> a -> bool (left 5) #### (>) :: a -> a -> bool (left 5) #### not :: bool -> bool #### (&&) :: bool -> bool -> bool (right 6) #### (||) :: bool -> bool -> bool (right 7) #### (~) :: int -> int #### band :: int -> int -> int (right 7) #### bor :: int -> int -> int (right 7) #### xor :: int -> int -> int (right 7) #### << :: int -> int -> int (right 7) #### >> :: int -> int -> int (right 7) #### ($) :: (a -> b) -> a -> b (right 10) #### (|>) :: a -> (a -> b) -> b (left 10) #### (.) :: (b -> c) -> (a -> b) -> a -> c (right 9) #### (++) :: [a] -> [a] -> [a] (right 4) #### (:) :: a -> [a] -> [a] (right 1) #### (<:) :: [a] -> a -> [a] (left 1) #### (!) :: [a] -> int -> a (left 2) (raises error) + Returns Nth element of the list. #### (?) :: [a] -> a -> bool (left 2) + Returns **True** if the list containts an element, **False** otherwise. #### (~=) :: string -> string -> bool (right 5) (raises error) + Returns **True** if the string matches a regular expression, **False** otherwise. #### (~~) :: string -> string -> strings (right 5) (raises error) + Extracts every match of a regular expression in the string. #### charCode? :: char -> int #### fromCharCode :: int -> char (raises error) #### error :: string -> a #### try :: a -> result a #### ok? :: result a -> bool #### error? :: result a -> bool #### fromResult :: a -> result a -> a #### length? :: [a] -> int #### head :: [a] -> a (raises error) #### tail :: [a] -> [a] (raises error) #### init :: [a] -> [a] (raises error) #### last :: [a] -> a (raises error) #### take :: int -> [a] -> [a] #### drop :: int -> [a] -> [a] #### until :: (a -> bool) -> (a -> a) -> a -> a #### reduce :: (a -> a -> a) -> [a] -> a (raises error) #### scan :: (a -> a -> a) -> [a] -> [a] #### map :: (a -> b) -> [a] -> [b] #### filter :: (a -> bool) -> [a] -> [a] #### print :: a -> none #### put :: a -> none #### show :: a -> string + Returns a string representation of the given value. #### (#) :: a -> b -> string (right 5) #### readFile :: string -> string (raises error) #### writeFile :: string -> string -> none (raises error) #### readBinaryFile :: string -> bytes (raises error) #### writeBinaryFile :: string -> bytes -> none (raises error) #### appendFile :: string -> string -> none (raises error) #### appendBinaryFile :: string -> bytes -> none (raises error) #### fileExists? :: string -> bool #### isFile? :: string -> bool #### isDirectory? :: string -> bool #### fileSize? :: string -> int (raises error) #### listDirectory :: string -> strings (raises error) #### removeDirectory :: string -> bool #### createDirectory :: string -> int -> bool #### getCurrentTime :: num #### formatTime :: string -> int -> string (raises error) #### executeSystem :: string -> (bytes, int) (raises error) #### exec :: string -> string (raises error) #### getCurrentDirectory :: string #### setCurrentDirectory :: string -> bool #### getEnv :: string -> string (raises error) #### setEnv :: string -> string -> bool #### getProcessID :: int #### getParentProcessID :: int #### killProcess :: int -> int -> bool #### forkProcess :: int (raises error) #### waitProcess :: int -> int -> (int, int) #### gather :: [a] -> none (raises error) #### executeFile :: string -> bool -> strings -> strings -> none (raises error) #### makePipe :: int (raises error) #### readPipe :: int -> int -> bytes (raises error) #### writePipe :: int -> bytes -> none (raises error) #### closePipe :: int -> none (raises error) #### makeSocketServer :: bool -> int -> int (raises error) #### makeSocketClient :: bool -> string -> int -> int (raises error) #### acceptSocketConnection :: int -> int (raises error) #### readSocket :: int -> int -> bytes (raises error) #### writeSocket :: int -> bytes -> none (raises error) #### closeSocket :: int -> none (raises error) #### socketIsServer :: int -> bool (raises error) #### socketIsClosed :: int -> bool (raises error) #### resolveAddress :: string -> strings (raises error) #### httpRequest :: string -> int -> bool -> string -> bytes (raises error) #### getRequest :: string -> bytes (raises error) #### postRequest :: string -> string -> string -> bytes (raises error) #### zCompress :: bytes -> bytes (raises error) #### zDecompress :: bytes -> bytes (raises error) #### getLine :: string #### getLines :: string #### getChar :: char (raises error) #### getBytes :: bytes #### getByte :: byte (raises error) #### open :: string -> int -> int -> int (raises error) #### read :: int -> int -> bytes (raises error) #### write :: int -> bytes -> int (raises error) #### close :: int -> bool #### sync :: int -> bool #### seek :: int -> int -> int -> int #### quit :: int -> a #### putChar :: char -> none #### putByte :: byte -> none #### putBytes :: bytes -> none ## Regular expressions syntax ### Metacharacters ``` . matches any single character. \ escapes the next character. ^ matches the beginning of line. $ matches the end of line. | alternation. ``` ### Brackets ``` () grouping. [] character class. [a-z] character range. (?mis) sets flags: m - multiline, i - case insensitive, s - stretchy. [:...:] special character class. (?^...) negative grouping. (?=...) positive lookahead. (?!...) negative lookahead. (?<=...) positive lookbehind. (?...) atomic grouping. (?0..9) calls subgroup. (?<...>) calls named subgroup. (?N:...) refers named subgroup. (?N<...>) named grouping. (?:...) subpattern. (?|...) resetting grouping. ``` ### Special character classes: ``` [:x:] [0-9A-Fa-f] [:d:] [0-9] [:s:] [ \t\n\r\f\v] [:N:] [^\n] [:a:] [A-Za-z] [:o:] [A-Za-z_] [:w:] [A-Za-z0-9_] [:l:] [a-z] [:u:] [A-Z] [:p:] [-!"#$%&'()*+,./:;<=>?@[\\\]^_`{|}~] ``` ### Escapes ``` \x... matches character with the specified hexadecimal code. \o... matches character with the specified octal code. \d matches any digit. \D matches any non-digit. \s matches any space character. \S matches any non-space character. \w matches any alphanumeric character. \p{...} matches any character within the specified Unicode property. \N{...} matches a character by its Unicode name. \0..9 backref. \K sets match start. \A matches the beginning of the subject. \Z matches the end of the subject. \b matches at word boundary. \B matches at not word boundary. \Q starts literal. \q terminates literal. ``` ### Quantifiers ``` * match 0 or more times. + match 1 or more times. ? match 0 or 1 times. {n} match n times. {n,m} match n to m times. {n,} match at least n times. {,n} match at most n times. ```