├── .gitignore ├── specification ├── typing.pdf ├── Makefile ├── language.md └── typing.tex ├── docs ├── Makefile ├── index.ja.adoc └── index.adoc ├── embed_macro ├── README.md ├── Cargo.toml ├── LICENSE └── src │ └── lib.rs ├── tests ├── transpile.rs └── embedded.rs ├── Cargo.toml ├── LICENSE ├── src ├── prelude.lisp ├── macro.rs ├── lib.rs ├── coq.rs ├── parser.rs └── runtime.rs ├── README.md └── history.md /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /specification/typing.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakano/blisp/HEAD/specification/typing.pdf -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | all: index.html index.ja.html 2 | 3 | index.html: index.adoc 4 | asciidoctor index.adoc 5 | 6 | index.ja.html: index.ja.adoc 7 | asciidoctor index.ja.adoc 8 | -------------------------------------------------------------------------------- /specification/Makefile: -------------------------------------------------------------------------------- 1 | all: typing.pdf 2 | 3 | typing.dvi: typing.tex 4 | platex typing.tex 5 | platex typing.tex 6 | 7 | typing.pdf: typing.dvi 8 | dvipdfmx -p letter typing.dvi 9 | 10 | clean: 11 | rm -f *.dvi *.log *.aux *.pdf -------------------------------------------------------------------------------- /embed_macro/README.md: -------------------------------------------------------------------------------- 1 | # `embedded` macro for BLisp 2 | 3 | Please see [blisp-repl](https://github.com/ytakano/blisp-repl) to use BLisp, 4 | and [baremetalisp](https://github.com/ytakano/baremetalisp) which is a toy OS. 5 | 6 | **[Homepage](https://ytakano.github.io/blisp/) is here.** 7 | -------------------------------------------------------------------------------- /tests/transpile.rs: -------------------------------------------------------------------------------- 1 | use blisp; 2 | 3 | #[test] 4 | fn test_transpile() { 5 | let expr = " 6 | (defun snoc (l y) 7 | (Pure (-> ( 8 | '(t) t) 9 | '(t))) 10 | (match l 11 | (nil (Cons y nil)) 12 | ((Cons h b) (Cons h (snoc b y))))) 13 | 14 | (defun rev (l) 15 | (Pure (-> ( 16 | '(t)) 17 | '(t))) 18 | (match l 19 | (nil nil) 20 | ((Cons h t) (snoc (rev t) h)))) 21 | "; 22 | let exprs = blisp::init(expr, vec![]).unwrap(); 23 | let ctx = blisp::typing(exprs).unwrap(); 24 | 25 | println!("{}", blisp::transpile(&ctx)); 26 | } 27 | -------------------------------------------------------------------------------- /embed_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "blisp_embedded" 3 | version = "0.1.1" 4 | authors = ["Yuuki Takano ", "Fumiya Saito"] 5 | edition = "2021" 6 | description = "embedded macro for BLisp" 7 | repository = "https://github.com/ytakano/blisp" 8 | keywords = [ 9 | "no_std", 10 | "scripting", 11 | "scripting-engine", 12 | "scripting-language", 13 | "embedded", 14 | ] 15 | categories = ["no-std", "embedded"] 16 | license-file = "LICENSE" 17 | readme = "README.md" 18 | homepage = "https://ytakano.github.io/blisp/" 19 | 20 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 21 | 22 | [lib] 23 | proc-macro = true 24 | 25 | [dependencies] 26 | syn = { version = "2.0", features = ["full"] } 27 | quote = "1.0" 28 | proc-macro2 = "1.0" 29 | convert_case = "0.7" 30 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "blisp" 3 | version = "0.4.6" 4 | authors = ["Yuuki Takano ", "Fumiya Saito"] 5 | edition = "2021" 6 | description = "A lisp like statically typed programing language for no_std." 7 | repository = "https://github.com/ytakano/blisp" 8 | keywords = [ 9 | "no_std", 10 | "scripting", 11 | "scripting-engine", 12 | "scripting-language", 13 | "embedded", 14 | ] 15 | categories = ["no-std", "embedded"] 16 | license-file = "LICENSE" 17 | readme = "README.md" 18 | homepage = "https://ytakano.github.io/blisp/" 19 | 20 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 21 | 22 | [dependencies] 23 | blisp_embedded = "0.1" 24 | 25 | [dependencies.num-bigint] 26 | version = "0.4" 27 | default-features = false 28 | 29 | [dependencies.num-traits] 30 | version = "0.2" 31 | default-features = false 32 | features = ["libm"] 33 | 34 | [lib] 35 | crate-type = ["rlib"] 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Yuuki Takano , Fumiya Saito 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /embed_macro/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Yuuki Takano , Fumiya Saito 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/prelude.lisp: -------------------------------------------------------------------------------- 1 | (data (Option t) 2 | (Some t) 3 | None) 4 | 5 | (data (Result t e) 6 | (Ok t) 7 | (Err e)) 8 | 9 | (export car (x) (Pure (-> ('(t)) (Option t))) 10 | (match x 11 | ((Cons n _) (Some n)) 12 | (_ None))) 13 | 14 | (export cdr (x) (Pure (-> ('(t)) '(t))) 15 | (match x 16 | ((Cons _ l) l) 17 | (_ '()))) 18 | 19 | (export map (f x) (Pure (-> ((Pure (-> (a) b)) '(a)) '(b))) 20 | (match x 21 | ((Cons h l) (Cons (f h) (map f l))) 22 | (_ '()))) 23 | 24 | (export fold (f init x) (Pure (-> ((Pure (-> (a b) b)) b '(a)) b)) 25 | (match x 26 | ((Cons h l) (fold f (f h init) l)) 27 | (_ init))) 28 | 29 | (export filter (f x) 30 | (Pure (-> 31 | ((Pure (-> (t) Bool)) '(t)) 32 | '(t))) 33 | (reverse (filter' f x '()))) 34 | 35 | (defun filter' (f x l) 36 | (Pure (-> ( 37 | (Pure (-> (t) Bool)) '(t) '(t)) 38 | '(t))) 39 | (match x 40 | ((Cons h a) (if (f h) (filter' f a (Cons h l)) (filter' f a l) )) 41 | (_ l))) 42 | 43 | (export reverse (x) (Pure (-> ('(t)) '(t))) 44 | (reverse' x '())) 45 | 46 | (defun reverse' (x l) (Pure (-> ('(t) '(t)) '(t))) 47 | (match x 48 | ((Cons h a) (reverse' a (Cons h l))) 49 | (_ l))) 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BLisp 2 | 3 | BLisp is a statically typed Lisp like programming language which adopts effect system for no_std environments. 4 | BLisp supports higher order RPC like higher order functions of functional programming languages. 5 | 6 | This repository provides only a library crate. 7 | Please see [blisp-repl](https://github.com/ytakano/blisp-repl) to use BLisp, 8 | and [baremetalisp](https://github.com/ytakano/baremetalisp) which is a toy OS. 9 | 10 | **[Homepage](https://ytakano.github.io/blisp/) is here.** 11 | 12 | ## Features 13 | 14 | - Algebraic data type 15 | - Generics 16 | - Hindley–Milner based type inference 17 | - Effect system to separate side effects from pure functions 18 | - Big integer 19 | - Supporting no_std environments 20 | 21 | ## How to Use 22 | 23 | ```rust 24 | use blisp; 25 | 26 | fn main() { 27 | let code = "(export factorial (n) (Pure (-> (Int) Int)) 28 | (if (<= n 0) 29 | 1 30 | (* n (factorial (- n 1)))))"; 31 | let exprs = blisp::init(code, vec![]).unwrap(); 32 | let ctx = blisp::typing(exprs).unwrap(); 33 | 34 | let e = "(factorial 10)"; 35 | blisp::eval(e, &ctx).unwrap(); 36 | } 37 | ``` 38 | 39 | If Rust compiler or linker says warning of fmod, 40 | please add fmod manually as follows. 41 | 42 | ```rust 43 | #[no_mangle] 44 | extern "C" fn fmod(x: f64, y: f64) -> f64 { 45 | libm::fmod(x, y) 46 | } 47 | ``` 48 | 49 | Cargo.toml 50 | 51 | ```toml 52 | [dependencies.blisp] 53 | version = "0.4" 54 | ``` 55 | 56 | ## Examples 57 | 58 | ```lisp 59 | "Hello, World!" ; "Hello, World!" 60 | (+ 0x10 0x20) ; 48 61 | (+ 0b111 0b101) ; 12 62 | (+ 0o777 0o444) ; 803 63 | (car '(1 2 3)) ; (Some 1) 64 | (cdr '(1 2 3)) ; '(2 3) 65 | (map (lambda (x) (* x 2)) '(8 9 10)) ; '(16 18 20) 66 | (fold + 0 '(1 2 3 4 5 6 7 8 9)) ; 45 67 | (reverse '(1 2 3 4 5 6 7 8 9)) ; '(9 8 7 6 5 4 3 2 1) 68 | (filter (lambda (x) (= (% x 2) 0)) '(1 2 3 4 5 6 7 8 9)) ; '(2 4 6 8) 69 | ``` 70 | -------------------------------------------------------------------------------- /tests/embedded.rs: -------------------------------------------------------------------------------- 1 | use blisp::embedded; 2 | use num_bigint::{BigInt, ToBigInt}; 3 | 4 | #[embedded] 5 | fn test_fun( 6 | _z: BigInt, 7 | _a: Vec, 8 | _b: (BigInt, BigInt), 9 | _c: Option, 10 | _d: Result, 11 | ) -> Option { 12 | let temp = 5.to_bigint(); 13 | temp 14 | } 15 | 16 | #[embedded] 17 | fn add_four_ints(a: BigInt, b: (BigInt, BigInt), c: Option) -> Result { 18 | let mut result = a + b.0 + b.1; 19 | if let Some(n) = c { 20 | result += n; 21 | } 22 | 23 | Ok(result) 24 | } 25 | 26 | #[embedded] 27 | fn no_return() {} 28 | 29 | #[test] 30 | fn test_embedded() { 31 | // test_fun 32 | let code = "(export call_test_fun () 33 | (IO (-> () (Option Int))) 34 | (test_fun 1 '(2 3) [4 5] (Some 6) (Ok 7)) 35 | )"; 36 | let exprs = blisp::init(code, vec![Box::new(TestFun)]).unwrap(); 37 | let ctx = blisp::typing(exprs).unwrap(); 38 | let result = blisp::eval("(call_test_fun)", &ctx).unwrap(); 39 | 40 | let front = result.front().unwrap().as_ref().unwrap(); 41 | assert_eq!(front, "(Some 5)"); 42 | 43 | // add_for_ints 44 | let code = "(export call_add_four_ints (n) 45 | (IO (-> ((Option Int)) (Result Int String))) 46 | (add_four_ints 1 [2 3] n) 47 | )"; 48 | let exprs = blisp::init(code, vec![Box::new(AddFourInts)]).unwrap(); 49 | let ctx = blisp::typing(exprs).unwrap(); 50 | let result = blisp::eval("(call_add_four_ints (Some 4))", &ctx).unwrap(); 51 | 52 | let front = result.front().unwrap().as_ref().unwrap(); 53 | assert_eq!(front, "(Ok 10)"); 54 | 55 | // no_return 56 | let code = "(export call_no_return () 57 | (IO (-> () [])) 58 | (no_return) 59 | )"; 60 | let exprs = blisp::init(code, vec![Box::new(NoReturn)]).unwrap(); 61 | let ctx = blisp::typing(exprs).unwrap(); 62 | let result = blisp::eval("(call_no_return)", &ctx).unwrap(); 63 | result.front().unwrap().as_ref().unwrap(); 64 | } 65 | -------------------------------------------------------------------------------- /history.md: -------------------------------------------------------------------------------- 1 | # Version History 2 | 3 | ## 0.4.0 4 | 5 | - add `embedded` macro for FFIs 6 | - add transpiler to Coq 7 | 8 | ## 0.3.9 9 | 10 | - fix a bug on the garbage collector 11 | 12 | ## 0.3.8 13 | 14 | - fix a bug on typing 15 | - check the number of arguments 16 | - (+ 10) was passed typing rule 17 | - add != and neq 18 | - (!= 10 10) 19 | - (neq (Some "Hello") 10) 20 | 21 | ## 0.3.7 22 | 23 | - add bit shift operators 24 | - (<< 8 4) ; shift left 25 | - (>> 128 4) ; shift right 26 | - add Char type 27 | - add character literal 28 | - \`H\` 29 | - add chars and str functions 30 | - chars converts String to (List Char) 31 | - (chars "Hello, World!") 32 | - str converts (List Char) to String 33 | - (str '(\`H\` \`e\` \`l\` \`l\` \`o\`)) 34 | 35 | ## 0.3.6 36 | 37 | - make <, >, <=, >= functions' type (Pure (-> (t t) Bool)) 38 | - perform comparison between 2 values whose types are same 39 | - (< "Hello" "World") 40 | - (>= (Some 5) (Some 19)) 41 | - add lt, gt, leq, geq functions 42 | - perform comparison between any 2 values 43 | - function type is (Pure (-> (t1 t2) Bool)) 44 | - (eq "Hello" 10) 45 | - (lt (Some 6) "Hello") 46 | 47 | ## 0.3.5 48 | 49 | - add string type and literal 50 | - String 51 | - "Hello World!" 52 | - make equal function generics 53 | - it can be used for non-integer types 54 | - (= "Hello" "Hello") 55 | - (= (Some 1) (Some 2)) 56 | - fix a bug on typing 57 | - (= (Some 1) (Some 2)) could not be typed properly 58 | 59 | ## 0.3.4 60 | 61 | - fix bugs on typing 62 | - bug 1: some locally defined functions are cannot be called 63 | - bug 2: empty list cannot be typed properly 64 | - add filter and reverse functions to prelude 65 | 66 | ## 0.3.3 67 | 68 | - add hexadecimal, octal, and binary 69 | - 0xabcDEF 70 | - 0o777 71 | - 0b1010 72 | 73 | ## 0.3.2 74 | 75 | - add pow to compute exponent 76 | - example: (pow 10 20) 77 | - type of pow: (Pure (-> (Int Int) (Option Int))) 78 | - if the exponent portion is greater or equal to 2^32, then return None 79 | - add sqrt 80 | - example: (sqrt 16) 81 | - type of sqrt: (Pure (-> (Int) (Option Int))) 82 | - if the value is less than 0, then return None 83 | - add bitwise operations 84 | - band, bor, bxor 85 | 86 | ## 0.3.1 87 | 88 | - garbage collection is ready (mark and sweep) 89 | -------------------------------------------------------------------------------- /specification/language.md: -------------------------------------------------------------------------------- 1 | # Syntax and Semantics of BLisp 2 | 3 | ## Literal 4 | 5 | - $LITERAL := $HEX | $OCT | $BIN | $DECIMAL | $BOOL | $STRING | $CHAR 6 | - $DECIMAL 7 | - decimal number 8 | - examples: 0, 100, 224, -130, 4457, 0007 9 | - $HEX 10 | - hexadecimal number 11 | - examples: 0xabcd 0xABCD 12 | - $BIN 13 | - binary number 14 | - examples: 0b1100, 0b01011 15 | - $BOOL := true | false 16 | - $STRING 17 | - string literal 18 | - example: "Hello, World!" 19 | - escape sequences 20 | - \r 21 | - \n 22 | - \t 23 | - \0 24 | - \\\\ 25 | - \\" 26 | - $CHAR 27 | - character literal 28 | - example: \`H\` 29 | - escape sequences 30 | - \r 31 | - \n 32 | - \t 33 | - \0 34 | - \\\\ 35 | - \\\` 36 | 37 | ## Identifier 38 | 39 | - $ID 40 | - a string whose first character is not capital (not 'A' to 'Z') 41 | - excludes "true" and "false" 42 | 43 | ## Type Identifier 44 | 45 | - $TID 46 | - a string whose first character is capital ('A' to 'Z') 47 | 48 | ## Type 49 | 50 | - $TYPE := Int | Bool | String | Char | $TYPE_LIST | $TYPE_TUPLE | $TYPE_FUN | $TYPE_DATA | $ID 51 | - $TYPE_LIST := '( $TYPE ) 52 | - $TYPE_TUPLE := \[ $TYPE* \] 53 | - $TYPE_DATA := $TID | ( $TID $TYPE* ) 54 | - $TYPE_FUN := ( $EFFECT $TYPE_ARROW ) 55 | - $TYPE_ARROW := ( -> $TYPES $TYPE ) 56 | - $TYPES := ( $TYPE* ) 57 | - $EFFECT := Pure | IO 58 | 59 | examples: 60 | 61 | ```common-lisp 62 | '(Int) 63 | [Int Bool] 64 | (Pure (-> (Int INT) Bool)) 65 | '('(Int Bool)) 66 | [Int Int '([Int Bool])] 67 | ``` 68 | 69 | ## Data Type 70 | 71 | - $DATA := ( data $DATA_NAME $MEMBER* ) 72 | - $DATA_NAME := $TID | ( $TID $ID* ) 73 | - $MEMBER := $TID | ( $TID $TYPE* ) 74 | 75 | examples: 76 | 77 | ```common-lisp 78 | (data Dim2 79 | (Dim2 Int Int)) 80 | 81 | (data (Maybe t) 82 | (Just t) 83 | Nothing) 84 | 85 | (data (Tree t) 86 | (Node (Tree t) (Tree t)) 87 | Leaf) 88 | ``` 89 | 90 | ## Function Definition 91 | 92 | - $DEFUN := ( $HEAD_DEFUN $ID ( $ID* ) $TYPE_FUN $EXPR ) 93 | - $HEAD_DEFUN := export | defun 94 | 95 | example: 96 | 97 | ```common-lisp 98 | (defun add (x y) (Pure (-> (Int Int) Int)) 99 | (+ x y)) 100 | ``` 101 | 102 | ## External Function 103 | 104 | - $EXTERN := ( extern $ID $TYPE_ARROW ) 105 | 106 | ## Expression 107 | 108 | - $EXPR := $LITERAL | $ID | $TID | $LET | $IF | $LAMBDA | $MATCH | $LIST | $TUPLE | $GENDATA | $APPLY 109 | 110 | ### Let Expression 111 | 112 | - $LET := ( let ( $DEFVAR+ ) $EXPR ) 113 | - $DEFVAR := ( $LETPAT $EXPR ) 114 | - $LETPAT := $ID | [ $LETPAT+ ] | ($TID $LETPAT+ ) 115 | 116 | ### If Expression 117 | 118 | - $IF := ( if $EXPR $EXPR $EXPR ) 119 | 120 | ### List Expression 121 | 122 | - $LIST := '( $EXPR* ) 123 | 124 | ### Tuple Expression 125 | 126 | - $TUPLE := [ $EXPR* ] 127 | 128 | ### Match Expression 129 | 130 | - $MATCH := ( match $EXPR $CASE+ ) 131 | - $CASE := ( $PATTERN $EXPR ) 132 | - $PATTERN := $LITERAL | $ID | $TID | \[ $PATTERN+ \] | ( $TID $PATTERN* ) | '() 133 | 134 | ### Function Application 135 | 136 | - $APPLY := ( $EXPR+ ) 137 | 138 | ### Data Creataion 139 | 140 | - $GENDATA := ( $TID $EXPR* ) 141 | 142 | ### Lambda 143 | 144 | - $LAMBDA := (lambda ($ID*) $EXPR) 145 | 146 | ## Built-in Functions 147 | 148 | - +, -, *, /, %: (Pure (-> (Int Int) Int)) 149 | - band, bor, bxor: (Pure (-> (Int Int) Int)) 150 | - pow, <<, >>: (Pure (-> (Int Int) (Some Int))) 151 | - sqrt: (Pure (-> (Int) (Some Int))) 152 | - <, >, <=, >=, =: (Pure (-> (t t) Bool)) 153 | - lt, gt, leq, geq, eq: (Pure (-> (t1 t2) Bool)) 154 | - and, or, xor: (Pure (-> (Bool Bool) Bool)) 155 | - not: (Pure (-> (Bool) Bool)) 156 | - chars: (Pure (-> (String) (List Char))) 157 | - str: (Pure (-> ((List Char)) String)) 158 | 159 | ## Macro 160 | 161 | - $MACRO := ( macro $ID $MACRO_RULE+ ) 162 | - $MACRO_RULE := ( ( $EXPR* ) ( $EXPR* ) ) 163 | 164 | ```common-lisp 165 | (macro add 166 | ((add $e1 $e2) (+ $e1 $e2)) 167 | ((_ $e1 $e2 $e3 ...) (+ $e1 (add $e2 $e3 ...)))) 168 | ``` 169 | -------------------------------------------------------------------------------- /embed_macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | use convert_case::{Case, Casing}; 4 | use proc_macro2::{Ident, TokenStream}; 5 | use quote::quote; 6 | use syn::{parse_macro_input, FnArg, GenericArgument, Item, PathArguments, Signature, Type}; 7 | 8 | #[proc_macro_attribute] 9 | pub fn embedded( 10 | _args: proc_macro::TokenStream, 11 | input: proc_macro::TokenStream, 12 | ) -> proc_macro::TokenStream { 13 | let mut out = input.clone(); 14 | 15 | let ty = parse_macro_input!(input as Item); 16 | let item_fn = match ty { 17 | Item::Fn(ref n) => n, 18 | _ => panic!("only function is allowed"), 19 | }; 20 | 21 | let fn_name = &item_fn.sig.ident.clone(); 22 | 23 | let fn_name_camel = { 24 | let mut temp = format!("{}", fn_name); 25 | temp = temp.to_case(Case::Pascal); 26 | Ident::new(&temp, Ident::span(fn_name)) 27 | }; 28 | 29 | // Generate BLisp. 30 | let fn_data = &item_fn.sig; 31 | let inputs_parse = inputs_type(fn_data); 32 | let output_ty = output_type(fn_data); 33 | 34 | let fn_body = format!("(extern {} (-> ({}) {}))", fn_name, inputs_parse, output_ty); 35 | 36 | // Generate FFI 37 | let fn_name_ffi = { 38 | let temp = format!("{fn_name}_ffi"); 39 | Ident::new(&temp, Ident::span(fn_name)) 40 | }; 41 | 42 | let fn_name_str = format!("{fn_name}"); 43 | 44 | let ffi_body = generate_ffi_body(fn_data, &fn_name, &fn_name_ffi); 45 | 46 | let expanded = quote! { 47 | struct #fn_name_camel; 48 | 49 | impl blisp::runtime::FFI for #fn_name_camel { 50 | fn blisp_extern(&self) -> &'static str { #fn_body } 51 | 52 | fn ffi(&self) -> fn(&mut blisp::runtime::Environment<'_>, &[blisp::runtime::RTData]) -> blisp::runtime::RTData { 53 | use blisp::runtime::{Environment, RTData, RTDataToRust, RustToRTData}; 54 | fn ffi_inner(env: &mut Environment<'_>, args: &[RTData]) ->RTData { 55 | #ffi_body 56 | } 57 | ffi_inner 58 | } 59 | 60 | fn name(&self) -> &'static str { 61 | #fn_name_str 62 | } 63 | } 64 | }; 65 | 66 | out.extend(proc_macro::TokenStream::from(expanded)); 67 | out 68 | } 69 | 70 | fn generate_ffi_body(data: &Signature, fn_name: &Ident, fn_name_ffi: &Ident) -> TokenStream { 71 | let mut body = quote! {}; 72 | for (i, arg) in data.inputs.iter().enumerate() { 73 | let arg_type = match arg { 74 | FnArg::Typed(pat) => &*pat.ty, 75 | _ => panic!("Need an explicitly typed input pattern "), 76 | }; 77 | 78 | let arg_dst = { 79 | let temp = format!("arg{i}"); 80 | Ident::new(&temp, Ident::span(fn_name_ffi)) 81 | }; 82 | 83 | let arg_src = { 84 | quote! { 85 | &args[#i] 86 | } 87 | }; 88 | 89 | let casting = typecast(arg_type, arg_dst, arg_src); 90 | 91 | body = quote! { 92 | #body 93 | #casting 94 | }; 95 | } 96 | 97 | let ffi_invoke = call_ffi(data.inputs.len(), fn_name); 98 | 99 | quote! { 100 | #body 101 | let result = #ffi_invoke; 102 | RustToRTData::from(env, result) 103 | } 104 | } 105 | 106 | fn call_ffi(len: usize, fn_name: &Ident) -> TokenStream { 107 | match len { 108 | 0 => quote! { 109 | #fn_name() 110 | }, 111 | 1 => quote! { 112 | #fn_name(arg0) 113 | }, 114 | 2 => quote! { 115 | #fn_name(arg0, arg1) 116 | }, 117 | 3 => quote! { 118 | #fn_name(arg0, arg1, arg2) 119 | }, 120 | 4 => quote! { 121 | #fn_name(arg0, arg1, arg2, arg3) 122 | }, 123 | 5 => quote! { 124 | #fn_name(arg0, arg1, arg2, arg3, arg4) 125 | }, 126 | 6 => quote! { 127 | #fn_name(arg0, arg1, arg2, arg3, arg4, arg5) 128 | }, 129 | 7 => quote! { 130 | #fn_name(arg0, arg1, arg2, arg3, arg4, arg5, arg6) 131 | }, 132 | 8 => quote! { 133 | #fn_name(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) 134 | }, 135 | 9 => quote! { 136 | #fn_name(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) 137 | }, 138 | _ => panic!("too many arguments"), 139 | } 140 | } 141 | 142 | fn typecast(ty: &Type, arg_dst: Ident, arg_src: TokenStream) -> TokenStream { 143 | match ty { 144 | Type::Tuple(_tup) => { 145 | quote! { 146 | let #arg_dst: #ty = RTDataToRust::into(#arg_src); 147 | } 148 | } 149 | Type::Path(path) => match &path.path.segments.first().unwrap().arguments { 150 | PathArguments::None => { 151 | quote! { 152 | let #arg_dst: #ty = RTDataToRust::into(#arg_src); 153 | } 154 | } 155 | PathArguments::AngleBracketed(_ang) => { 156 | let type_name = &path.path.segments.first().unwrap().ident; 157 | let type_name_str = format!("{}", &type_name); 158 | 159 | match type_name_str.as_str() { 160 | "Vec" | "Option" | "Result" => quote! { 161 | let #arg_dst: #ty = RTDataToRust::into(#arg_src); 162 | }, 163 | _ => panic!("only Vec, Option, or Result generics types are allowed"), 164 | } 165 | } 166 | _ => panic!("no parentheses at PathArgument"), 167 | }, 168 | _ => panic!("parse type miss"), 169 | } 170 | } 171 | 172 | fn inputs_type(data: &Signature) -> String { 173 | let ret = data.inputs.iter().map(|arg| match arg { 174 | FnArg::Typed(pat) => parse_type(&*pat.ty), 175 | _ => panic!("Need an explicitly typed input pattern "), 176 | }); 177 | 178 | let mut statements = String::from(""); 179 | for (i, data) in ret.enumerate() { 180 | if i == 0 { 181 | statements = format!("{}{}", statements, data); 182 | } else { 183 | statements = format!("{} {}", statements, data); 184 | } 185 | } 186 | statements 187 | } 188 | 189 | fn output_type(data: &Signature) -> String { 190 | let ret = match &data.output { 191 | syn::ReturnType::Default => "[]".to_string(), 192 | syn::ReturnType::Type(_, ty) => parse_type(&*ty), 193 | }; 194 | 195 | ret 196 | } 197 | 198 | fn parse_type(ty: &Type) -> String { 199 | match ty { 200 | Type::Tuple(tup) => { 201 | let mut statements = String::from("["); 202 | 203 | for (i, data) in tup.elems.iter().enumerate() { 204 | if i == 0 { 205 | statements = format!("{}{}", statements, parse_type(data)); 206 | } else { 207 | statements = format!("{} {}", statements, parse_type(data)); 208 | } 209 | } 210 | format!("{}]", statements) 211 | } 212 | Type::Path(path) => { 213 | let mut args_str = String::from(""); 214 | match &path.path.segments.first().unwrap().arguments { 215 | // not generic type (eg BigInt) 216 | PathArguments::None => ex_type_check(&path.path.segments.first().unwrap().ident), 217 | 218 | // generic type (vec, option, result) 219 | PathArguments::AngleBracketed(ang) => { 220 | let args = ang.args.iter().map(|a| match a { 221 | GenericArgument::Type(gene_type) => parse_type(gene_type), 222 | _ => panic!("GenericArgument is only Type"), 223 | }); 224 | 225 | for (i, data) in args.enumerate() { 226 | if i == 0 { 227 | args_str = format!("{}{}", args_str, data); 228 | } else { 229 | args_str = format!("{} {}", args_str, data); 230 | } 231 | } 232 | 233 | let type_name = &path.path.segments.first().unwrap().ident; 234 | let type_name_str = format!("{}", &type_name); 235 | 236 | match type_name_str.as_str() { 237 | "Vec" => format!("'({})", args_str), 238 | "Option" => format!("(Option {})", args_str), 239 | "Result" => format!("(Result {})", args_str), 240 | _ => panic!("only Vec, Option, or Result generics types are allowed"), 241 | } 242 | } 243 | _ => panic!("no parentheses at PathArgument"), 244 | } 245 | } 246 | _ => panic!("parse type miss"), 247 | } 248 | } 249 | 250 | fn ex_type_check(id: &Ident) -> String { 251 | let id_str = format!("{}", &id); 252 | match &*id_str { 253 | "BigInt" => String::from("Int"), 254 | "char" => String::from("Char"), 255 | "String" => String::from("String"), 256 | "bool" => String::from("Bool"), 257 | _ => panic!("arguments must be BigInt, char, bool, or String"), 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /src/macro.rs: -------------------------------------------------------------------------------- 1 | use crate::{parser::Expr, Pos}; 2 | use alloc::{ 3 | collections::{btree_map::Entry, BTreeMap, LinkedList}, 4 | string::String, 5 | }; 6 | 7 | #[derive(Debug)] 8 | pub struct MacroErr { 9 | pub pos: Pos, 10 | pub msg: &'static str, 11 | } 12 | 13 | /// `e1` is a pattern and `e2` is an expression to be matched. 14 | pub fn match_pattern(e1: &Expr, e2: &Expr, ctx: &mut BTreeMap>) -> bool { 15 | match (e1, e2) { 16 | (Expr::ID(left, _), _) => { 17 | if let Some('$') = left.chars().next() { 18 | // If `e1` is `$id`, then a map from `$id` to `e1` is added to `ctx`. 19 | let entry = ctx.entry(left.clone()); 20 | match entry { 21 | Entry::Vacant(ent) => { 22 | let mut list = LinkedList::new(); 23 | list.push_back(e2.clone()); 24 | ent.insert(list); 25 | 26 | true 27 | } 28 | Entry::Occupied(ent) => { 29 | let exprs = ent.get(); 30 | if exprs.len() != 1 { 31 | false 32 | } else { 33 | eq_expr(exprs.front().unwrap(), e2) 34 | } 35 | } 36 | } 37 | } else if left == "_" { 38 | true 39 | } else { 40 | matches!(e2, Expr::ID(right, _) if left == right) 41 | } 42 | } 43 | (Expr::Bool(left, _), Expr::Bool(right, _)) => left == right, 44 | (Expr::Char(left, _), Expr::Char(right, _)) => left == right, 45 | (Expr::Num(left, _), Expr::Num(right, _)) => left == right, 46 | (Expr::Str(left, _), Expr::Str(right, _)) => left == right, 47 | (Expr::Tuple(left, _), Expr::Tuple(right, _)) => match_list(left, right, ctx), 48 | (Expr::Apply(left, _), Expr::Apply(right, _)) => match_list(left, right, ctx), 49 | (Expr::List(left, _), Expr::List(right, _)) => match_list(left, right, ctx), 50 | _ => false, 51 | } 52 | } 53 | 54 | pub fn match_list( 55 | left: &LinkedList, 56 | right: &LinkedList, 57 | ctx: &mut BTreeMap>, 58 | ) -> bool { 59 | let mut prev = None; 60 | let mut it_left = left.iter(); 61 | let mut it_right = right.iter(); 62 | 63 | loop { 64 | match (it_left.next(), it_right.next()) { 65 | (Some(e1), Some(e2)) => { 66 | if let Expr::ID(id, _) = e1 { 67 | if id == "..." { 68 | if let Some(key) = &prev { 69 | let Some(exprs) = ctx.get_mut(key) else { 70 | return false; 71 | }; 72 | exprs.push_back(e2.clone()); 73 | break; 74 | } 75 | } else { 76 | prev = Some(id.clone()); 77 | } 78 | } 79 | 80 | if !match_pattern(e1, e2, ctx) { 81 | return false; 82 | } 83 | } 84 | (Some(e1), None) => { 85 | if let Expr::ID(id, _) = e1 { 86 | return id == "..."; 87 | } else { 88 | return false; 89 | } 90 | } 91 | (None, Some(_)) => return false, 92 | _ => return true, 93 | } 94 | } 95 | 96 | let key = prev.unwrap(); 97 | let exprs = ctx.get_mut(&key).unwrap(); 98 | for expr in it_right { 99 | exprs.push_back(expr.clone()); 100 | } 101 | 102 | true 103 | } 104 | 105 | fn eq_expr(e1: &Expr, e2: &Expr) -> bool { 106 | match (e1, e2) { 107 | (Expr::ID(left, _), Expr::ID(right, _)) => left == right, 108 | (Expr::Bool(left, _), Expr::Bool(right, _)) => left == right, 109 | (Expr::Char(left, _), Expr::Char(right, _)) => left == right, 110 | (Expr::Num(left, _), Expr::Num(right, _)) => left == right, 111 | (Expr::Str(left, _), Expr::Str(right, _)) => left == right, 112 | (Expr::Tuple(left, _), Expr::Tuple(right, _)) => eq_exprs(left, right), 113 | (Expr::Apply(left, _), Expr::Apply(right, _)) => eq_exprs(left, right), 114 | (Expr::List(left, _), Expr::List(right, _)) => eq_exprs(left, right), 115 | _ => false, 116 | } 117 | } 118 | 119 | fn eq_exprs(es1: &LinkedList, es2: &LinkedList) -> bool { 120 | if es1.len() != es2.len() { 121 | return false; 122 | } 123 | 124 | es1.iter().zip(es2.iter()).all(|(e1, e2)| eq_expr(e1, e2)) 125 | } 126 | 127 | pub(crate) fn process_macros(exprs: &mut LinkedList) -> Result { 128 | let macros = parse_macros(exprs)?; 129 | 130 | for expr in exprs.iter_mut() { 131 | apply_macros(¯os, expr)?; 132 | } 133 | 134 | Ok(macros) 135 | } 136 | 137 | pub(crate) fn apply(expr: &mut Expr, macros: &Macros) -> Result<(), MacroErr> { 138 | apply_macros(macros, expr) 139 | } 140 | 141 | fn apply_macros(macros: &Macros, expr: &mut Expr) -> Result<(), MacroErr> { 142 | if let Expr::Apply(exprs, _) = expr { 143 | if let Some(Expr::ID(id, _)) = exprs.front() { 144 | if id == "macro" { 145 | return Ok(()); 146 | } 147 | } 148 | } 149 | 150 | apply_macros_recursively(macros, expr, 0) 151 | } 152 | 153 | fn apply_macros_expr( 154 | pos: Pos, 155 | macros: &Macros, 156 | expr: &Expr, 157 | count: u8, 158 | ) -> Result, MacroErr> { 159 | if count == 0xff { 160 | return Err(MacroErr { 161 | pos, 162 | msg: "too deep macro", 163 | }); 164 | } 165 | 166 | for (_, rules) in macros.iter() { 167 | let mut ctx = BTreeMap::new(); 168 | 169 | for rule in rules.iter() { 170 | if match_pattern(&rule.pattern, expr, &mut ctx) { 171 | let expr = expand(pos, &rule.template, &ctx).pop_front().unwrap(); 172 | 173 | if let Some(e) = apply_macros_expr(pos, macros, &expr, count + 1)? { 174 | return Ok(Some(e)); 175 | } else { 176 | return Ok(Some(expr)); 177 | } 178 | } 179 | } 180 | } 181 | 182 | Ok(None) 183 | } 184 | 185 | fn apply_macros_recursively(macros: &Macros, expr: &mut Expr, count: u8) -> Result<(), MacroErr> { 186 | if count == 0xff { 187 | panic!("{}: too deep macro", expr.get_pos()); 188 | } 189 | 190 | if let Some(e) = apply_macros_expr(expr.get_pos(), macros, expr, count)? { 191 | *expr = e; 192 | } 193 | 194 | match expr { 195 | Expr::Apply(exprs, _) | Expr::List(exprs, _) | Expr::Tuple(exprs, _) => { 196 | for expr in exprs { 197 | apply_macros_recursively(macros, expr, count + 1)?; 198 | } 199 | } 200 | _ => (), 201 | } 202 | 203 | Ok(()) 204 | } 205 | 206 | pub(crate) type Macros = BTreeMap>; 207 | 208 | #[derive(Debug)] 209 | pub(crate) struct MacroRule { 210 | pattern: Expr, 211 | template: Expr, 212 | } 213 | 214 | fn parse_macros(exprs: &LinkedList) -> Result { 215 | let mut result = BTreeMap::new(); 216 | 217 | for e in exprs.iter() { 218 | if let Expr::Apply(es, _) = e { 219 | let mut it = es.iter(); 220 | 221 | let Some(front) = it.next() else { 222 | continue; 223 | }; 224 | 225 | if let Expr::ID(id_macro, _) = front { 226 | if id_macro == "macro" { 227 | let id = it.next(); 228 | let Some(Expr::ID(id, _)) = id else { 229 | return Err(MacroErr { 230 | pos: e.get_pos(), 231 | msg: "invalid macro", 232 | }); 233 | }; 234 | 235 | let mut rules = LinkedList::new(); 236 | for rule in it { 237 | let Expr::Apply(rule_exprs, _) = rule else { 238 | return Err(MacroErr { 239 | pos: rule.get_pos(), 240 | msg: "invalid macro rule", 241 | }); 242 | }; 243 | 244 | if rule_exprs.len() != 2 { 245 | return Err(MacroErr { 246 | pos: rule.get_pos(), 247 | msg: "the number of arguments of a macro rule is not 2", 248 | }); 249 | } 250 | 251 | let mut rule_it = rule_exprs.iter(); 252 | 253 | let mut pattern = rule_it.next().unwrap().clone(); 254 | if let Expr::Apply(arguments, _) = &mut pattern { 255 | if let Some(Expr::ID(front, _)) = arguments.front_mut() { 256 | if front == "_" { 257 | *front = id.clone(); 258 | } else if front != id { 259 | return Err(MacroErr { 260 | pos: pattern.get_pos(), 261 | msg: "invalid macro pattern", 262 | }); 263 | } 264 | } 265 | 266 | let template = rule_it.next().unwrap().clone(); 267 | 268 | rules.push_back(MacroRule { pattern, template }); 269 | } else { 270 | return Err(MacroErr { 271 | pos: pattern.get_pos(), 272 | msg: "invalid macro pattern", 273 | }); 274 | }; 275 | } 276 | 277 | if let Entry::Vacant(entry) = result.entry(id.clone()) { 278 | entry.insert(rules); 279 | } else { 280 | return Err(MacroErr { 281 | pos: e.get_pos(), 282 | msg: "multiply defined", 283 | }); 284 | } 285 | } 286 | } 287 | } 288 | } 289 | 290 | Ok(result) 291 | } 292 | 293 | fn expand(pos: Pos, template: &Expr, ctx: &BTreeMap>) -> LinkedList { 294 | match template { 295 | Expr::ID(id, _) => { 296 | if let Some(exprs) = ctx.get(id) { 297 | let expr = exprs.front().unwrap(); 298 | let mut result = LinkedList::new(); 299 | result.push_back(expr.clone()); 300 | result 301 | } else { 302 | let mut result: LinkedList = LinkedList::new(); 303 | result.push_back(template.clone()); 304 | result 305 | } 306 | } 307 | Expr::Apply(templates, _) => { 308 | let exprs = expand_list(pos, templates, ctx); 309 | let mut result = LinkedList::new(); 310 | 311 | // TODO: rename variables 312 | 313 | result.push_back(Expr::Apply(exprs, pos)); 314 | result 315 | } 316 | Expr::List(templates, _) => { 317 | let exprs = expand_list(pos, templates, ctx); 318 | let mut result = LinkedList::new(); 319 | result.push_back(Expr::List(exprs, pos)); 320 | result 321 | } 322 | Expr::Tuple(templates, _) => { 323 | let exprs = expand_list(pos, templates, ctx); 324 | let mut result = LinkedList::new(); 325 | result.push_back(Expr::Tuple(exprs, pos)); 326 | result 327 | } 328 | expr => { 329 | let mut result = LinkedList::new(); 330 | result.push_back(expr.clone()); 331 | result 332 | } 333 | } 334 | } 335 | 336 | fn expand_list( 337 | pos: Pos, 338 | templates: &LinkedList, 339 | ctx: &BTreeMap>, 340 | ) -> LinkedList { 341 | let mut result = LinkedList::new(); 342 | 343 | let mut prev = None; 344 | 345 | for template in templates { 346 | if let Expr::ID(id, _) = template { 347 | if id == "..." { 348 | if let Some(p) = &prev { 349 | if let Some(exprs) = ctx.get(p) { 350 | let mut it = exprs.iter(); 351 | let _ = it.next(); 352 | 353 | for expr in it { 354 | result.push_back(expr.clone()); 355 | } 356 | } else { 357 | prev = None; 358 | } 359 | } else { 360 | prev = None; 361 | } 362 | 363 | continue; 364 | } else { 365 | prev = Some(id.clone()); 366 | } 367 | } else { 368 | prev = None; 369 | } 370 | 371 | let mut exprs = expand(pos, template, ctx); 372 | result.append(&mut exprs); 373 | } 374 | 375 | result 376 | } 377 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # BLisp 2 | //! 3 | //! BLisp is a well typed Lisp like programming language which adopts effect 4 | //! system for no_std environments. 5 | //! BLisp supports higher order RPCs like higher order functions 6 | //! of functional programing languages. 7 | //! 8 | //! This repository provides only a library crate. 9 | //! Please see [blisp-repl](https://github.com/ytakano/blisp-repl) to use BLisp, 10 | //! or [baremetalisp](https://github.com/ytakano/baremetalisp) which is a toy OS. 11 | //! 12 | //! [Homepage](https://ytakano.github.io/blisp/) is here. 13 | //! 14 | //! ## Features 15 | //! 16 | //! - Algebraic data type 17 | //! - Generics 18 | //! - Hindley–Milner based type inference 19 | //! - Effect system to separate side effects from pure functions 20 | //! - Big integer 21 | //! - Supporting no_std environments 22 | //! 23 | //! ## Examples 24 | //! 25 | //! ### Simple Eval 26 | //! 27 | //! ``` 28 | //! let code = " 29 | //! (export factorial (n) (Pure (-> (Int) Int)) 30 | //! (factorial' n 1)) 31 | //! 32 | //! (defun factorial' (n total) (Pure (-> (Int Int) Int)) 33 | //! (if (<= n 0) 34 | //! total 35 | //! (factorial' (- n 1) (* n total))))"; 36 | //! 37 | //! let exprs = blisp::init(code, vec![]).unwrap(); 38 | //! let ctx = blisp::typing(exprs).unwrap(); 39 | //! let expr = "(factorial 10)"; 40 | //! for result in blisp::eval(expr, &ctx).unwrap() { 41 | //! println!("{}", result.unwrap()); 42 | //! } 43 | //! ``` 44 | //! 45 | //! ### Foreign Function Interface 46 | //! 47 | //! ``` 48 | //! use blisp::{self, embedded}; 49 | //! use num_bigint::BigInt; 50 | //! 51 | //! #[embedded] 52 | //! fn add_four_ints(a: BigInt, b: (BigInt, BigInt), c: Option) -> Result { 53 | //! let mut result = a + b.0 + b.1; 54 | //! if let Some(n) = c { 55 | //! result += n; 56 | //! } 57 | //! 58 | //! Ok(result) 59 | //! } 60 | //! 61 | //! let code = " 62 | //! (export call_add_four_ints (n) 63 | //! (IO (-> ((Option Int)) (Result Int String))) 64 | //! (add_four_ints 1 [2 3] n))"; // call `add_four_ints` in Rust here. 65 | //! 66 | //! let exprs = blisp::init(code, vec![Box::new(AddFourInts)]).unwrap(); // extern `add_four_ints` 67 | //! let ctx = blisp::typing(exprs).unwrap(); 68 | //! let result = blisp::eval("(call_add_four_ints (Some 4))", &ctx).unwrap(); 69 | //! 70 | //! let front = result.front().unwrap().as_ref().unwrap(); 71 | //! assert_eq!(front, "(Ok 10)"); 72 | //! ``` 73 | //! 74 | //! ### Expressions 75 | //! 76 | //! ```lisp 77 | //! "Hello, World!" ; string 78 | //! (+ 0x10 0x20) ; 48 79 | //! (+ 0b111 0b101) ; 12 80 | //! (+ 0o777 0o444) ; 803 81 | //! (car '(1 2 3)) ; (Some 1) 82 | //! (cdr '(1 2 3)) ; '(2 3) 83 | //! (map (lambda (x) (* x 2)) '(8 9 10)) ; '(16 18 20) 84 | //! (fold + 0 '(1 2 3 4 5 6 7 8 9)) ; 45 85 | //! (reverse '(1 2 3 4 5 6 7 8 9)) ; '(9 8 7 6 5 4 3 2 1) 86 | //! (filter (lambda (x) (= (% x 2) 0)) '(1 2 3 4 5 6 7 8 9)) ; '(2 4 6 8) 87 | //! ``` 88 | 89 | #![cfg_attr(not(test), no_std)] 90 | 91 | extern crate alloc; 92 | 93 | use core::fmt::Display; 94 | 95 | use alloc::{ 96 | boxed::Box, 97 | collections::linked_list::LinkedList, 98 | format, 99 | string::{String, ToString}, 100 | vec::Vec, 101 | }; 102 | 103 | pub mod coq; 104 | pub mod r#macro; 105 | pub mod parser; 106 | pub mod runtime; 107 | pub mod semantics; 108 | 109 | pub use blisp_embedded::embedded; 110 | use r#macro::{process_macros, Macros}; 111 | use runtime::FFI; 112 | 113 | #[derive(Debug, Clone, Copy)] 114 | pub enum FileType { 115 | Prelude, 116 | User, 117 | Eval, 118 | Extern(u64), 119 | } 120 | 121 | /// indicate a position of file 122 | #[derive(Debug, Clone, Copy)] 123 | pub struct Pos { 124 | pub file_id: FileType, 125 | pub line: usize, // line number, 0 origin 126 | pub column: usize, // column number, 0 origin 127 | } 128 | 129 | impl Display for Pos { 130 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 131 | write!(f, "{:?}:{}:{}", self.file_id, self.line, self.column) 132 | } 133 | } 134 | 135 | /// error message 136 | #[derive(Debug)] 137 | pub struct LispErr { 138 | pub msg: String, 139 | pub pos: Pos, 140 | } 141 | 142 | impl LispErr { 143 | fn new(msg: String, pos: Pos) -> LispErr { 144 | LispErr { msg, pos } 145 | } 146 | } 147 | 148 | pub struct TypingContext { 149 | exprs: LinkedList, 150 | ext_funs: Vec>, 151 | macros: Macros, 152 | } 153 | 154 | /// initialize BLisp with code 155 | /// 156 | /// # Example 157 | /// 158 | /// ``` 159 | /// let code = "(export factorial (n) (Pure (-> (Int) Int)) 160 | /// (if (<= n 0) 161 | /// 1 162 | /// (* n (factorial (- n 1)))))"; 163 | /// 164 | /// blisp::init(code, vec![]).unwrap(); 165 | /// ``` 166 | pub fn init(code: &str, ext_funs: Vec>) -> Result { 167 | let prelude = include_str!("prelude.lisp"); 168 | // let prelude = ""; 169 | let mut ps = parser::Parser::new(prelude, FileType::Prelude); 170 | let mut exprs = match ps.parse() { 171 | Ok(e) => e, 172 | Err(e) => { 173 | let msg = format!("Syntax Error: {}", e.msg); 174 | return Err(LispErr::new(msg, e.pos)); 175 | } 176 | }; 177 | 178 | for (i, fun) in ext_funs.iter().enumerate() { 179 | let mut ps = parser::Parser::new(fun.blisp_extern(), FileType::Extern(i as u64)); 180 | match ps.parse() { 181 | Ok(mut e) => { 182 | exprs.append(&mut e); 183 | } 184 | Err(e) => { 185 | let msg = format!("Syntax Error: {}", e.msg); 186 | return Err(LispErr::new(msg, e.pos)); 187 | } 188 | } 189 | } 190 | 191 | let mut ps = parser::Parser::new(code, FileType::User); 192 | match ps.parse() { 193 | Ok(mut e) => { 194 | exprs.append(&mut e); 195 | 196 | let macros = match process_macros(&mut exprs) { 197 | Ok(macros) => macros, 198 | Err(e) => { 199 | let msg = format!("Macro Error: {}", e.msg); 200 | return Err(LispErr::new(msg, e.pos)); 201 | } 202 | }; 203 | 204 | Ok(TypingContext { 205 | exprs, 206 | ext_funs, 207 | macros, 208 | }) 209 | } 210 | Err(e) => { 211 | let msg = format!("Syntax Error: {}", e.msg); 212 | Err(LispErr::new(msg, e.pos)) 213 | } 214 | } 215 | } 216 | 217 | /// perform type checking and inference 218 | /// 219 | /// # Example 220 | /// 221 | /// ``` 222 | /// let code = "(export factorial (n) (Pure (-> (Int) Int)) 223 | /// (if (<= n 0) 224 | /// 1 225 | /// (* n (factorial (- n 1)))))"; 226 | /// 227 | /// let exprs = blisp::init(code, vec![]).unwrap(); 228 | /// blisp::typing(exprs).unwrap(); 229 | /// ``` 230 | pub fn typing(exprs: TypingContext) -> Result { 231 | match semantics::exprs2context(exprs) { 232 | Ok(c) => Ok(c), 233 | Err(e) => { 234 | let msg = format!("Typing Error: {}", e.msg); 235 | Err(LispErr::new(msg, e.pos)) 236 | } 237 | } 238 | } 239 | 240 | /// evaluate an expression 241 | /// 242 | /// # Example 243 | /// 244 | /// ``` 245 | /// let code = "(export factorial (n) (Pure (-> (Int) Int)) 246 | /// (if (<= n 0) 247 | /// 1 248 | /// (* n (factorial (- n 1)))))"; 249 | /// 250 | /// let exprs = blisp::init(code, vec![]).unwrap(); 251 | /// let ctx = blisp::typing(exprs).unwrap(); 252 | /// let expr = "(factorial 30)"; 253 | /// for result in blisp::eval(expr, &ctx).unwrap() { 254 | /// println!("{}", result.unwrap()); 255 | /// } 256 | /// ``` 257 | pub fn eval( 258 | code: &str, 259 | ctx: &semantics::Context, 260 | ) -> Result>, LispErr> { 261 | runtime::eval(code, ctx) 262 | } 263 | 264 | pub fn transpile(ctx: &semantics::Context) -> String { 265 | let mut s = "".to_string(); 266 | for (_, d) in ctx.data.iter() { 267 | s = format!("{}{}\n", s, coq::to_coq_data(d)); 268 | } 269 | 270 | for (_, f) in ctx.funs.iter() { 271 | s = format!("{}{}\n", s, coq::to_coq_func(f)); 272 | } 273 | 274 | format!("{}\n\n{}", coq::import(), s) 275 | } 276 | 277 | #[cfg(test)] 278 | mod tests { 279 | use super::*; 280 | 281 | #[test] 282 | fn test_macro() { 283 | let expr = " 284 | (macro add 285 | ((add $e1 $e2) (+ $e1 $e2)) 286 | ((add $e1 $e2 $e3 ...) (+ $e1 (add $e2 $e3 ...)))) 287 | 288 | (macro minus 289 | ((_ $e1 $e2) (- $e1 $e2)) 290 | ((_ $e1 $e2 $e3 ...) (- $e1 (minus $e2 $e3 ...)))) 291 | 292 | (macro tuple_to_list 293 | ((_ []) ((lambda (x) x) '())) 294 | ((_ [$e ...]) ((lambda (x) x) '($e ...)))) 295 | 296 | (macro none ((_ _) ([]))) 297 | 298 | (tuple_to_list []) 299 | (tuple_to_list [1 2 3]) 300 | 301 | (add 1 2 3 4 5) 302 | 303 | (defun test_add () (Pure (-> () Int)) 304 | (add 1 2 3 4 (minus 5 6 7) 8)) 305 | 306 | (add 1) 307 | 308 | (none 123) 309 | "; 310 | 311 | let typing_context = init(expr, vec![]).unwrap(); 312 | 313 | for expr in typing_context.exprs.iter() { 314 | println!("{expr}"); 315 | } 316 | } 317 | 318 | fn eval_result(code: &str, ctx: &semantics::Context) { 319 | for r in eval(code, ctx).unwrap() { 320 | println!("{} -> {}", code, r.unwrap()); 321 | } 322 | } 323 | 324 | #[test] 325 | fn ops() { 326 | let exprs = init("", vec![]).unwrap(); 327 | let ctx = typing(exprs).unwrap(); 328 | eval_result("(neq (Some \"Hello\") 10)", &ctx); 329 | eval_result("(chars \"Hello, World!\")", &ctx); 330 | eval_result("(str '(`H` `e` `l` `l` `o`))", &ctx); 331 | eval_result("`\\``", &ctx); 332 | eval_result("(= `h` `h`)", &ctx); 333 | eval_result("(<< 8 4)", &ctx); 334 | eval_result("(>> 128 4)", &ctx); 335 | eval_result("\"Hello, World!\"", &ctx); 336 | eval_result("(= \"Hello, World!\" \"Hello, World!\")", &ctx); 337 | eval_result("(= (Some 1) (Some 2))", &ctx); 338 | eval_result("(< (Some 1) (Some 2))", &ctx); 339 | eval_result("(> (Some 1) (Some 2))", &ctx); 340 | eval_result("(= \"Hello\" \"Hel\")", &ctx); 341 | eval_result("(eq \"Hello\" 10)", &ctx); 342 | eval_result("(lt \"Hello\" 10)", &ctx); 343 | eval_result("(lt 5 10)", &ctx); 344 | eval_result("(+ 0x10 0x20)", &ctx); 345 | eval_result("(+ 0b111 0b101)", &ctx); 346 | eval_result("(+ 0o777 0o444)", &ctx); 347 | eval_result("(+ 10 20)", &ctx); 348 | eval_result("(pow 10 20)", &ctx); 349 | eval_result("(band 1 0)", &ctx); 350 | eval_result("(band 1 1)", &ctx); 351 | eval_result("(bor 1 0)", &ctx); 352 | eval_result("(bor 1 1)", &ctx); 353 | eval_result("(bxor 1 0)", &ctx); 354 | eval_result("(bxor 1 1)", &ctx); 355 | eval_result("(sqrt 16)", &ctx); 356 | eval_result("(sqrt -1)", &ctx); 357 | } 358 | 359 | #[test] 360 | fn lambda() { 361 | let expr = "(export lambda-test (f) 362 | (Pure (-> ((Pure (-> (Int Int) Int))) Int)) 363 | (f 10 20)) 364 | "; 365 | let exprs = init(expr, vec![]).unwrap(); 366 | let ctx = typing(exprs).unwrap(); 367 | let e = "(lambda-test (lambda (x y) (* x y)))"; 368 | eval_result(e, &ctx); 369 | 370 | let e = "(lambda-test +)"; 371 | eval_result(e, &ctx); 372 | } 373 | 374 | #[test] 375 | fn list() { 376 | let expr = " 377 | (export head (x) (Pure (-> ('(Int)) (Option Int))) 378 | (match x 379 | ((Cons n _) (Some n)) 380 | (_ None))) 381 | (export tail (x) (Pure (-> ('(Int)) (Option Int))) 382 | ; match expression 383 | (match x 384 | (Nil None) 385 | ((Cons n Nil) (Some n)) 386 | ((Cons _ l) (tail l)))) 387 | "; 388 | let exprs = init(expr, vec![]).unwrap(); 389 | let ctx = typing(exprs).unwrap(); 390 | 391 | let e = "(head '(30 40 50))"; 392 | eval_result(e, &ctx); 393 | 394 | let e = "(tail '(30 40 50))"; 395 | eval_result(e, &ctx); 396 | } 397 | 398 | #[test] 399 | fn tuple() { 400 | let expr = "(export first (x) (Pure (-> ([Int Bool]) Int)) 401 | (match x 402 | ([n _] n))) 403 | "; 404 | let exprs = init(expr, vec![]).unwrap(); 405 | let ctx = typing(exprs).unwrap(); 406 | let e = "(first [10 false])"; 407 | eval_result(e, &ctx); 408 | } 409 | 410 | #[test] 411 | fn prelude() { 412 | let expr = " 413 | (export factorial (n) (Pure (-> (Int) Int)) 414 | (factorial' n 1)) 415 | (defun factorial' (n total) (Pure (-> (Int Int) Int)) 416 | (if (<= n 0) 417 | total 418 | (factorial' (- n 1) (* n total)))) 419 | "; 420 | let exprs = init(expr, vec![]).unwrap(); 421 | let ctx = typing(exprs).unwrap(); 422 | 423 | let e = "(Some 10)"; 424 | eval_result(e, &ctx); 425 | 426 | let e = "(car '(1 2 3))"; 427 | eval_result(e, &ctx); 428 | 429 | let e = "(cdr '(1 2 3))"; 430 | eval_result(e, &ctx); 431 | 432 | let e = "(map (lambda (x) (* x 2)) '(8 9 10))"; 433 | eval_result(e, &ctx); 434 | 435 | let e = "(fold + 0 '(1 2 3 4 5 6 7 8 9))"; 436 | eval_result(e, &ctx); 437 | 438 | let e = "(reverse '(1 2 3 4 5 6 7 8 9))"; 439 | eval_result(e, &ctx); 440 | 441 | let e = "(filter (lambda (x) (= (% x 2) 0)) '(1 2 3 4 5 6 7 8 9))"; 442 | eval_result(e, &ctx); 443 | 444 | let e = "(factorial 2000)"; 445 | eval_result(e, &ctx); 446 | } 447 | 448 | #[test] 449 | fn callback() { 450 | let expr = " 451 | (export callback (x y z) (IO (-> (Int Int Int) (Option Int))) 452 | (call-rust x y z))"; 453 | let exprs = init(expr, vec![]).unwrap(); 454 | let mut ctx = typing(exprs).unwrap(); 455 | 456 | use num_bigint::BigInt; 457 | use std::boxed::Box; 458 | let fun = |x: &BigInt, y: &BigInt, z: &BigInt| { 459 | let n = x * y * z; 460 | println!("n = {}", n); 461 | Some(n) 462 | }; 463 | ctx.set_callback(Box::new(fun)); 464 | 465 | let e = "(callback 100 2000 30000)"; 466 | eval_result(e, &ctx); 467 | } 468 | 469 | #[test] 470 | fn do_transpile() { 471 | let expr = " 472 | (defun snoc (l y) 473 | (Pure (-> ( 474 | '(t) t) 475 | '(t))) 476 | (match l 477 | (nil (Cons y nil)) 478 | ((Cons h b) (Cons h (snoc b y))))) 479 | 480 | (defun rev (l) 481 | (Pure (-> ( 482 | '(t)) 483 | '(t))) 484 | (match l 485 | (nil nil) 486 | ((Cons h t) (snoc (rev t) h)))) 487 | "; 488 | let exprs = init(expr, vec![]).unwrap(); 489 | let ctx = typing(exprs).unwrap(); 490 | 491 | println!("{}", transpile(&ctx)); 492 | } 493 | 494 | #[test] 495 | fn test_multibyte() { 496 | let expr = "あ"; 497 | let _exprs = init(expr, vec![]).unwrap(); 498 | 499 | let expr = ""; 500 | let exprs = init(expr, vec![]).unwrap(); 501 | let ctx = typing(exprs).unwrap(); 502 | 503 | let e = "\"あ\""; 504 | let r = eval(e, &ctx).unwrap(); 505 | println!("{r:?}"); 506 | 507 | let e = "`あ`"; 508 | let r = eval(e, &ctx).unwrap(); 509 | println!("{r:?}"); 510 | } 511 | } 512 | -------------------------------------------------------------------------------- /src/coq.rs: -------------------------------------------------------------------------------- 1 | use super::semantics as S; 2 | use alloc::{ 3 | collections::LinkedList, 4 | format, 5 | string::{String, ToString}, 6 | vec::Vec, 7 | }; 8 | 9 | pub(crate) fn to_coq_type( 10 | expr: &S::TypeExpr, 11 | depth: usize, 12 | targs: &mut LinkedList, 13 | ) -> String { 14 | match expr { 15 | S::TypeExpr::Bool(_) => "bool".to_string(), 16 | S::TypeExpr::Int(_) => "Z".to_string(), 17 | S::TypeExpr::String(_) => "string".to_string(), 18 | S::TypeExpr::Char(_) => "ascii".to_string(), 19 | S::TypeExpr::Id(e) => { 20 | if let Some(c) = e.id.chars().next() { 21 | if c.is_ascii_lowercase() { 22 | let mut flag = false; 23 | for s in targs.iter() { 24 | if *s == e.id { 25 | flag = true; 26 | } 27 | } 28 | 29 | if !flag { 30 | targs.push_back(e.id.clone()); 31 | } 32 | } 33 | } 34 | e.id.clone() 35 | } 36 | S::TypeExpr::Tuple(e) => { 37 | if e.ty.is_empty() { 38 | return "unit".to_string(); 39 | } 40 | 41 | let mut i = 0; 42 | let mut s = "".to_string(); 43 | 44 | for t in e.ty.iter() { 45 | i += 1; 46 | if i == e.ty.len() { 47 | s = format!("{}{}", s, to_coq_type(t, depth + 1, targs)); 48 | } else { 49 | s = format!("{}{} * ", s, to_coq_type(t, depth + 1, targs)); 50 | } 51 | } 52 | 53 | if depth > 0 { 54 | format!("({})", s) 55 | } else { 56 | s 57 | } 58 | } 59 | S::TypeExpr::List(e) => { 60 | if depth == 0 { 61 | format!("list {}", to_coq_type(&e.ty, depth + 1, targs)) 62 | } else { 63 | format!("(list {})", to_coq_type(&e.ty, depth + 1, targs)) 64 | } 65 | } 66 | S::TypeExpr::Data(e) => { 67 | if e.type_args.is_empty() { 68 | e.id.id.clone() 69 | } else { 70 | let mut args = "".to_string(); 71 | for arg in e.type_args.iter() { 72 | args = format!("{}{}", args, to_coq_type(arg, depth + 1, targs)); 73 | } 74 | 75 | if depth == 0 { 76 | format!("{} {}", e.id.id, args) 77 | } else { 78 | format!("({} {})", e.id.id, args) 79 | } 80 | } 81 | } 82 | S::TypeExpr::Fun(e) => { 83 | let mut s = "".to_string(); 84 | 85 | // ここがおかしいかも 86 | for (i, arg) in e.args.iter().enumerate() { 87 | if i == 0 { 88 | s = to_coq_type(arg, depth + 1, targs); 89 | } else { 90 | //s = format!("{} -> {}", s, to_coq_type(arg, depth + 1, targs)); 91 | s = format!("{} -> {}", s, to_coq_type(arg, depth + 1, targs)); 92 | } 93 | } 94 | s = format!("{} -> {}", s, to_coq_type(&e.ret, depth + 1, targs)); 95 | 96 | if depth > 0 { 97 | format!("({})", s) 98 | } else { 99 | s 100 | } 101 | } 102 | } 103 | } 104 | 105 | pub(crate) fn import() -> &'static str { 106 | "Require Import ZArith. 107 | Require Import Coq.Lists.List." 108 | /*\n 109 | Inductive tuple5 (A, B, C, D, E:Type): Type := 110 | | tup5 (x0: A, x1: B, x2: C, x3: D, x4: E). 111 | Inductive tuple4 (A, B, C, D:Type): Type := 112 | | tup4 (x0: A, x1: B, x2: C, x3: D). 113 | Inductive tuple3 (A, B, C:Type): Type := 114 | | tup3 (x0: A, x1: B, x2: C). 115 | Inductive tuple2 (A, B:Type): Type := 116 | | tup2 (x0: A, x1: B). 117 | Inductive tuple1 (A:Type): Type := 118 | | tup1 (x0: A). 119 | Inductive tuple0 : Type := 120 | | tup0."*/ 121 | } 122 | 123 | pub(crate) fn to_coq_data(expr: &S::DataType) -> String { 124 | let mut mem = "".to_string(); 125 | let mut i = 0; 126 | for d in expr.members.iter() { 127 | i += 1; 128 | if i == expr.members.len() { 129 | mem = format!("{}{}.\n", mem, to_coq_data_mem(d)); 130 | } else { 131 | mem = format!("{}{}\n", mem, to_coq_data_mem(d)); 132 | } 133 | } 134 | 135 | if expr.members.is_empty() { 136 | format!("Inductive {}\n{}", to_coq_data_def(&expr.name), mem) 137 | } else { 138 | let extend = inductive_arguments(expr); 139 | format!( 140 | "Inductive {}\n{}{}\n", 141 | to_coq_data_def(&expr.name), 142 | mem, 143 | extend 144 | ) 145 | } 146 | } 147 | 148 | fn to_coq_data_def(expr: &S::DataTypeName) -> String { 149 | let mut args = "(".to_string(); 150 | let mut i = 0; 151 | for t in expr.type_args.iter() { 152 | i += 1; 153 | if expr.type_args.len() == i { 154 | args = format!("{}{}: Type)", args, t.id); 155 | } else { 156 | args = format!("{}{} ", args, t.id); 157 | } 158 | } 159 | 160 | if !expr.type_args.is_empty() { 161 | format!("{} {}: Type :=", expr.id.id, args) 162 | } else { 163 | format!("{}: Type :=", expr.id.id) 164 | } 165 | } 166 | 167 | fn to_coq_data_mem(expr: &S::DataTypeMem) -> String { 168 | let mut mem = "".to_string(); 169 | for (i, t) in expr.types.iter().enumerate() { 170 | let mut targs = LinkedList::new(); 171 | if expr.types.len() == i + 1 { 172 | mem = format!("{}(x{}: {})", mem, i, to_coq_type(t, 0, &mut targs)); 173 | } else { 174 | mem = format!("{}(x{}: {}) ", mem, i, to_coq_type(t, 0, &mut targs)); 175 | } 176 | } 177 | 178 | if !expr.types.is_empty() { 179 | format!("| {} {}", expr.id.id, mem) 180 | } else { 181 | format!("| {}", expr.id.id) 182 | } 183 | } 184 | 185 | fn to_args_type(args: &LinkedList, ty: &str) -> String { 186 | let mut s = "(".to_string(); 187 | let mut i = 0; 188 | for a in args { 189 | i += 1; 190 | if i == args.len() { 191 | s = format!("{}{}: {})", s, a, ty); 192 | } else { 193 | s = format!("{}{} ", s, a); 194 | } 195 | } 196 | 197 | s 198 | } 199 | 200 | fn inductive_arguments(expr: &S::DataType) -> String { 201 | let mut add_expr = "".to_string(); 202 | for t in &expr.members { 203 | let mut temp = "".to_string(); 204 | for (i, _id) in expr.name.type_args.iter().enumerate() { 205 | match i { 206 | 0 => temp = format!("{}{}", temp, _id.id), 207 | _ => temp = format!("{} {}", temp, _id.id), 208 | } 209 | } 210 | add_expr = format!("{}\nArguments {}{{{}}}.", add_expr, t.id.id, temp); 211 | } 212 | add_expr 213 | } 214 | 215 | pub(crate) fn to_coq_func(expr: &S::Defun) -> String { 216 | let head = if is_recursive(expr) { 217 | format!("Fixpoint {}", expr.id.id) 218 | } else { 219 | format!("Definition {}", expr.id.id) 220 | }; 221 | 222 | let fun_type = if let S::TypeExpr::Fun(e) = &expr.fun_type { 223 | e 224 | } else { 225 | return "".to_string(); 226 | }; 227 | 228 | // transpile arguments 229 | // arguments whose types are same are aggregated 230 | let mut args = "".to_string(); 231 | let mut targs = LinkedList::new(); 232 | let mut args_list = LinkedList::new(); 233 | let mut prev = "".to_string(); 234 | for (arg, t) in expr.args.iter().zip(fun_type.args.iter()) { 235 | let ta = to_coq_type(t, 0, &mut targs); 236 | if prev.is_empty() { 237 | prev = ta.clone(); 238 | } 239 | 240 | if prev != ta { 241 | let s = to_args_type(&args_list, &prev); 242 | args = format!("{} {}", args, s); 243 | args_list.clear(); 244 | args_list.push_back(arg.id.clone()); 245 | prev = ta; 246 | } else { 247 | args_list.push_back(arg.id.clone()); 248 | } 249 | } 250 | 251 | let s = to_args_type(&args_list, &prev); 252 | args = format!("{} {}", args, s); 253 | 254 | // transpile return type 255 | let ret = to_coq_type(&fun_type.ret, 0, &mut targs); 256 | 257 | //indent count 258 | let mut tab_count = 0; 259 | let tl_expr = func_analyze(&expr.expr, &mut tab_count); 260 | 261 | // if there is no type argument, then return 262 | if targs.is_empty() { 263 | return format!("{}{}: {} :=\n{}.\n", head, args, ret, tl_expr); 264 | } 265 | 266 | // transpile type arguments 267 | let mut s_targs = "{".to_string(); 268 | let mut i = 0; 269 | for targ in &targs { 270 | i += 1; 271 | if i == targs.len() { 272 | s_targs = format!("{}{}", s_targs, targ); 273 | } else { 274 | s_targs = format!("{}{} ", s_targs, targ); 275 | } 276 | } 277 | 278 | s_targs = format!("{}: Type}}", s_targs); 279 | 280 | format!("{} {}{}: {} :=\n{}.\n", head, s_targs, args, ret, tl_expr) 281 | } 282 | 283 | fn func_analyze(expr: &S::LangExpr, count: &mut i32) -> String { 284 | match expr { 285 | S::LangExpr::IfExpr(ex) => { 286 | let mut if_expr = "".to_string(); 287 | if_expr = format!( 288 | "{}match {} with\n", 289 | if_expr, 290 | func_analyze(&ex.cond_expr, count) 291 | ); 292 | *count += 2; 293 | let tab_expr = tabb(*count); 294 | 295 | if_expr = format!( 296 | "{}{}| true => {}\n", 297 | if_expr, 298 | tab_expr, 299 | func_analyze(&ex.then_expr, count) 300 | ); 301 | if_expr = format!( 302 | "{}{}| false => {}\n", 303 | if_expr, 304 | tab_expr, 305 | func_analyze(&ex.else_expr, count) 306 | ); 307 | 308 | *count -= 2; 309 | format!("{}{}end", if_expr, tabb(*count + 2)) 310 | } 311 | S::LangExpr::LetExpr(ex) => { 312 | let mut let_expr = "".to_string(); 313 | if ex.def_vars.is_empty() { 314 | return let_expr; 315 | } 316 | for t in &ex.def_vars { 317 | let_expr = format!( 318 | "{}let {} = {} in\n", 319 | let_expr, 320 | pattern_analyze(&t.pattern), 321 | func_analyze(&t.expr, count) 322 | ); 323 | } 324 | let_expr 325 | } 326 | S::LangExpr::LitStr(ex) => ex.str.to_string(), 327 | S::LangExpr::LitChar(ex) => ex.c.to_string(), 328 | S::LangExpr::LitNum(ex) => ex.num.to_string(), 329 | S::LangExpr::LitBool(ex) => ex.val.to_string(), 330 | S::LangExpr::IDExpr(ex) => ex.id.to_string(), 331 | S::LangExpr::DataExpr(ex) => { 332 | let mut data_expr = "".to_string(); 333 | let temp: &str = &ex.label.id; 334 | let temp1 = match temp { 335 | "Cons" => "cons".to_string(), 336 | _ => temp.to_string(), 337 | }; 338 | data_expr = format!("{}({}", data_expr, temp1); 339 | if !&ex.exprs.is_empty() { 340 | for t in &ex.exprs { 341 | data_expr = format!("{} {}", data_expr, func_analyze(t, count)); 342 | } 343 | } 344 | format!("{})", data_expr) 345 | } 346 | S::LangExpr::MatchExpr(ex) => { 347 | let mut match_expr = "match".to_string(); 348 | match_expr = format!("{} {} with", match_expr, func_analyze(&ex.expr, count)); 349 | 350 | *count += 2; 351 | let tab_expr = tabb(*count); 352 | 353 | let mut case_expr = "".to_string(); 354 | for t in &ex.cases { 355 | case_expr = format!( 356 | "{}\n{}| {} => {}", 357 | case_expr, 358 | tab_expr, 359 | pattern_analyze(&t.pattern), 360 | func_analyze(&t.expr, count) 361 | ); 362 | } 363 | *count -= 2; 364 | format!("{}{}\n{}end", match_expr, case_expr, tabb(*count + 2)) 365 | } 366 | S::LangExpr::ApplyExpr(ex) => { 367 | let mut apply_expr = "(".to_string(); 368 | let mut store: Option = None; 369 | for t in &ex.exprs { 370 | let temp = func_analyze(t, count); 371 | match apply_arith(temp.clone()) { 372 | Some(_) => { 373 | store = apply_arith(temp); 374 | } 375 | None => { 376 | match store { 377 | Some(y) => apply_expr = format!("{} {} {}", apply_expr, temp, y), 378 | None => apply_expr = format!("{}{} ", apply_expr, temp), 379 | } 380 | store = None; 381 | } 382 | } 383 | } 384 | format!("{})", apply_expr) 385 | } 386 | S::LangExpr::ListExpr(ex) => { 387 | if ex.exprs.is_empty() { 388 | return "nil".to_string(); 389 | } 390 | let mut list_expr = "".to_string(); 391 | let mut temp = "".to_string(); 392 | for (_i, t) in ex.exprs.iter().enumerate() { 393 | list_expr = format!("{}(cons {} ", list_expr, func_analyze(t, count)); 394 | temp = format!("{})", temp); 395 | } 396 | format!("{}nil{}", list_expr, temp) 397 | } 398 | S::LangExpr::TupleExpr(ex) => { 399 | let length = &ex.exprs.len(); 400 | let mut tupple_expr = format!("tup{}", &length); 401 | match length { 402 | 0 => tupple_expr, 403 | _ => { 404 | tupple_expr = format!("{} (", tupple_expr); 405 | for t in &ex.exprs { 406 | tupple_expr = format!("{} {}", tupple_expr, func_analyze(t, count)); 407 | } 408 | format!("{})", tupple_expr) 409 | } 410 | } 411 | } 412 | S::LangExpr::LambdaExpr(ex) => { 413 | let mut lambda_expr = "fun".to_string(); 414 | if ex.args.is_empty() { 415 | lambda_expr = format!("{} _", lambda_expr); 416 | } else { 417 | for t in &ex.args { 418 | lambda_expr = format!("{} {}", lambda_expr, t.id); 419 | } 420 | } 421 | format!("{} => {}", lambda_expr, func_analyze(&ex.expr, count)) 422 | } 423 | } 424 | } 425 | 426 | fn pattern_analyze(pattern: &S::Pattern) -> String { 427 | match pattern { 428 | S::Pattern::PatStr(ex) => ex.str.to_string(), 429 | S::Pattern::PatChar(ex) => ex.c.to_string(), 430 | S::Pattern::PatNum(ex) => ex.num.to_string(), 431 | S::Pattern::PatBool(ex) => ex.val.to_string(), 432 | S::Pattern::PatID(ex) => ex.id.to_string(), 433 | S::Pattern::PatTuple(ex) => { 434 | let mut pattern_expr = "".to_string(); 435 | let length = &ex.pattern.len(); 436 | if ex.pattern.is_empty() { 437 | return format!("{}tup{}", pattern_expr, length); 438 | } 439 | pattern_expr = format!("{}tup{} (", pattern_expr, length); 440 | for t in &ex.pattern { 441 | pattern_expr = format!("{} {}", pattern_expr, pattern_analyze(t)); 442 | } 443 | format!("{})", pattern_expr) 444 | } 445 | S::Pattern::PatData(ex) => { 446 | let mut pattern_expr = "".to_string(); 447 | let temp: &str = &ex.label.id; 448 | let temp = match temp { 449 | "Cons" => "cons".to_string(), 450 | _ => temp.to_string(), 451 | }; 452 | pattern_expr = format!("{}({}", pattern_expr, temp); 453 | for t in &ex.pattern { 454 | pattern_expr = format!("{} {}", pattern_expr, pattern_analyze(t)); 455 | } 456 | format!("{})", pattern_expr) 457 | } 458 | S::Pattern::PatNil(_) => "_".to_string(), 459 | } 460 | } 461 | 462 | fn apply_arith(expr: String) -> Option { 463 | let temp: Vec = expr.chars().collect(); 464 | match temp[0] { 465 | '+' => Some(String::from("+")), 466 | '-' => Some(String::from("-")), 467 | '*' => Some(String::from("*")), 468 | '/' => Some(String::from("/")), 469 | '%' => Some(String::from("%")), 470 | _ => None, 471 | } 472 | } 473 | 474 | fn tabb(count: i32) -> String { 475 | let mut tab_expr = "".to_string(); 476 | for _ in 1..=count { 477 | tab_expr = format!("{} ", tab_expr); 478 | } 479 | tab_expr 480 | } 481 | 482 | fn is_recursive(expr: &S::Defun) -> bool { 483 | is_recursive_expr(&expr.expr, &expr.id.id) 484 | } 485 | 486 | fn is_recursive_expr(expr: &S::LangExpr, id: &str) -> bool { 487 | match expr { 488 | S::LangExpr::IfExpr(e) => { 489 | is_recursive_expr(&e.cond_expr, id) 490 | || is_recursive_expr(&e.then_expr, id) 491 | || is_recursive_expr(&e.else_expr, id) 492 | } 493 | S::LangExpr::LetExpr(e) => is_recursive_expr(&e.expr, id), 494 | S::LangExpr::LitStr(_) => false, 495 | S::LangExpr::LitChar(_) => false, 496 | S::LangExpr::LitNum(_) => false, 497 | S::LangExpr::LitBool(_) => false, 498 | S::LangExpr::IDExpr(e) => e.id == *id, 499 | S::LangExpr::DataExpr(e) => is_recursive_exprs(&e.exprs, id), 500 | S::LangExpr::MatchExpr(e) => { 501 | if is_recursive_expr(&e.expr, id) { 502 | return true; 503 | } 504 | 505 | for c in e.cases.iter() { 506 | if is_recursive_expr(&c.expr, id) { 507 | return true; 508 | } 509 | } 510 | 511 | false 512 | } 513 | S::LangExpr::ApplyExpr(e) => is_recursive_exprs(&e.exprs, id), 514 | S::LangExpr::ListExpr(e) => is_recursive_exprs(&e.exprs, id), 515 | S::LangExpr::TupleExpr(e) => is_recursive_exprs(&e.exprs, id), 516 | S::LangExpr::LambdaExpr(e) => is_recursive_expr(&e.expr, id), 517 | } 518 | } 519 | 520 | fn is_recursive_exprs(exprs: &[S::LangExpr], id: &str) -> bool { 521 | for e in exprs.iter() { 522 | if is_recursive_expr(e, id) { 523 | return true; 524 | } 525 | } 526 | false 527 | } 528 | -------------------------------------------------------------------------------- /docs/index.ja.adoc: -------------------------------------------------------------------------------- 1 | = BLisp: Lispっぽい静的型付け言語 2 | Yuuki Takano 3 | v0.4.0, 2023-02 4 | :doctype: article 5 | :toc: 6 | :sectnums: 7 | :lang: ja 8 | :encoding: utf-8 9 | :stem: latexmath 10 | :source-highlighter: pygments 11 | 12 | 13 | BLispは静的型付けされたLispライクなプログラミング言語で、no_std環境用のエフェクトシステムを採用しています。BLispは関数型プログラミング言語の高階関数のような高階のRPCをサポートしています。 14 | 15 | * https://github.com/ytakano/blisp[GitHubリポジトリ] 16 | * https://crates.io/crates/blisp[BLispのcrates.io] 17 | 18 | 本リポジトリではライブラリクレートのみを提供しています。BLispを利用するには https://github.com/ytakano/blisp-repl[blisp-repl] か、おもちゃのOSである https://github.com/ytakano/baremetalisp[baremetalisp] を参照してください。 19 | 20 | https://ytakano.github.io/blisp/[English version is here.] 21 | 22 | .no_stdで動くBLisp 23 | image:https://cdn-ak.f.st-hatena.com/images/fotolife/y/ytakano/20210221/20210221155657.gif[no_stdで動くBLisp] 24 | 25 | == 特徴 26 | 27 | * 代数的データ型 28 | * ジェネリクス 29 | * 型推論 30 | * IOと純粋な関数を分離するためのエフェクトシステム 31 | * 多倍長整数 32 | * no_std環境のサポート 33 | 34 | == 値 35 | 36 | .values 37 | [source, lisp] 38 | ---- 39 | `A` ; 文字リテラル 40 | "Hello" ; 文字列リテラル 41 | 144 ; 整数値 42 | 0xabcDEF ; 16進数 43 | 0o777 ; 8進数 44 | 0b1001 ; 2進数 45 | true ; 真偽値 46 | false ; 真偽値 47 | [true 10] ; タプル 48 | [] ; 空のタプル 49 | '(1 2 3) ; リスト 50 | '() ; 空のリスト、Nil 51 | ---- 52 | 53 | == 基本型 54 | 55 | .types 56 | [source, lisp] 57 | ---- 58 | Char ; 文字型 59 | String ; 文字列型 60 | Int ; 整数型 61 | Bool ; 真偽値型 62 | '(Int) ; Int型のリスト型 63 | [Int Bool] ; Int型とBool型のタプル型 64 | (Pure (-> (Int Int) Bool)) ; Int型の値を2つとり、Bool型の値をリターンする純粋な関数 65 | (IO (-> (Int) [])) ; Int型の値をとり[]をリターンするIOのある関数 66 | ---- 67 | 68 | PureとIOは関数の効果です。IO関数内では、Pure関数とIO関数の両方を呼び出すことができます。しかし、Pure関数内では、Pure関数の呼び出しのみ許可されています。 69 | 70 | == 関数定義 71 | 72 | 関数はdefunやexportで定義することができます。"defun"はRustのeval関数からは呼び出せないローカル関数を定義します。 73 | 74 | 以下の2つの関数があるとしましょう。 75 | 76 | .defun 77 | [source, lisp] 78 | ---- 79 | (defun double (x) ; 関数名がdoubleでxは引数 80 | (Pure (-> (Int) Int)) ; 関数の型 81 | (* 2 x)) ; 関数の中身 82 | ---- 83 | 84 | .export 85 | [source, lisp] 86 | ---- 87 | (export quad (x) ; 関数名がquadで引数はx 88 | (Pure (-> (Int) Int)) ; 関数の型 89 | (double (double x))) ; 関数の中身 90 | ---- 91 | 92 | doubleはRustのevalからは呼び出せませんが、内部で定義された関数からは呼び出せます。quadはRustのevalから呼び出すことができ、内部的にdoubleを呼び出します。 93 | 94 | これはRustで実際に行うコードです。 95 | 96 | .Rust's eval 97 | [source, rust] 98 | ---- 99 | use blisp; 100 | 101 | fn eval(e: &str, ctx: &blisp::semantics::Context) { 102 | // evaluate expressions 103 | let exprs = match blisp::eval(e, ctx) { 104 | Ok(es) => es, 105 | Err(err) => { 106 | println!("error:{}:{}: {}", err.pos.line, err.pos.column, err.msg); 107 | return; 108 | } 109 | }; 110 | 111 | for r in exprs { 112 | match r { 113 | Ok(msg) => { 114 | println!("{}", msg); 115 | } 116 | Err(msg) => { 117 | println!("error: {}", msg); 118 | } 119 | } 120 | } 121 | } 122 | 123 | fn main() { 124 | // internal code 125 | let code = " 126 | (defun double (x) ; 関数名がdoubleでxは引数 127 | (Pure (-> (Int) Int)) ; 関数の型 128 | (* 2 x)) ; 関数の中身 129 | 130 | (export quad (x) ; 関数名がquadで引数はx 131 | (Pure (-> (Int) Int)) ; 関数の型 132 | (double (double x))) ; 関数の中身 133 | "; 134 | let exprs = blisp::init(code, vec![]).unwrap(); 135 | let ctx = blisp::typing(exprs).unwrap(); 136 | 137 | let e = "(double 10) ; エラー"; 138 | eval(e, &ctx); 139 | 140 | let e = "(quad 10) ; OK"; 141 | eval(e, &ctx); 142 | } 143 | ---- 144 | 145 | このコードは以下のように出力します。 146 | 147 | error:0:1: Typing Error: double is not defined 148 | 40 149 | 150 | == 算術演算 151 | 152 | .基本 153 | [source, lisp] 154 | ---- 155 | ; (Pure (-> (Int Int) Int)) 156 | (+ 10 20) 157 | (- 30 40) 158 | (* 6 100) 159 | (/ 100 2) 160 | (% 10 3) 161 | ---- 162 | 163 | == 真偽値演算 164 | 165 | .logical 166 | [source, lisp] 167 | ---- 168 | ; (Pure (-> (Bool Bool) Bool)) 169 | (and true false) 170 | (or true false) 171 | (xor true false) 172 | ---- 173 | 174 | .negation 175 | [source, lisp] 176 | ---- 177 | ; (Pure (-> (Bool) Bool)) 178 | (not true) 179 | ---- 180 | 181 | == 比較演算 182 | 183 | =, !=, <, >, \<=, >= といった関数は、同じ型の2つの値に対して適用できます。 184 | 185 | .comparison between 2 values whose types are same 186 | [source, lisp] 187 | ---- 188 | ; (Pure (-> (t t) Bool)) 189 | (= 4 4) ; true 190 | (!= 4 4) ; false 191 | (= "Hello" "Hello") ; true 192 | (= (Some 1) (Some 2)) ; false 193 | (< 6 7) 194 | (> 6 7) 195 | (<= 30 40) 196 | (>= 30 40) 197 | (< "Hello" "World") 198 | (<= (Some 1) (Some 2)) 199 | ---- 200 | 201 | _eq_, _neq_, _lt_, _gt_, _leq_, _geq_ といった関数は、異なる型同士の値でも比較可能です。 202 | 203 | .comparison between any 2 values 204 | [source, lisp] 205 | ---- 206 | ; (Pure (-> (t1 t1) Bool)) 207 | (geq (Some 1) "Hello") ; (Some 1)は"Hello"より大きい、もしくは等しいか? 208 | (eq "Hello" 100) ; "Hello"と100は等しいか? 209 | (neq "Hello" 100) ; "Hello"と100は等しくないか? 210 | (lt 100 (Some 20)) ; 100は(Some 20)より小さいか? 211 | (gt 200 "Hello") ; 200は"Hello"より大きいか? 212 | ---- 213 | 214 | == ビット演算 215 | 216 | [source, lisp] 217 | ---- 218 | (band 1 0) ; ビット積 219 | (band 1 1) ; ビット積 220 | (bor 1 0) ; ビット和 221 | (bor 1 1) ; ビット和 222 | (bxor 1 0) ; ビット排他的論理和 223 | ---- 224 | 225 | .ビットシフト 226 | [source, lisp] 227 | ---- 228 | ; (Pure (-> (Int Int) (Option Int))) 229 | (<< 8 4) ; (Some 128) 230 | (>> 128 4) ; (Some 8) 231 | (>> -128 4) ; (Some -8) 232 | ---- 233 | 234 | 2番目の引数が2^64^以上の場合はNoneをリターンします。 235 | 236 | == 数学的演算 237 | 238 | [source, lisp] 239 | ---- 240 | ; (Pure (-> (Int Int) (Option Int))) 241 | (pow 10 20) ; (Some 100000000000000000000) namely 10^20 242 | 243 | ; (Pure (-> (Int) (Option Int))) 244 | (sqrt 16) ; (Some 4) 245 | ---- 246 | 247 | powの指数部が2^32^以上の場合は、powはNoneをリターンします。 248 | 249 | sqrtの引数が0以下の場合は、sqrtはNoneをリターンします。 250 | 251 | == 代数的データ型 252 | 253 | 代数的データ型は以下のように定義できます。 254 | 255 | [source, lisp] 256 | ---- 257 | ; in BLisp 258 | (data Gender ; 型名 259 | Male ; 値 260 | Female) ; 値 261 | ---- 262 | 263 | 型名とその値の最初の文字は大文字でなければなりません。これはRustの以下のコードと同等です。 264 | 265 | [source, rust] 266 | ---- 267 | // in Rust 268 | enum Gender { 269 | Male, 270 | Female 271 | } 272 | ---- 273 | 274 | 各要素は以下のような値を持つことができます。 275 | 276 | [source, lisp] 277 | ---- 278 | ; in BLisp 279 | (data Dim2 280 | (Dim2 Int Int)) ; Dim2は2つのInt型の値を持つ 281 | ---- 282 | 283 | Dim2は以下のようにインスタンス化することができます。 284 | 285 | [source, lisp] 286 | ---- 287 | (Dim2 10 20) 288 | ---- 289 | 290 | この型はRustの以下の型と同等です。 291 | 292 | [source, rust] 293 | ---- 294 | // in Rust 295 | use num_bigint::BigInt; 296 | enum Dim2 { 297 | Dim2(BigInt, BigInt) 298 | } 299 | ---- 300 | 301 | == ジェネリクス 302 | 303 | Option型とResult型は内部で定義されています。 304 | 305 | [source, lisp] 306 | ---- 307 | (data (Option t) 308 | (Some t) 309 | None) 310 | 311 | (data (Result t e) 312 | (Ok t) 313 | (Err e)) 314 | ---- 315 | 316 | 317 | _t_ と _e_ は型変数です。このコードは、Rustの以下のコードと同等です。 318 | 319 | [source, rust] 320 | ---- 321 | // in Rust 322 | enum Option { 323 | Some(T), 324 | None, 325 | } 326 | 327 | enum Result { 328 | Ok(T), 329 | Err(E), 330 | } 331 | ---- 332 | 333 | リスト型は以下のような組み込み型です。 334 | 335 | [source, lisp] 336 | ---- 337 | (data (List t) 338 | (Cons t (List t)) 339 | Nil) 340 | ---- 341 | 342 | したがって、以下の2つのリストは等価です。 343 | 344 | [source, lisp] 345 | ---- 346 | (Cons 1 (Cons 2 (Cons 3 Nil))) 347 | '(1 2 3) 348 | ---- 349 | 350 | == ジェネリック関数 351 | 352 | _car_ と _cdr_ は内部的に定義されたジェネリック関数です。これらの定義は以下の通りです。 353 | 354 | [source, lisp] 355 | ---- 356 | (export car (x) (Pure (-> ('(t)) (Option t))) 357 | (match x 358 | ((Cons n _) (Some n)) 359 | (_ None))) 360 | 361 | (export cdr (x) (Pure (-> ('(t)) '(t))) 362 | (match x 363 | ((Cons _ l) l) 364 | (_ '()))) 365 | ---- 366 | 367 | t_は型変数です。これらの関数は以下のように使うことができます。 368 | 369 | [source, lisp] 370 | ---- 371 | (car '(3 8 9)) ; returns (Some 3) 372 | (cdr '(8 10 4)) ; returns '(10 4) 373 | ---- 374 | 375 | 通常変数と型変数の最初の文字は小文字でなければなりません。 376 | 377 | == If式 378 | 379 | 単純です. 380 | 381 | [source, lisp] 382 | ---- 383 | (if (< 10 20) 384 | '(1 2 3) 385 | '()) 386 | ---- 387 | 388 | == Match式 389 | 390 | リストは以下のようにマッチさせることができます。 391 | 392 | [source, lisp] 393 | ---- 394 | (match '(1 2 3) 395 | ((Cons n _) n) 396 | ('() 0)) 397 | ---- 398 | 399 | この、 400 | 401 | (Cons n _) 402 | 403 | という式はパターンです。 404 | パターンが '(1 2 3) にマッチした場合、1は可変変数 _n_ に代入されます。そうすると、_n_ つまり1が返されます。 405 | 406 | タプルのパターンマッチングの例です。 407 | 408 | [source, lisp] 409 | ---- 410 | (match [1 3] 411 | ([x y] [y x])) 412 | ---- 413 | 414 | このコードはタプルの第1要素と第2要素を入れ替えます。 415 | 416 | 整数値はパターンマッチングにも使用できます。 417 | 418 | [source, lisp] 419 | ---- 420 | (match 20 421 | (20 true) 422 | (_ false)) 423 | ---- 424 | 425 | より複雑な例としては、以下のようなものがあります。 426 | 427 | [source, lisp] 428 | ---- 429 | (match [(Some 10) true] 430 | ([(Some 10) false] 1) 431 | ([(Some 10) true] 2) 432 | (_ 0)) 433 | ---- 434 | 435 | BLispはパターンを網羅的にチェックします。そのため、以下のコードは拒否されます。 436 | 437 | [source, lisp] 438 | ---- 439 | (match '(1 2) 440 | ('() 0)) 441 | ---- 442 | 443 | == Let式 444 | 445 | 変数のバインドには、以下のようにLet式を使用します。 446 | 447 | [source, lisp] 448 | ---- 449 | (let ((x 10) (y 20)) ; x is 10, y is 20 450 | (* x y)) 451 | 452 | (let ((x 10) (x (* x x)) (x (* x x))) ; x = 10, x = x * x, x = x * x 453 | x) 454 | ---- 455 | 456 | また、以下のように分配束縛を行うこともできます。 457 | 458 | [source, lisp] 459 | ---- 460 | (let (((Some x) (Some 10))) ; x is 10 461 | (* x 2)) 462 | 463 | (let (([x y] [10 20])) ; x is 10, y is 20 464 | (* x y)) 465 | ---- 466 | 467 | == ラムダ式 468 | 469 | ラムダ式は以下のように定義されます。 470 | 471 | [source, lisp] 472 | ---- 473 | (lambda (x y) (* x y)) 474 | ---- 475 | 476 | このラムダは2つの整数を受け取り、それらの乗算を返します。これに引数を適用するのは次のように簡単に行えます。 477 | 478 | [source, lisp] 479 | ---- 480 | ((lambda (x y) (* x y)) 10 20) 481 | ---- 482 | 483 | すべてのラムダ式は純粋です。よって、ラムダ式からIO関数を呼び出すことはできません。 484 | 485 | _map_ と _fold_ 関数は内部的に以下のように定義されています。 486 | 487 | [source, lisp] 488 | ---- 489 | (export map (f x) (Pure (-> ((Pure (-> (a) b)) '(a)) '(b))) 490 | (match x 491 | ((Cons h l) (Cons (f h) (map f l))) 492 | (_ '()))) 493 | 494 | (export fold (f init x) (Pure (-> ((Pure (-> (a b) b)) b '(a)) b)) 495 | (match x 496 | ((Cons h l) (fold f (f h init) l)) 497 | (_ init))) 498 | ---- 499 | 500 | _map_ を使うと、以下のようにリストの要素に関数を適用することができます。 501 | 502 | [source, lisp] 503 | ---- 504 | ; それぞれをの要素を2乗 505 | (let ((l '(1 2 3)) 506 | (f (lambda (x) (* x x)))) 507 | (map f l)) 508 | ---- 509 | 510 | _fold_ を使用して、リストの要素にまたがって計算することができます。例えば、合計は以下のように計算できます。 511 | 512 | [source, lisp] 513 | ---- 514 | ; 合計 515 | (let ((l '(20 50 60)) 516 | (f (lambda (x y) (+ x y)))) 517 | (fold f 0 l)) ; 0 is an initial value 518 | ---- 519 | 520 | 当然、これは以下のようにも記述できます。 521 | 522 | [source, lisp] 523 | ---- 524 | ; summation 525 | (fold + 0 '(20 50 60)) 526 | ---- 527 | 528 | == 文字列と文字 529 | 530 | _chars_ はStringから(List Char)へ変換します。 531 | 532 | [source, lisp] 533 | ---- 534 | ; (Pure (-> (String) (List Char))) 535 | (chars "Hello") ; '(`H` `e` `l` `l` `o`) 536 | ---- 537 | 538 | _str_ は(List Char)からStringへ変換します。 539 | 540 | [source, lisp] 541 | ---- 542 | ; (Pure (-> ((List Char)) String)) 543 | (str '(`H` `e` `l` `l` `o`)) ; "Hello" 544 | ---- 545 | 546 | == 外部関数呼び出し 547 | 548 | _blisp::embedded_ は、外部関数呼び出し用のマクロです。 549 | このマクロを利用すると、Rustの関数をBLispから容易に呼び出せるようになります。 550 | 551 | たとえば、はじめに、Rustの関数を以下のように定義します。 552 | 553 | [source, rust] 554 | ---- 555 | use blisp::embedded; 556 | use num_bigint::{BigInt, ToBigInt}; 557 | 558 | #[embedded] 559 | fn add_four_ints(a: BigInt, b: (BigInt, BigInt), c: Option) -> Result { 560 | let mut result = a + b.0 + b.1; 561 | if let Some(n) = c { 562 | result += n; 563 | } 564 | 565 | Ok(result) 566 | } 567 | ---- 568 | 569 | _blisp::embedded_ マクロは外部関数呼び出し用の型定義を生成します。 570 | この関数は、以下のようにBLispから呼び出せます。 571 | 572 | [source, lisp] 573 | ---- 574 | (export call_add_four_ints (n) 575 | (IO (-> ((Option Int)) (Result Int String))) 576 | (add_four_ints 1 [2 3] n)) 577 | ---- 578 | 579 | 外部関数を登録するためには、以下のように、 _embedded_ によって生成される型定義のベクタを 580 | _blisp::init_ に渡します。 581 | 582 | [source, rust] 583 | ---- 584 | // add_for_ints 585 | let code = "(export call_add_four_ints (n) 586 | (IO (-> ((Option Int)) (Result Int String))) 587 | (add_four_ints 1 [2 3] n) 588 | )"; 589 | let exprs = blisp::init(code, vec![Box::new(AddFourInts)]).unwrap(); 590 | let ctx = blisp::typing(exprs).unwrap(); 591 | let result = blisp::eval("(call_add_four_ints (Some 4))", &ctx).unwrap(); 592 | ---- 593 | 594 | ここでは、関数名が _add_four_ints_ のため、そのキャメルケースの _AddFourInts_ 595 | を _Box_ と _Vec_ に包んで _blisp::init_ に渡さなければなりません。 596 | 597 | Rustの外部関数は以下に示される型のみ引数と返り値で利用可能です。 598 | _Vec_ のような他の型はサポート外ですが、 599 | _Vec>_ のような型はOKです。 600 | BLispとRustの間の型変換は _embedded_ マクロが生成する関数によって自動的に行われます。 601 | 602 | .Type Conversion between BLisp and Rust 603 | |=== 604 | |BLisp | Rust 605 | 606 | |_Int_ | _BigInt_ 607 | |_Bool_ | _bool_ 608 | |_Char_ | _char_ 609 | |_String_ | _String_ 610 | |_'(T)_ | _Vec_ 611 | |_[T0, T1]_ | _(T0, T1)_ 612 | |_(Option T)_ | _Option_ 613 | |_(Result T E)_ | _Result_ 614 | |=== 615 | 616 | == Coqへのトランスパイラ (実験的) 617 | 618 | BLispは実験的にCoqへのトランスパイラを実装しています。 619 | トランスパイラは、以下のように _blisp::transpile_ を呼び出すことで実行されます。 620 | 621 | [source, coq] 622 | ---- 623 | let expr = " 624 | (defun snoc (l y) 625 | (Pure (-> ( 626 | '(t) t) 627 | '(t))) 628 | (match l 629 | (nil (Cons y nil)) 630 | ((Cons h b) (Cons h (snoc b y))))) 631 | 632 | (defun rev (l) 633 | (Pure (-> ( 634 | '(t)) 635 | '(t))) 636 | (match l 637 | (nil nil) 638 | ((Cons h t) (snoc (rev t) h)))) 639 | "; 640 | let exprs = blisp::init(expr, vec![]).unwrap(); 641 | let ctx = blisp::typing(exprs).unwrap(); 642 | 643 | println!("{}", blisp::transpile(&ctx)); 644 | ---- 645 | 646 | これは以下のようなCoqのコードを出力します。 647 | この出力には、BLispのプレリュードも含まれます。 648 | 649 | [source, coq] 650 | ---- 651 | Require Import ZArith. 652 | Require Import Coq.Lists.List. 653 | 654 | Inductive Option (t: Type): Type := 655 | | Some (x0: t) 656 | | None. 657 | 658 | Arguments Some{t}. 659 | Arguments None{t}. 660 | 661 | Inductive Result (t e: Type): Type := 662 | | Ok (x0: t) 663 | | Err (x0: e). 664 | 665 | Arguments Ok{t e}. 666 | Arguments Err{t e}. 667 | 668 | Definition car {t: Type} (x: list t): Option t := 669 | match x with 670 | | (cons n _) => (Some n) 671 | | _ => (None) 672 | end. 673 | 674 | Definition cdr {t: Type} (x: list t): list t := 675 | match x with 676 | | (cons _ l) => l 677 | | _ => nil 678 | end. 679 | 680 | Definition filter {t: Type} (f: t -> bool) (x: list t): list t := 681 | (reverse (filter' f x nil ) ). 682 | 683 | Fixpoint filter' {t: Type} (f: t -> bool) (x l: list t): list t := 684 | match x with 685 | | (cons h a) => match (f h ) with 686 | | true => (filter' f a (cons h l) ) 687 | | false => (filter' f a l ) 688 | end 689 | | _ => l 690 | end. 691 | 692 | Fixpoint fold {a b: Type} (f: a -> b -> b) (init: b) (x: list a): b := 693 | match x with 694 | | (cons h l) => (fold f (f h init ) l ) 695 | | _ => init 696 | end. 697 | 698 | Fixpoint map {a b: Type} (f: a -> b) (x: list a): list b := 699 | match x with 700 | | (cons h l) => (cons (f h ) (map f l )) 701 | | _ => nil 702 | end. 703 | 704 | Fixpoint rev {t: Type} (l: list t): list t := 705 | match l with 706 | | nil => nil 707 | | (cons h t) => (snoc (rev t ) h ) 708 | end. 709 | 710 | Definition reverse {t: Type} (x: list t): list t := 711 | (reverse' x nil ). 712 | 713 | Fixpoint reverse' {t: Type} (x l: list t): list t := 714 | match x with 715 | | (cons h a) => (reverse' a (cons h l) ) 716 | | _ => l 717 | end. 718 | 719 | Fixpoint snoc {t: Type} (l: list t) (y: t): list t := 720 | match l with 721 | | nil => (cons y nil) 722 | | (cons h b) => (cons h (snoc b y )) 723 | end. 724 | ---- 725 | 726 | このトランスパイラは実験的なものであることに注意してください。 727 | よって、いくつかの出力はCoqが解釈できません。 728 | その場合は、手動でソースコードを修正してください。 729 | 簡単にできるはずです。 730 | 731 | == 例 732 | 733 | === リバース 734 | 735 | _reverse_ は内部で定義されている関数です。この関数はリストを反転します。 736 | 737 | [source, lisp] 738 | ---- 739 | (reverse '(1 2 3 4 5 6 7 8 9)) 740 | ---- 741 | 742 | このコードの出力は以下の通りです。 743 | 744 | '(9 8 7 6 5 4 3 2 1) 745 | 746 | === Filter 747 | 748 | _filter_ は内部で定義されている関数です。この関数はリストの中身をフィルターします。 749 | 750 | [source, lisp] 751 | ---- 752 | (filter (lambda (x) (= (% x 2) 0)) '(1 2 3 4 5 6 7 8 9)) 753 | ---- 754 | 755 | このコードの出力は以下の通りです。 756 | 757 | '(2 4 6 8) 758 | 759 | _filter_' の型は以下の通りです。 760 | 761 | [source, lisp] 762 | ---- 763 | (Pure (-> 764 | ((Pure (-> (t) Bool)) ; 関数を引数にとる 765 | '(t)) ; リストを引数にとる 766 | '(t))) ; リストをリターン 767 | ---- 768 | 769 | === 階乗 770 | 771 | 末尾呼び出し版の階乗関数は、以下のように実装できます。 772 | 773 | [source, lisp] 774 | ---- 775 | (export factorial (n) (Pure (-> (Int) Int)) 776 | (fact n 1)) 777 | 778 | (defun fact (n total) (Pure (-> (Int Int) Int)) 779 | (if (<= n 0) 780 | total 781 | (fact (- n 1) (* n total)))) 782 | ---- 783 | 784 | この関数は以下のように呼び出すことができます。 785 | 786 | >> (factorial 10) 787 | 3628800 788 | >> 789 | >> (factorial 1000) 790 | 402387260077093773543702433923003985719374864210714632543799910429938512398629020592044208486969404800479988610197196058631666872994808558901323829669944590997424504087073759918823627727188732519779505950995276120874975462497043601418278094646496291056393887437886487337119181045825783647849977012476632889835955735432513185323958463075557409114262417474349347553428646576611667797396668820291207379143853719588249808126867838374559731746136085379534524221586593201928090878297308431392844403281231558611036976801357304216168747609675871348312025478589320767169132448426236131412508780208000261683151027341827977704784635868170164365024153691398281264810213092761244896359928705114964975419909342221566832572080821333186116811553615836546984046708975602900950537616475847728421889679646244945160765353408198901385442487984959953319101723355556602139450399736280750137837615307127761926849034352625200015888535147331611702103968175921510907788019393178114194545257223865541461062892187960223838971476088506276862967146674697562911234082439208160153780889893964518263243671616762179168909779911903754031274622289988005195444414282012187361745992642956581746628302955570299024324153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690897968482590125458327168226458066526769958652682272807075781391858178889652208164348344825993266043367660176999612831860788386150279465955131156552036093988180612138558600301435694527224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136493273497565513958720559654228749774011413346962715422845862377387538230483865688976461927383814900140767310446640259899490222221765904339901886018566526485061799702356193897017860040811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614839657382291123125024186649353143970137428531926649875337218940694281434118520158014123344828015051399694290153483077644569099073152433278288269864602789864321139083506217095002597389863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826280956121450994871701244516461260379029309120889086942028510640182154399457156805941872748998094254742173582401063677404595741785160829230135358081840096996372524230560855903700624271243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 791 | >> 792 | >> (factorial 100) 793 | 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000 794 | >> 795 | >> (factorial 500) 796 | 1220136825991110068701238785423046926253574342803192842192413588385845373153881997605496447502203281863013616477148203584163378722078177200480785205159329285477907571939330603772960859086270429174547882424912726344305670173270769461062802310452644218878789465754777149863494367781037644274033827365397471386477878495438489595537537990423241061271326984327745715546309977202781014561081188373709531016356324432987029563896628911658974769572087926928871281780070265174507768410719624390394322536422605234945850129918571501248706961568141625359056693423813008856249246891564126775654481886506593847951775360894005745238940335798476363944905313062323749066445048824665075946735862074637925184200459369692981022263971952597190945217823331756934581508552332820762820023402626907898342451712006207714640979456116127629145951237229913340169552363850942885592018727433795173014586357570828355780158735432768888680120399882384702151467605445407663535984174430480128938313896881639487469658817504506926365338175055478128640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -------------------------------------------------------------------------------- /specification/typing.tex: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | 3 | \usepackage{amsmath,amssymb} 4 | \usepackage[dvipdfmx]{hyperref,graphicx} 5 | \usepackage{listings} 6 | 7 | \lstset{% 8 | language={lisp}, 9 | basicstyle={\small\ttfamily},% 10 | identifierstyle={\small},% 11 | commentstyle={\small\itshape},% 12 | keywordstyle={\small\bfseries},% 13 | ndkeywordstyle={\small},% 14 | stringstyle={\small\ttfamily}, 15 | frame={tb}, 16 | keepspaces=true, 17 | breaklines=true, 18 | columns=[l]{fullflexible},% 19 | numbers=left,% 20 | xrightmargin=0zw,% 21 | xleftmargin=3zw,% 22 | numberstyle={\scriptsize},% 23 | stepnumber=1, 24 | numbersep=1zw,% 25 | lineskip=-0.5ex% 26 | } 27 | 28 | \title{Typing Rule of Baremetalisp} 29 | \author{Yuuki Takano\\ ytakano@wide.ad.jp} 30 | 31 | \begin{document} 32 | 33 | \maketitle 34 | 35 | \section{Introduction} 36 | 37 | In this paper, I will formally describe the typing rule of Baremetalisp, 38 | which is a well typed Lisp for trusted execution environment. 39 | 40 | \begin{table}[tb] 41 | \centering 42 | \caption{Notation} 43 | \label{tab:notation} 44 | \begin{tabular}{rl} 45 | $A \Rightarrow B$ & logical implication (if A then B)\\ 46 | $e$ & expression \\ 47 | $z$ & integer literal such as 10, -34, 112 \\ 48 | $x$ & variable \\ 49 | $t$ & type variable \\ 50 | $D$ & type name of user defined data \\ 51 | $L$ & label of user defined data \\ 52 | $E$ & effect \\ 53 | $E_\mathcal{T}: T \rightarrow E$ & effect of type \\ 54 | $\mathcal{T}$ & type \\ 55 | $C$ & type constraint \\ 56 | $io(C): C \rightarrow \mathtt{Bool}$ & does $C$ contain $\mathtt{IO}$ functions? \\ 57 | $\Gamma$ & context \\ 58 | $\mathcal{P}$ & pattern \\ 59 | $\mathcal{P}_{let}$ & pattern of let expression \\ 60 | $\mathcal{T}_1 \equiv_\alpha \mathcal{T}_2$ & $\mathcal{T}_1$ and $\mathcal{T}_2$ are $\alpha$-equivalent \\ 61 | $\mathcal{S} : t \rightarrow \mathcal{T}$ & substitution from type variable to type\\ 62 | $\mathcal{T} \cdot \mathcal{S}$ & apply $\mathcal{S}$ to $\mathcal{T}$ \\ 63 | $\mathcal{X}$ & set of $t$ \\ 64 | $FV_\mathcal{T} : \mathcal{T} \rightarrow \mathcal{X}$ & function from $\mathcal{T}$ to its free variables\\ 65 | $FV_\Gamma : \Gamma \rightarrow \mathcal{X}$ & function from $\Gamma$ to its free variables\\ 66 | $Size : L \rightarrow \mathtt{Int}$ & the number of labels $L$'s type has \\ 67 | $\Gamma \vdash e : \mathcal{T}\ |_\mathcal{X}\ C$ & $e$'s type is deduced as $\mathcal{T}$ from $\Gamma$ \\ 68 | & under constraint $C$ and type variables $\mathcal{X}$ 69 | \end{tabular} 70 | \end{table} 71 | 72 | \begin{figure}[tb] 73 | \centering 74 | \begin{tabular}{rrll} 75 | $\mathcal{C}$ & := & $\mathcal{T} = \mathcal{T}, \mathcal{C}$ & \bf{type constraint} \\ 76 | & $|$ & $\varnothing$ \\ \\ 77 | 78 | $\Gamma$ & := & & \bf{context} \\ 79 | & & $x: \mathcal{T}, \Gamma$ & type of variable \\ 80 | & $|$ & $L: \mathcal{T}, \Gamma$ & type of label \\ 81 | & $|$ & $L_{nth}: \mathcal{T}, \Gamma$ & n-th type of label's element \\ 82 | & $|$ & $\varnothing$ \\ \\ 83 | 84 | $E$ & := & $\mathtt{Pure}\ |\ \mathtt{IO}$ & \bf{effect} \\ \\ 85 | 86 | $\mathcal{T}$ & := & & \bf{type} \\ 87 | & & $\mathtt{Int}$ \\ 88 | & $|$ & $\mathtt{Bool}$ \\ 89 | & $|$ & $'(\mathcal{T})$ & list type \\ 90 | & $|$ & $[\mathcal{T}+]$ & tuple type \\ 91 | & $|$ & $D$ & user defined type \\ 92 | & $|$ & $(D\ \mathcal{T}+)$ & user defined type with type arguments \\ 93 | & $|$ & $(E\ (\rightarrow\ (\mathcal{T}*)\ \mathcal{T}))$ & function type \\ 94 | & $|$ & $t$ & type variable \\ \\ 95 | 96 | $\mathcal{P}$ & := & & \bf{pattern} \\ 97 | & & $x$ & variable \\ 98 | & $|$ & $L$ & label \\ 99 | & $|$ & $(L\ \mathcal{P}+)$ & label with patterns \\ 100 | & $|$ & $'()$ & empty list \\ 101 | & $|$ & $[\mathcal{P}+]$ & tuple \\ \\ 102 | 103 | $\mathcal{P}_{let}$ & := & & \bf{patten for let} \\ 104 | & & $x$ & variable \\ 105 | & $|$ & $(L\ \mathcal{P}_{let}+)$ & label with patterns \\ 106 | & $|$ & $[\mathcal{P}_{let}+]$ & tuple \\ 107 | \end{tabular} 108 | \caption{Syntax} 109 | \label{fig:syntax} 110 | \end{figure} 111 | 112 | \section{Notation and Syntax} 113 | 114 | Table~\ref{tab:notation} and Fig.~\ref{fig:syntax} shows notation used in this paper 115 | and syntax for the typing rule, respectively. 116 | 117 | \begin{lstlisting}[caption=Example of variable and type,label=src:vars] 118 | (defun add (a b) (Pure (-> (Int Int) Int)) 119 | (+ a b)) 120 | \end{lstlisting} 121 | 122 | $x$ is a variable. 123 | For example, $x \in \{a, b\}$ in Listing~\ref{src:vars}. 124 | $\mathcal{T}$ is a type. 125 | For example, $\mathcal{T} \in \{\mathtt{Int}, (\rightarrow\ (\mathtt{Int}\ \mathtt{Int})\ \mathtt{Int})\}$ 126 | in Listing~\ref{src:vars}. 127 | $(\rightarrow\ (\mathtt{Int}\ \mathtt{Int})\ \mathtt{Int})$ is a function type which takes 2 integer values 128 | and return 1 integer value. 129 | $\mathtt{Pure}$ in Listing~\ref{src:vars} denotes the effect of the function but I just ignore it now. 130 | Function effects will be described in Sec.~\ref{sec:effect}. 131 | $\mathcal{T}$ can be other forms as described in Fig.~\ref{fig:syntax} such as 132 | $\mathtt{Bool}$, $'(\mathtt{Int})$, $[\mathtt{Bool}\ \mathtt{Int}]$, $(\mathrm{List}\ a)$, $(\mathrm{List}\ \mathtt{Int})$. 133 | $C$ is a type constraint, which is a set of pairs of types. 134 | For example, $C = \{(\rightarrow\ (t_1\ t_2)\ t) = (\rightarrow\ (\mathtt{Int}\ \mathtt{Int})\ \mathtt{Int})\}$ deduced from 135 | Listing~\ref{src:vars} means $(\rightarrow\ (t_1\ t_2)\ t)$ and $(\rightarrow\ (\mathtt{Int}\ \mathtt{Int})\ \mathtt{Int})$ are 136 | semantically equal and every type variable in $C$, $t_1, t_2, t$, is thus $\mathtt{Int}$. 137 | $\Gamma$ is a map from variable and label to type. 138 | For example, $\Gamma = \{a : t_1, b : t_2, + : (\rightarrow\ (\mathtt{Int}\ \mathtt{Int})\ \mathtt{Int})\}$ 139 | in Listing~\ref{src:vars}. 140 | $\Gamma$ is called context generally, thus I call $\Gamma$ context in this paper. 141 | 142 | \begin{lstlisting}[caption=Example of user defined data type,label=src:cons] 143 | (data (List a) 144 | (Cons a (List a)) 145 | Nil) 146 | \end{lstlisting} 147 | 148 | $t$ is a type variable. 149 | For example, $t \in \{a\}$ in Listing~\ref{src:cons}. 150 | $L$ is a label for user defined type. 151 | For example, $L \in \{\mathrm{Cons}, \mathrm{Nil}\}$ in Listing~\ref{src:cons}. 152 | $D$ is user defined data. 153 | For example, $D \in \{\mathrm{List}\}$ in Listing~\ref{src:cons}. 154 | $\Gamma$ will hold mapping from labels in addition to variables. 155 | For example, 156 | $\Gamma = \{\mathrm{Cons} : (\mathrm{List}\ a), \mathrm{Nil} : (\mathrm{List}\ a), \mathrm{Cons}_{1st} : a, \mathrm{Cons}_{2nd} : (\mathrm{List}\ a)\}$ 157 | in Listing~\ref{src:cons}. 158 | 159 | $FV_\mathcal{T}$ and $FV_\Gamma$ are functions, which take $\mathcal{T}$ and $\Gamma$ and return free variables. 160 | For example, $FV_\mathcal{T}((\rightarrow\ (t_1\ t_2)\ t)) = \{t_1, t_2, t\}$ and 161 | \begin{equation*} 162 | \begin{aligned} 163 | &FV_\Gamma(\{a : t_1, b : t_1, + : (\rightarrow\ (\mathtt{Int}\ \mathtt{Int})\ \mathtt{Int})\}) \\ 164 | &=\{FV_\mathcal{T}(t_1), FV_\mathcal{T}(t_1), FV_\mathcal{T}((\rightarrow\ (\mathtt{Int}\ \mathtt{Int})\ \mathtt{Int}))\} \\ 165 | &=\{t_1, t_2\}. 166 | \end{aligned} 167 | \end{equation*} 168 | $\mathcal{T}_1 \equiv_\alpha \mathcal{T}_2$ denotes that $\mathcal{T}_1$ and $\mathcal{T}_2$ are $\alpha$-equivalent, 169 | which means $\mathcal{T}_1$ and $\mathcal{T}_2$ are semantically equal. 170 | For example, $(\rightarrow\ (t_1\ t_2)\ t) \equiv_\alpha (\rightarrow\ (t_{10}\ t_{11})\ t_{12})$. 171 | $\mathcal{S}$ is a substitution, which is a map from type variable to type, 172 | and it can be applied to $\mathcal{T}$ as $\mathcal{T} \cdot \mathcal{S}$. 173 | For example, if $\mathcal{S}(t_1) = [\mathtt{Bool}\ \mathtt{Int}], \mathcal{S}(t_2) = (\mathrm{List}\ t_3)$ then 174 | $(\rightarrow\ (t_1\ t_2)\ t) \cdot \mathcal{S} = (\rightarrow\ ([\mathtt{Bool}\ \mathtt{Int}]\ (\mathrm{List}\ t_3))\ t)$. 175 | 176 | \begin{lstlisting}[caption=Example of pattern matching,label=src:match] 177 | (data Dim2 (Dim2 Int Int)) 178 | 179 | (data (Maybe t) 180 | (Just t) 181 | Nothing) 182 | 183 | (defun match-let (a) (Pure (-> ((Maybe Dim2)) Int)) 184 | (match a 185 | ((Just val) 186 | (let (((Dim2 x y) val)) 187 | (+ x y))) 188 | (Nothing 189 | 0))) 190 | \end{lstlisting} 191 | $\mathcal{P}$ and $\mathcal{P}_{let}$ are pattern in match and let expressions. 192 | For example, in listings~\ref{src:match}, $(\mathrm{Just}\ val)$ and Nothing at line 9 and 12 are from $\mathcal{P}$ 193 | and $(\mathrm{Dim2}\ x\ y)$ at line 10 is from $\mathcal{P}_{let}$. 194 | $Size$ is a function which takes a label and return the number of labels the label's type has. 195 | For example, $Size(\mathrm{Just}) = Size(\mathrm{Nothing}) = 2$ because Maybe type has 2 labels 196 | and $Size(\mathrm{Dim2}) = 1$ because Dim2 type has 1 label in listings~\ref{src:match}. 197 | 198 | \begin{figure}[tb] 199 | \centering 200 | \begin{tabular}{rlrl} 201 | $\Gamma \vdash \mathtt{true} : \mathtt{Bool}\ |_\varnothing\ \varnothing$ & (T-True) & 202 | $\Gamma \vdash \mathtt{false} : \mathtt{Bool}\ |_\varnothing\ \varnothing$ & (T-False) \vspace{5mm} \\ 203 | 204 | $\dfrac{x : T \in \Gamma}{\Gamma \vdash x : T\ |_\varnothing\ \varnothing}$ & (T-Var) & 205 | $\Gamma \vdash z : \mathtt{Int}\ |_\varnothing\ \varnothing$ & (T-Num) \vspace{5mm} \\ 206 | 207 | $\dfrac{x : T' \in \Gamma \hspace{5mm} T' \cdot S \equiv_\alpha T}{\Gamma \vdash x : T\ |_{FV_\mathcal{T}(T)}\ \varnothing}$ & (T-VarPoly) \vspace{5mm} \\ 208 | 209 | \multicolumn{3}{r}{ 210 | $\dfrac{ 211 | \begin{aligned} 212 | &\Gamma_0 \vdash \mathcal{P}_{let} : \mathcal{T}_0\ |_{\mathcal{X}_0}\ C_0 \hspace{5mm} 213 | \Gamma \vdash e_1 : \mathcal{T}_1\ |_{\mathcal{X}_1}\ C_1 \hspace{5mm} 214 | \Gamma, \Gamma_0 \vdash e_2 : \mathcal{T}_2\ |_{\mathcal{X}_2}\ C_2\\ 215 | &\mathcal{X}_0 \cap \mathcal{X}_1 \cap \mathcal{X}_2 = \varnothing \hspace{5mm} 216 | C = C_0 \cup C_1 \cup C_2 \cup \{ \mathcal{T}_0 = \mathcal{T}_1 \} 217 | \end{aligned} 218 | }{ 219 | \Gamma \vdash (\mathtt{let1}\ \mathcal{P}_{let}\ e_1\ e_2) : \mathcal{T}_2\ |_{\mathcal{X}_0 \cup \mathcal{X}_1 \cup \mathcal{X}_2}\ C 220 | }$} & (T-Let1) \vspace{5mm} \\ 221 | 222 | \multicolumn{3}{r}{ 223 | $\dfrac{ 224 | \begin{aligned} 225 | &\Gamma \vdash e_1 : \mathcal{T}_1\ |_{\mathcal{X}_1}\ C_1 \hspace{5mm} \Gamma \vdash e_2 : \mathcal{T}_2\ |_{\mathcal{X}_2}\ C_2 \hspace{5mm} \Gamma \vdash e_3 : \mathcal{T}_3\ |_{\mathcal{X}_3}\ C_3 \\ 226 | &\mathcal{X}_1 \cap \mathcal{X}_2 \cap \mathcal{X}_3 = \varnothing \hspace{5mm} C = C_1 \cup C_2 \cup C_3 \cup \{ \mathcal{T}_1 = \mathtt{Bool}, \mathcal{T}_2 = T_3 \} 227 | \end{aligned} 228 | }{ 229 | \Gamma \vdash (\mathtt{if}\ e_1\ e_2\ e_3) : \mathcal{T}_2\ |_{\mathcal{X}_1 \cup \mathcal{X}_2 \cup \mathcal{X}_3}\ C 230 | }$} & (T-If) \vspace{5mm} \\ 231 | 232 | \multicolumn{3}{r}{ 233 | $\dfrac{ 234 | \begin{aligned} 235 | &\Gamma \vdash e_1 : \mathcal{T}_1\ |_{\mathcal{X}_1}\ C_1 \hspace{5mm} 236 | \Gamma \vdash e_2 : \mathcal{T}_2\ |_{\mathcal{X}_2}\ C_2 \land \cdots \land \Gamma \vdash e_n : \mathcal{T}_n\ |_{\mathcal{X}_n}\ C_n \\ 237 | &\{t\} \cap FV_\Gamma(\Gamma) = \varnothing \hspace{5mm} \{t\} \cap \mathcal{X}_1 \cap \cdots \cap \mathcal{X}_n = \varnothing\\ 238 | &\mathcal{X} = \{t\} \cup \mathcal{X}_1 \cup \cdots \cup \mathcal{X}_n \hspace{5mm} E = E_\mathcal{T}(\mathcal{T}_1) \\ 239 | &C = C_1 \cup \cdots \cup C_n \cup \{ \mathcal{T}_1 = (E\ (\rightarrow\ (\mathcal{T}_2\ \cdots\ \mathcal{T}_n)\ t)) \} 240 | \end{aligned} 241 | }{ 242 | \Gamma \vdash (e_1\ e_2\ \cdots\ e_n) : t\ |_\mathcal{X}\ C 243 | }$} & (T-App) \vspace{5mm} \\ 244 | 245 | \multicolumn{3}{r}{ 246 | $\dfrac{ 247 | \begin{aligned} 248 | &\Gamma \vdash e_0 : \mathcal{T}_0\ |_{\mathcal{X}_0}\ C_0 \\ 249 | &\Gamma, \Gamma_1 \vdash e_1 : \mathcal{T}_{e1}\ |_{\mathcal{X}_{e1}}\ C_{e1} \land \cdots \land \Gamma, \Gamma_n \vdash e_n : \mathcal{T}_{en}\ |_{\mathcal{X}_{en}}\ C_{en} \\ 250 | &\Gamma_1 \vdash \mathcal{P}_1 : \mathcal{T}_{p1}\ |_{\mathcal{X}_{p1}}\ C_{p1} \land \cdots \land \Gamma_n \vdash \mathcal{P}_{pn} : \mathcal{T}_{pn}\ |_{\mathcal{X}_{pn}}\ C_{pn} \\ 251 | &\mathcal{X}_0 \cap \mathcal{X}_{e1} \cap \cdots \cap \mathcal{X}_{en} \cap \mathcal{X}_{p1} \cap \cdots \cap \mathcal{X}_{pn} = \varnothing \\ 252 | &\mathcal{X} = \mathcal{X}_0 \cup \mathcal{X}_{e1} \cup \cdots \cup \mathcal{X}_{en} \cup \mathcal{X}_{p1} \cup \cdots \cup \mathcal{X}_{pn} \\ 253 | &\begin{aligned} 254 | C =\ &C_0 \cup C_{e1} \cup \cdots \cup C_{en} \cup C_{p1} \cup \cdots \cup C_{pn} \cup \\ 255 | &\{\mathcal{T}_0 = \mathcal{T}_{p1}, \cdots, \mathcal{T}_0 = \mathcal{T}_{pn}\} \cup 256 | \{\mathcal{T}_{e1} = \mathcal{T}_{e2}, \cdots, \mathcal{T}_{e1} = \mathcal{T}_{en}\} 257 | \end{aligned} 258 | \end{aligned} 259 | }{ 260 | \Gamma \vdash (\mathtt{match}\ e_0\ (\mathcal{P}_1\ e_1)\ \cdots\ (\mathcal{P}_n\ e_n)) : T_{e1}\ |_\mathcal{X}\ C 261 | }$} & (T-Match) 262 | \end{tabular} 263 | \caption{Typing rule (1/2)} 264 | \label{fig:typing1} 265 | \end{figure} 266 | 267 | \begin{figure}[tb] 268 | \centering 269 | \begin{tabular}{rlrl} 270 | $\Gamma \vdash\ '() :\ '(T)\ |_{\{T\}}\ \varnothing$ & (T-Nil) & 271 | $\dfrac{L : \mathcal{T}' \in \Gamma \hspace{5mm} \mathcal{T}' \cdot \mathcal{S} \equiv_\alpha \mathcal{T}}{\Gamma \vdash L : \mathcal{T}\ |_{FV_\mathcal{T}(\mathcal{T})}\ \varnothing}$ & (T-Label0) \vspace{5mm} \\ 272 | 273 | \multicolumn{3}{r}{ 274 | $\dfrac{ 275 | \begin{aligned} 276 | &\Gamma \vdash e_1 : T_1\ |_{\mathcal{X}_1}\ C_1 \land \cdots \land \Gamma \vdash e_n : T_n\ |_{\mathcal{X}_n}\ C_n \\ 277 | &\mathcal{X}_1 \cap \cdots \cap \mathcal{X}_n = \varnothing \hspace{5mm} 278 | \mathcal{X} = \mathcal{X}_1 \cup \cdots \cup \mathcal{X}_n \hspace{5mm} 279 | C = C_1 \cup \cdots \cup C_n 280 | \end{aligned} 281 | }{\Gamma \vdash [e_1\ \cdots\ e_n] : [T_1\ \cdots\ T_n]\ |_\mathcal{X}\ C}$ 282 | } & (T-Tuple) \vspace{5mm} \\ 283 | 284 | \multicolumn{3}{r}{ 285 | $\dfrac{ 286 | \begin{aligned} 287 | &\Gamma \vdash e_1 : T_1\ |_{\mathcal{X}_1}\ C_1 \land \cdots \land \Gamma \vdash e_n : T_n\ |_{\mathcal{X}_n}\ C_n \\ 288 | &\mathcal{X}_1 \cap \cdots \cap \mathcal{X}_n = \varnothing \hspace{5mm} 289 | \mathcal{X} = \mathcal{X}_1 \cup \cdots \cup \mathcal{X}_n \\ 290 | &C = C_1 \cup \cdots \cup C_n \cup \{T_1 = T_2, \cdots, T_1 = T_n \} 291 | \end{aligned} 292 | }{\Gamma \vdash\ '(e_1\ \cdots\ e_n) :\ '(T_1)\ |_\mathcal{X}\ C}$ 293 | } & (T-List) \vspace{5mm} \\ 294 | 295 | \multicolumn{3}{r}{ 296 | $\dfrac{ 297 | \begin{aligned} 298 | &\Gamma \vdash e_1 : \mathcal{T}_1\ |_{\mathcal{X}_1}\ C_1 \land \cdots \land 299 | \Gamma \vdash e_n : \mathcal{T}_n\ |_{\mathcal{X}_n}\ C_n \\ 300 | &L : \mathcal{T}_0' \in \Gamma \hspace{5mm} \mathcal{T}_0' \cdot \mathcal{S}\equiv_\alpha \mathcal{T}_0 \hspace{5mm} FV(\mathcal{T}_0) \cap \mathcal{X}_1 \cap \cdots \cap \mathcal{X}_n = \varnothing \\ 301 | &FV_\mathcal{T}(\mathcal{T}_0) \cap FV_\Gamma(\Gamma) = \varnothing \hspace{5mm} 302 | \mathcal{X} = FV(\mathcal{T}_0) \cup \mathcal{X}_1 \cup \cdots \cup \mathcal{X}_n \\ 303 | &L_{1st} : T_1' \in \Gamma \land \cdots \land L_{nth} : T_n' \in \Gamma \\ 304 | &C = C_1 \cup \cdots \cup C_n \cup \{T_1' \cdot \mathcal{S} = \mathcal{T}_1, \cdots, T_n' \cdot \mathcal{S} = \mathcal{T}_n\} \\ 305 | \end{aligned} 306 | }{ 307 | \Gamma \vdash (L\ e_1\ \cdots\ e_n) : \mathcal{T}_0\ |_{\mathcal{X}}\ C 308 | }$} & (T-Label) \vspace{5mm} \\ 309 | 310 | \multicolumn{3}{r}{ 311 | $\dfrac{ 312 | \begin{aligned} 313 | &\Gamma, x_1 : t_1, \cdots, x_n : t_n \vdash e : \mathcal{T}_0\ |_{\mathcal{X}}\ C_0 \hspace{5mm} \neg io(C)\\ 314 | &C = \{\mathcal{T} = (\mathtt{Pure}\ (\rightarrow\ (t_1\ \cdots\ t_n)\ \mathcal{T}_0))\} \cup C_0 315 | \end{aligned} 316 | } 317 | { 318 | \Gamma \vdash (\mathtt{lambda}\ (x_1\ \cdots\ x_n)\ e) : \mathcal{T}\ |_\mathcal{X}\ C 319 | }$ 320 | } & (T-Lambda) \vspace{5mm} \\ 321 | 322 | \multicolumn{3}{r}{ 323 | $\dfrac{ 324 | \begin{aligned} 325 | &\Gamma, x_1 : t_1, \cdots, x_n : t_n \vdash e : \mathcal{T}_0\ |_{\mathcal{X}}\ C_0 \hspace{5mm}\\ 326 | &E = E_\mathcal{T}(\mathcal{T}) \hspace{5mm} (E = \mathtt{Pure}) \Rightarrow \neg io(C)\\ 327 | &C = C_0 \cup \{\mathcal{T} = (E\ (\rightarrow (\mathcal{T}_1\ \cdots\ \mathcal{T}_n)\ \mathcal{T}_0)) \} 328 | \end{aligned} 329 | }{ 330 | \Gamma \vdash (\mathtt{defun}\ \mathrm{name}\ (x_1\ \cdots\ x_n)\ \mathcal{T}\ e) : \mathcal{T}\ |_\mathcal{X}\ C 331 | }$ 332 | } & (T-Defun) 333 | \end{tabular} 334 | \caption{Typing rule (2/2)} 335 | \label{fig:typing2} 336 | \end{figure} 337 | 338 | 339 | \begin{figure}[tb] 340 | \centering 341 | \begin{tabular}{rlrl} 342 | $\Gamma \vdash \mathtt{true} : \mathtt{Bool}\ |_\varnothing\ \varnothing$ & (P-True) & 343 | $\Gamma \vdash \mathtt{false} : \mathtt{Bool}\ |_\varnothing\ \varnothing$ & (P-False) \vspace{5mm} \\ 344 | 345 | $\dfrac{x : T \in \Gamma}{\Gamma \vdash x : T\ |_\varnothing\ \varnothing}$ & (P-Var) & 346 | $\Gamma \vdash z : \mathtt{Int}\ |_\varnothing\ \varnothing$ & (P-Num) \vspace{5mm} \\ 347 | 348 | $\Gamma \vdash\ '() :\ '(T)\ |_{\{T\}}\ \varnothing$ & (P-Nil) & 349 | $\dfrac{L : \mathcal{T}' \in \Gamma \hspace{5mm} \mathcal{T}' \cdot \mathcal{S} \equiv_\alpha \mathcal{T}}{\Gamma \vdash L : \mathcal{T}\ |_{FV_\mathcal{T}(\mathcal{T})}\ \varnothing}$ & (P-Label0) \vspace{5mm} \\ 350 | 351 | \multicolumn{3}{r}{ 352 | $\dfrac{ 353 | \begin{aligned} 354 | &\Gamma \vdash \mathcal{P}_1 : \mathcal{T}_1\ |_{\mathcal{X}_1}\ C_1 \land \cdots \land 355 | \Gamma \vdash \mathcal{P}_n : \mathcal{T}_n\ |_{\mathcal{X}_n}\ C_n \\ 356 | &L : \mathcal{T}_0' \in \Gamma \hspace{5mm} \mathcal{T}_0' \cdot \mathcal{S}\equiv_\alpha \mathcal{T}_0 \hspace{5mm} FV(\mathcal{T}_0) \cap \mathcal{X}_1 \cap \cdots \cap \mathcal{X}_n = \varnothing \\ 357 | &FV_\mathcal{T}(\mathcal{T}_0) \cap FV_\Gamma(\Gamma) = \varnothing \hspace{5mm} 358 | \mathcal{X} = FV(\mathcal{T}_0) \cup \mathcal{X}_1 \cup \cdots \cup \mathcal{X}_n \\ 359 | &L_{1st} : T_1' \in \Gamma \land \cdots \land L_{nth} : T_n' \in \Gamma \\ 360 | &C = C_1 \cup \cdots \cup C_n \cup \{T_1' \cdot \mathcal{S} = \mathcal{T}_1, \cdots, T_n' \cdot \mathcal{S} = \mathcal{T}_n\} \\ 361 | &Size(L) = 1\ \mbox{for only}\ P_{let} 362 | \end{aligned} 363 | }{ 364 | \Gamma \vdash (L\ \mathcal{P}_1\ \cdots\ \mathcal{P}_n) : \mathcal{T}_0\ |_{\mathcal{X}}\ C 365 | }$} & (P-Label) \vspace{5mm} \\ 366 | 367 | \multicolumn{3}{r}{ 368 | $\dfrac{ 369 | \begin{aligned} 370 | &\Gamma \vdash \mathcal{P}_1 : \mathcal{T}_1\ |_{\mathcal{X}_1}\ C_1 \land \cdots \land 371 | \Gamma \vdash \mathcal{P}_n : \mathcal{T}_n\ |_{\mathcal{X}_n}\ C_n \\ 372 | &\mathcal{X}_1 \cap \cdots \cap \mathcal{X}_n = \varnothing \hspace{5mm} 373 | \mathcal{X} = \mathcal{X}_1 \cup \cdots \cup \mathcal{X}_n \hspace{5mm} 374 | C = C_1 \cup \cdots \cup C_n 375 | \end{aligned} 376 | }{ 377 | \Gamma \vdash [\mathcal{P}_1\ \cdots\ \mathcal{P}_n] : [\mathcal{T}_1 \cdots \mathcal{T}_n]\ |_{\mathcal{X}}\ C 378 | }$} & (P-Tuple) \\ 379 | \end{tabular} 380 | \caption{Typing rule of pattern} 381 | \end{figure} 382 | 383 | \section{Typing Rule} 384 | 385 | In this section, I will introduce the typing rule of Baremetalisp. 386 | Before describing the rule, I introduce an assumption 387 | that there is no variable shadowing to make it simple. 388 | This means that every variable should be properly $\alpha$-converted 389 | by using the De Bruijn index technique or variable shadowing should be handled 390 | when implementing the type inference algorithm. 391 | 392 | Fig.~\ref{fig:typing1} and \ref{fig:typing2} are the typing rule of 393 | expressions and function definitions. 394 | 395 | \section{Effect} 396 | \label{sec:effect} 397 | 398 | \end{document} -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * $NUM := [0-9]* 3 | * $HEX := 0x[0-9a-fA-F]* 4 | * $OCT := 0o[0-9a-fA-F]* 5 | * $BIN := 0b[01]* 6 | * $BOOL := true | false 7 | * $STR := " string literal " 8 | * $CHAR := ' character literal ' 9 | * $ESCC := character * $ID := string 10 | * $LIST := '( $EXPRS ) 11 | * $TUPLE := [ $EXPRS ] 12 | * $APPLY := ( $EXPRS ) 13 | * $EXP := $HEX | $OCT | $BIN | $NUM | $BOOL | $ID | $LIST | $TUPLE | $APPLY 14 | * $EXPRS := $EXP $EXPRS | ∅ 15 | */ 16 | 17 | use super::Pos; 18 | use crate::FileType; 19 | use alloc::{ 20 | collections::linked_list::LinkedList, 21 | string::{String, ToString}, 22 | }; 23 | use core::{fmt::Display, usize}; 24 | use num_bigint::BigInt; 25 | use num_traits::Zero; 26 | 27 | #[derive(Debug)] 28 | pub struct SyntaxErr { 29 | pub pos: Pos, 30 | pub msg: &'static str, 31 | } 32 | 33 | pub struct Parser<'a> { 34 | pos: Pos, 35 | remain: &'a str, 36 | } 37 | 38 | #[derive(Debug, Clone)] 39 | pub enum Expr { 40 | Str(String, Pos), 41 | Char(char, Pos), 42 | Num(BigInt, Pos), 43 | ID(String, Pos), 44 | Bool(bool, Pos), 45 | List(LinkedList, Pos), 46 | Tuple(LinkedList, Pos), 47 | Apply(LinkedList, Pos), 48 | } 49 | 50 | impl Display for Expr { 51 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 52 | fn fmt_exprs( 53 | f: &mut core::fmt::Formatter<'_>, 54 | exprs: &LinkedList, 55 | ) -> core::fmt::Result { 56 | for (n, expr) in exprs.iter().enumerate() { 57 | if n == 0 { 58 | write!(f, "{expr}")?; 59 | } else { 60 | write!(f, " {expr}")?; 61 | } 62 | } 63 | 64 | Ok(()) 65 | } 66 | 67 | match self { 68 | Expr::Bool(val, _) => { 69 | write!(f, "{val}") 70 | } 71 | Expr::Char(val, _) => { 72 | write!(f, "'{val}'") 73 | } 74 | Expr::Str(val, _) => { 75 | write!(f, "\"{val}\"") 76 | } 77 | Expr::Num(val, _) => { 78 | write!(f, "{val}") 79 | } 80 | Expr::ID(val, _) => { 81 | write!(f, "{val}") 82 | } 83 | Expr::Apply(exprs, _) => { 84 | write!(f, "(")?; 85 | fmt_exprs(f, exprs)?; 86 | write!(f, ")") 87 | } 88 | Expr::List(exprs, _) => { 89 | write!(f, "'(")?; 90 | fmt_exprs(f, exprs)?; 91 | write!(f, ")") 92 | } 93 | Expr::Tuple(exprs, _) => { 94 | write!(f, "[")?; 95 | fmt_exprs(f, exprs)?; 96 | write!(f, "]") 97 | } 98 | } 99 | } 100 | } 101 | 102 | impl Expr { 103 | pub fn get_pos(&self) -> Pos { 104 | match self { 105 | Expr::Char(_, pos) => *pos, 106 | Expr::Num(_, pos) => *pos, 107 | Expr::ID(_, pos) => *pos, 108 | Expr::Bool(_, pos) => *pos, 109 | Expr::List(_, pos) => *pos, 110 | Expr::Tuple(_, pos) => *pos, 111 | Expr::Apply(_, pos) => *pos, 112 | Expr::Str(_, pos) => *pos, 113 | } 114 | } 115 | } 116 | 117 | impl<'a> Parser<'a> { 118 | pub fn new(code: &'a str, file_id: FileType) -> Parser<'a> { 119 | Parser { 120 | pos: Pos { 121 | file_id, 122 | line: 0, 123 | column: 0, 124 | }, 125 | remain: code, 126 | } 127 | } 128 | 129 | pub fn parse(&mut self) -> Result, SyntaxErr> { 130 | let mut exprs = LinkedList::new(); 131 | 132 | loop { 133 | self.skip_spaces(); 134 | if self.remain.is_empty() { 135 | return Ok(exprs); 136 | } 137 | 138 | exprs.push_back(self.parse_expr()?); 139 | } 140 | } 141 | 142 | fn parse_id_bool(&mut self) -> Result { 143 | let mut i = 0; 144 | 145 | for s in self.remain.chars() { 146 | if is_paren(s) || is_space(s) || s == ';' { 147 | break; 148 | } 149 | i += s.to_string().len(); 150 | } 151 | 152 | if i == 0 { 153 | Err(SyntaxErr { 154 | pos: self.pos, 155 | msg: "unexpected EOF", 156 | }) 157 | } else { 158 | let c = self.remain[..i].to_string(); 159 | self.remain = &self.remain[i..]; 160 | let pos = self.pos; 161 | self.pos.column += i; 162 | 163 | if c == "true" { 164 | Ok(Expr::Bool(true, pos)) 165 | } else if c == "false" { 166 | Ok(Expr::Bool(false, pos)) 167 | } else { 168 | Ok(Expr::ID(c.to_string(), pos)) 169 | } 170 | } 171 | } 172 | 173 | fn parse_oct(&mut self) -> Result { 174 | let mut n = Zero::zero(); 175 | let mut i = 0; 176 | 177 | for c in self.remain.chars() { 178 | let m = if ('0'..='7').contains(&c) { 179 | c as u32 - '0' as u32 180 | } else { 181 | break; 182 | }; 183 | n *= 8; 184 | n += m; 185 | i += 1; 186 | } 187 | 188 | if i == 0 { 189 | return Err(SyntaxErr { 190 | pos: self.pos, 191 | msg: "expect hexadecimal number", 192 | }); 193 | } 194 | 195 | let expr = Expr::Num(n, self.pos); 196 | 197 | self.pos.column += i; 198 | self.remain = &self.remain[i..]; 199 | 200 | self.check_eof(expr) 201 | } 202 | 203 | fn parse_hex(&mut self) -> Result { 204 | let mut n = Zero::zero(); 205 | let mut i = 0; 206 | 207 | for c in self.remain.chars() { 208 | let m = if c.is_ascii_digit() { 209 | c as u32 - '0' as u32 210 | } else if ('a'..='f').contains(&c) { 211 | c as u32 - 'a' as u32 + 10 212 | } else if ('A'..='F').contains(&c) { 213 | c as u32 - 'A' as u32 + 10 214 | } else { 215 | break; 216 | }; 217 | n *= 16; 218 | n += m; 219 | i += 1; 220 | } 221 | 222 | if i == 0 { 223 | return Err(SyntaxErr { 224 | pos: self.pos, 225 | msg: "expect hexadecimal number", 226 | }); 227 | } 228 | 229 | let expr = Expr::Num(n, self.pos); 230 | 231 | self.pos.column += i; 232 | self.remain = &self.remain[i..]; 233 | 234 | self.check_eof(expr) 235 | } 236 | 237 | fn check_eof(&self, expr: Expr) -> Result { 238 | if self.remain.is_empty() { 239 | return Ok(expr); 240 | } 241 | 242 | match self.remain.chars().next() { 243 | Some(c0) => { 244 | if is_paren(c0) || is_space(c0) { 245 | Ok(expr) 246 | } else { 247 | Err(SyntaxErr { 248 | pos: self.pos, 249 | msg: "expected '(', ')', '[', ']' or space", 250 | }) 251 | } 252 | } 253 | None => Err(SyntaxErr { 254 | pos: self.pos, 255 | msg: "unexpected EOF", 256 | }), 257 | } 258 | } 259 | 260 | fn parse_binary(&mut self) -> Result { 261 | let mut n = Zero::zero(); 262 | let mut i = 0; 263 | 264 | for c in self.remain.chars() { 265 | let m = if c == '0' { 266 | 0 267 | } else if c == '1' { 268 | 1 269 | } else { 270 | break; 271 | }; 272 | n *= 2; 273 | n += m; 274 | i += 1; 275 | } 276 | 277 | if i == 0 { 278 | return Err(SyntaxErr { 279 | pos: self.pos, 280 | msg: "expect binary number", 281 | }); 282 | } 283 | 284 | let expr = Expr::Num(n, self.pos); 285 | 286 | self.pos.column += i; 287 | self.remain = &self.remain[i..]; 288 | 289 | self.check_eof(expr) 290 | } 291 | 292 | fn parse_num(&mut self) -> Result { 293 | let mut i = 0; 294 | let is_minus; 295 | 296 | let mut cs = self.remain.chars(); 297 | let c0 = cs.next(); 298 | let c1 = cs.next(); 299 | let c = match (c0, c1) { 300 | (Some('-'), _) => { 301 | is_minus = true; 302 | i += 1; 303 | &self.remain[1..] 304 | } 305 | (Some('0'), Some('x')) => { 306 | self.pos.column += 2; 307 | self.remain = &self.remain[2..]; 308 | return self.parse_hex(); 309 | } 310 | (Some('0'), Some('b')) => { 311 | self.pos.column += 2; 312 | self.remain = &self.remain[2..]; 313 | return self.parse_binary(); 314 | } 315 | (Some('0'), Some('o')) => { 316 | self.pos.column += 2; 317 | self.remain = &self.remain[2..]; 318 | return self.parse_oct(); 319 | } 320 | _ => { 321 | is_minus = false; 322 | self.remain 323 | } 324 | }; 325 | 326 | // parse decimal number 327 | let mut n = Zero::zero(); 328 | 329 | for a in c.chars() { 330 | if a.is_ascii_digit() { 331 | n *= 10; 332 | n += a as usize - '0' as usize; 333 | i += 1; 334 | } else { 335 | break; 336 | } 337 | } 338 | 339 | if is_minus { 340 | n *= -1; 341 | } 342 | 343 | let expr = Expr::Num(n, self.pos); 344 | 345 | self.pos.column += i; 346 | self.remain = &self.remain[i..]; 347 | 348 | self.check_eof(expr) 349 | } 350 | 351 | fn skip_spaces(&mut self) { 352 | let mut i = 0; 353 | let mut prev = ' '; 354 | let mut is_comment = false; 355 | for s in self.remain.chars() { 356 | if is_comment { 357 | if s == '\r' || s == '\n' { 358 | is_comment = false; 359 | } else { 360 | self.pos.column += 1; 361 | i += 1; 362 | prev = s; 363 | continue; 364 | } 365 | } 366 | 367 | if s == ';' { 368 | is_comment = true; 369 | self.pos.column += 1; 370 | } else if is_space(s) { 371 | if s == '\r' || (s == '\n' && prev != '\r') { 372 | self.pos.line += 1; 373 | self.pos.column = 0; 374 | } else { 375 | self.pos.column += 1; 376 | } 377 | } else { 378 | break; 379 | } 380 | i += 1; 381 | prev = s; 382 | } 383 | self.remain = &self.remain[i..] 384 | } 385 | 386 | fn parse_exprs(&mut self) -> Result, SyntaxErr> { 387 | let mut exprs = LinkedList::::new(); 388 | self.skip_spaces(); 389 | 390 | loop { 391 | self.skip_spaces(); 392 | let c0 = self.remain.chars().next(); 393 | if self.remain.is_empty() || c0 == Some(')') || c0 == Some(']') { 394 | break; 395 | } 396 | exprs.push_back(self.parse_expr()?); 397 | } 398 | 399 | Ok(exprs) 400 | } 401 | 402 | fn parse_expr(&mut self) -> Result { 403 | self.skip_spaces(); 404 | match self.remain.chars().next() { 405 | Some('(') => self.parse_apply(), 406 | Some('\'') => self.parse_list(), 407 | Some('[') => self.parse_tuple(), 408 | Some('"') => self.parse_string(), 409 | Some('`') => self.parse_char(), 410 | Some(a) => { 411 | if a == ')' { 412 | Err(SyntaxErr { 413 | pos: self.pos, 414 | msg: "invalid )", 415 | }) 416 | } else if a.is_ascii_digit() { 417 | self.parse_num() 418 | } else if a == '-' { 419 | match self.remain.chars().nth(1) { 420 | Some(b) => { 421 | if b.is_ascii_digit() { 422 | self.parse_num() 423 | } else { 424 | self.parse_id_bool() 425 | } 426 | } 427 | _ => self.parse_id_bool(), 428 | } 429 | } else { 430 | self.parse_id_bool() 431 | } 432 | } 433 | _ => Err(SyntaxErr { 434 | pos: self.pos, 435 | msg: "unexpected character", 436 | }), 437 | } 438 | } 439 | 440 | fn parse_char(&mut self) -> Result { 441 | self.remain = &self.remain[1..]; // skip '`' 442 | let pos = self.pos; 443 | self.pos.column += 1; 444 | 445 | if let Some(c) = self.remain.chars().next() { 446 | match c { 447 | '\\' => { 448 | if let Some(c1) = self.remain.chars().nth(1) { 449 | // TODO: 450 | // \x41 | 7-bit character code (exactly 2 digits, up to 0x7F) 451 | // \u{7FFF} | 24-bit Unicode character code (up to 6 digits) 452 | 453 | let esc = match c1 { 454 | 'r' => '\r', 455 | 'n' => '\n', 456 | 't' => '\t', 457 | '0' => '\0', 458 | '\\' => '\\', 459 | '`' => '`', 460 | _ => { 461 | return Err(SyntaxErr { 462 | pos: self.pos, 463 | msg: "invalid escape character", 464 | }); 465 | } 466 | }; 467 | 468 | if let Some('`') = self.remain.chars().nth(2) { 469 | self.remain = &self.remain[3..]; 470 | self.pos.column += 3; 471 | Ok(Expr::Char(esc, pos)) 472 | } else { 473 | self.pos.column += 1; 474 | Err(SyntaxErr { 475 | pos: self.pos, 476 | msg: "expected `", 477 | }) 478 | } 479 | } else { 480 | Err(SyntaxErr { 481 | pos: self.pos, 482 | msg: "expected escape character", 483 | }) 484 | } 485 | } 486 | '\r' | '\n' => Err(SyntaxErr { 487 | pos: self.pos, 488 | msg: "use \\r or \\n", 489 | }), 490 | c => { 491 | if let Some('`') = self.remain.chars().nth(1) { 492 | self.remain = &self.remain[c.to_string().len() + 1..]; 493 | self.pos.column += 2; 494 | Ok(Expr::Char(c, pos)) 495 | } else { 496 | self.pos.column += 1; 497 | Err(SyntaxErr { 498 | pos: self.pos, 499 | msg: "expected `", 500 | }) 501 | } 502 | } 503 | } 504 | } else { 505 | Err(SyntaxErr { 506 | pos: self.pos, 507 | msg: "expected character literal", 508 | }) 509 | } 510 | } 511 | 512 | fn parse_string(&mut self) -> Result { 513 | self.remain = &self.remain[1..]; // skip '"' 514 | let pos = self.pos; 515 | self.pos.column += 1; 516 | 517 | let mut prev = ' '; 518 | let mut str = "".to_string(); 519 | loop { 520 | if let Some(c) = self.remain.chars().next() { 521 | match c { 522 | '"' => { 523 | self.pos.column += 1; 524 | self.remain = &self.remain[1..]; 525 | break; 526 | } 527 | '\\' => { 528 | if let Some(c1) = self.remain.chars().nth(1) { 529 | // TODO: 530 | // \x41 | 7-bit character code (exactly 2 digits, up to 0x7F) 531 | // \u{7FFF} | 24-bit Unicode character code (up to 6 digits) 532 | 533 | let esc = match c1 { 534 | 'r' => '\r', 535 | 'n' => '\n', 536 | 't' => '\t', 537 | '0' => '\0', 538 | '\\' => '\\', 539 | '"' => '"', 540 | _ => { 541 | return Err(SyntaxErr { 542 | pos: self.pos, 543 | msg: "invalid escape character", 544 | }); 545 | } 546 | }; 547 | 548 | str.push(esc); 549 | self.remain = &self.remain[2..]; 550 | self.pos.column += 2; 551 | continue; 552 | } else { 553 | return Err(SyntaxErr { 554 | pos: self.pos, 555 | msg: "expected escape character", 556 | }); 557 | } 558 | } 559 | _ => { 560 | if c == '\r' || (c == '\n' && prev != '\r') { 561 | self.pos.line += 1; 562 | self.pos.column = 0; 563 | } else { 564 | self.pos.column += 1; 565 | } 566 | 567 | prev = c; 568 | str.push(c); 569 | self.remain = &self.remain[c.to_string().len()..]; 570 | } 571 | } 572 | } else { 573 | return Err(SyntaxErr { 574 | pos: self.pos, 575 | msg: "string is not ended", 576 | }); 577 | } 578 | } 579 | 580 | Ok(Expr::Str(str, pos)) 581 | } 582 | 583 | fn parse_apply(&mut self) -> Result { 584 | self.remain = &self.remain[1..]; // skip '(' 585 | let pos = self.pos; 586 | self.pos.column += 1; 587 | 588 | let exprs = self.parse_exprs()?; 589 | if self.remain.starts_with(')') { 590 | self.remain = &self.remain[1..]; 591 | self.pos.column += 1; 592 | Ok(Expr::Apply(exprs, pos)) 593 | } else { 594 | Err(SyntaxErr { 595 | pos: self.pos, 596 | msg: "expected ')'", 597 | }) 598 | } 599 | } 600 | 601 | fn parse_list(&mut self) -> Result { 602 | let c = &self.remain[1..]; // skip '\'' 603 | let pos = self.pos; 604 | self.pos.column += 1; 605 | 606 | match c.chars().next() { 607 | Some('(') => { 608 | self.remain = &c[1..]; 609 | let exprs = self.parse_exprs()?; 610 | if self.remain.starts_with(')') { 611 | self.remain = &self.remain[1..]; 612 | self.pos.column += 1; 613 | Ok(Expr::List(exprs, pos)) 614 | } else { 615 | Err(SyntaxErr { 616 | pos: self.pos, 617 | msg: "expected ')'", 618 | }) 619 | } 620 | } 621 | _ => Err(SyntaxErr { 622 | pos: self.pos, 623 | msg: "expected '('", 624 | }), 625 | } 626 | } 627 | 628 | fn parse_tuple(&mut self) -> Result { 629 | self.remain = &self.remain[1..]; // skip '[' 630 | let pos = self.pos; 631 | self.pos.column += 1; 632 | 633 | let exprs = self.parse_exprs()?; 634 | if self.remain.starts_with(']') { 635 | self.remain = &self.remain[1..]; 636 | self.pos.column += 1; 637 | Ok(Expr::Tuple(exprs, pos)) 638 | } else { 639 | Err(SyntaxErr { 640 | pos: self.pos, 641 | msg: "expected ']'", 642 | }) 643 | } 644 | } 645 | } 646 | 647 | fn is_space(c: char) -> bool { 648 | c == ' ' || c == '\r' || c == '\n' || c == '\t' 649 | } 650 | 651 | fn is_paren(c: char) -> bool { 652 | c == '(' || c == ')' || c == '[' || c == ']' 653 | } 654 | -------------------------------------------------------------------------------- /docs/index.adoc: -------------------------------------------------------------------------------- 1 | = BLisp: A Statically Typed Lisp Like Language 2 | Yuuki Takano 3 | v0.4.0, 2023-02 4 | :doctype: article 5 | :toc: 6 | :sectnums: 7 | :encoding: utf-8 8 | :stem: latexmath 9 | :source-highlighter: pygments 10 | 11 | 12 | BLisp is a statically typed Lisp like programming language which adopts effect system for no_std environments. 13 | BLisp supports higher order RPC like higher order functions of functional programming languages. 14 | 15 | * https://github.com/ytakano/blisp[GitHub's Repository] 16 | * https://crates.io/crates/blisp[BLisp's crates.io] 17 | 18 | This repository provides only a library crate. 19 | Please see https://github.com/ytakano/blisp-repl[blisp-repl] to use BLisp, 20 | and https://github.com/ytakano/baremetalisp[baremetalisp] which is a toy OS. 21 | 22 | https://ytakano.github.io/blisp/index.ja.html[日本語版はこちら] 23 | 24 | .BLisp on no_std Environment 25 | image:https://cdn-ak.f.st-hatena.com/images/fotolife/y/ytakano/20210221/20210221155657.gif[BLisp on no_std Environment] 26 | 27 | == Features 28 | 29 | * Algebraic data type 30 | * Generics 31 | * Hindley–Milner based type inference 32 | * Effect system to separate side effects from pure functions 33 | * Big integer 34 | * Supporting no_std environments 35 | 36 | == Values 37 | 38 | .values 39 | [source, lisp] 40 | ---- 41 | `A` ; character literal 42 | "Hello" ; string literal 43 | 144 ; integer value 44 | 0xabcDEF ; hexadecimal 45 | 0o777 ; octal 46 | 0b1001 ; binary 47 | true ; boolean value 48 | false ; boolean value 49 | [true 10] ; tuple 50 | [] ; empty tuple 51 | '(1 2 3) ; list 52 | '() ; empty list, Nil 53 | ---- 54 | 55 | == Basic Types 56 | 57 | .types 58 | [source, lisp] 59 | ---- 60 | Char ; character 61 | String ; string 62 | Int ; signed integer 63 | Bool ; boolean 64 | '(Int) ; list of Int 65 | [Int Bool] ; tuple of Int and Bool 66 | (Pure (-> (Int Int) Bool)) ; Pure function, which takes 2 integers and return boolean value 67 | (IO (-> (Int) [])) ; IO function, which takes an integer and return [] 68 | ---- 69 | 70 | Pure and IO are function effects. 71 | In IO functions, both Pure and IO functions can be called. 72 | However, in Pure functions, calling only Pure functions is permitted. 73 | 74 | == Function Definition 75 | 76 | Functions can be defined by defun or export. 77 | "defun" defines a local function which cannot be called from Rust's eval function. 78 | 79 | Suppose following 2 functions. 80 | 81 | .defun 82 | [source, lisp] 83 | ---- 84 | (defun double (x) ; function name is "double" and "x" is an argument 85 | (Pure (-> (Int) Int)) ; function Type 86 | (* 2 x)) ; function body 87 | ---- 88 | 89 | .export 90 | [source, lisp] 91 | ---- 92 | (export quad (x) ; function name is "quad" and "x" is an argument 93 | (Pure (-> (Int) Int)) ; function Type 94 | (double (double x))) ; function body 95 | ---- 96 | 97 | double cannot be called from Rust's eval, but can be called from internally defined functions. 98 | quad can be called from Rust's eval, and it calls double internally. 99 | 100 | This is the code what actually do in Rust. 101 | 102 | .Rust's eval 103 | [source, rust] 104 | ---- 105 | use blisp; 106 | 107 | fn eval(e: &str, ctx: &blisp::semantics::Context) { 108 | // evaluate expressions 109 | let exprs = match blisp::eval(e, ctx) { 110 | Ok(es) => es, 111 | Err(err) => { 112 | println!("error:{}:{}: {}", err.pos.line, err.pos.column, err.msg); 113 | return; 114 | } 115 | }; 116 | 117 | for r in exprs { 118 | match r { 119 | Ok(msg) => { 120 | println!("{}", msg); 121 | } 122 | Err(msg) => { 123 | println!("error: {}", msg); 124 | } 125 | } 126 | } 127 | } 128 | 129 | fn main() { 130 | // internal code 131 | let code = " 132 | (defun double (x) ; function name is double and x is an argument 133 | (Pure (-> (Int) Int)) ; function Type 134 | (* 2 x)) ; function body 135 | 136 | (export quad (x) ; function name is quad and x is an argument 137 | (Pure (-> (Int) Int)) ; function Type 138 | (double (double x))) ; function body 139 | "; 140 | let exprs = blisp::init(code, vec![]).unwrap(); 141 | let ctx = blisp::typing(&exprs).unwrap(); 142 | 143 | let e = "(double 10) ; error"; 144 | eval(e, &ctx); 145 | 146 | let e = "(quad 10) ; OK"; 147 | eval(e, &ctx); 148 | } 149 | ---- 150 | 151 | This code output as follows. 152 | 153 | error:0:1: Typing Error: double is not defined 154 | 40 155 | 156 | == Arithmetic Operations 157 | 158 | .basic 159 | [source, lisp] 160 | ---- 161 | ; (Pure (-> (Int Int) Int)) 162 | (+ 10 20) 163 | (- 30 40) 164 | (* 6 100) 165 | (/ 100 2) 166 | (% 10 3) 167 | ---- 168 | 169 | == Boolean Operations 170 | 171 | .logical 172 | [source, lisp] 173 | ---- 174 | ; (Pure (-> (Bool Bool) Bool)) 175 | (and true false) 176 | (or true false) 177 | (xor true false) 178 | ---- 179 | 180 | .negation 181 | [source, lisp] 182 | ---- 183 | ; (Pure (-> (Bool) Bool)) 184 | (not true) 185 | ---- 186 | 187 | == Comparison 188 | 189 | =, !=, <, >, \<=, >= can be used for 2 values whose types are same. 190 | 191 | .comparison between 2 values whose types are same 192 | [source, lisp] 193 | ---- 194 | ; (Pure (-> (t t) Bool)) 195 | (= 4 4) ; true 196 | (!= 4 4) ; false 197 | (= "Hello" "Hello") ; true 198 | (= (Some 1) (Some 2)) ; false 199 | (< 6 7) 200 | (> 6 7) 201 | (<= 30 40) 202 | (>= 30 40) 203 | (< "Hello" "World") 204 | (<= (Some 1) (Some 2)) 205 | ---- 206 | 207 | _eq_, _neq_, _lt_, _gt_, _leq_, _geq_ can be used for any 2 values 208 | 209 | .comparison between any 2 values 210 | [source, lisp] 211 | ---- 212 | ; (Pure (-> (t1 t1) Bool)) 213 | (geq (Some 1) "Hello") ; (Some 1) is greater than or qeual to "Hello" 214 | (eq "Hello" 100) ; Is "Hello" qeual to 100? 215 | (neq "Hello" 100) ; Is "Hello" not equal to 100? 216 | (lt 100 (Some 20)) ; Is 100 less than (Some 20)? 217 | (gt 200 "Hello") ; Is 200 greater than "Hello" 218 | ---- 219 | 220 | == Bitwise Operations 221 | 222 | [source, lisp] 223 | ---- 224 | (band 1 0) ; bitwise and 225 | (band 1 1) ; bitwise and 226 | (bor 1 0) ; bitwise or 227 | (bor 1 1) ; bitwise or 228 | (bxor 1 0) ; bitwise xor 229 | ---- 230 | 231 | .bit shift 232 | [source, lisp] 233 | ---- 234 | ; (Pure (-> (Int Int) (Option Int))) 235 | (<< 8 4) ; (Some 128) 236 | (>> 128 4) ; (Some 8) 237 | (>> -128 4) ; (Some -8) 238 | ---- 239 | 240 | If 2nd argument is greater or equal to 2^64^, then these function return None. 241 | 242 | == Mathematical Operations 243 | 244 | [source, lisp] 245 | ---- 246 | ; (Pure (-> (Int Int) (Option Int))) 247 | (pow 10 20) ; (Some 100000000000000000000) 248 | 249 | ; (Pure (-> (Int) (Option Int))) 250 | (sqrt 16) ; (Some 4) 251 | ---- 252 | 253 | If _pow_'s exponent portion is greater or equal to 2^32^, then _pow_ returns None. 254 | 255 | If _sqrt_'s argument is less than 0. then _sqrt_ returns None. 256 | 257 | == Algebraic Data Type 258 | 259 | Algebraic data type can be defined as follows. 260 | 261 | [source, lisp] 262 | ---- 263 | ; in BLisp 264 | (data Gender ; type name 265 | Male ; value 266 | Female) ; value 267 | ---- 268 | 269 | Type name's and its value's first character must be uppercase. 270 | This is equivalent to Rust's following code. 271 | 272 | [source, rust] 273 | ---- 274 | // in Rust 275 | enum Gender { 276 | Male, 277 | Female 278 | } 279 | ---- 280 | 281 | Each element can have values as follows. 282 | 283 | [source, lisp] 284 | ---- 285 | ; in BLisp 286 | (data Dim2 287 | (Dim2 Int Int)) ; Dim2 has integers 288 | ---- 289 | 290 | Dim2 can be instantiated as follows. 291 | 292 | [source, lisp] 293 | ---- 294 | (Dim2 10 20) 295 | ---- 296 | 297 | This type is equivalent to Rust's following type. 298 | 299 | [source, rust] 300 | ---- 301 | // in Rust 302 | use num_bigint::BigInt; 303 | enum Dim2 { 304 | Dim2(BigInt, BigInt) 305 | } 306 | ---- 307 | 308 | == Generics 309 | 310 | Option and Result types are defined internally. 311 | 312 | [source, lisp] 313 | ---- 314 | (data (Option t) 315 | (Some t) 316 | None) 317 | 318 | (data (Result t e) 319 | (Ok t) 320 | (Err e)) 321 | ---- 322 | 323 | _t_ and _e_ are type variables. 324 | This code is equivalent to Rust's following code. 325 | 326 | [source, rust] 327 | ---- 328 | // in Rust 329 | enum Option { 330 | Some(T), 331 | None, 332 | } 333 | 334 | enum Result { 335 | Ok(T), 336 | Err(E), 337 | } 338 | ---- 339 | 340 | List type is a built-in type as follows. 341 | 342 | [source, lisp] 343 | ---- 344 | (data (List t) 345 | (Cons t (List t)) 346 | Nil) 347 | ---- 348 | 349 | So, following 2 lists are equivalent. 350 | 351 | [source, lisp] 352 | ---- 353 | (Cons 1 (Cons 2 (Cons 3 Nil))) 354 | '(1 2 3) 355 | ---- 356 | 357 | == Generic Function 358 | 359 | _car_ and _cdr_ are internally defined generic functions. 360 | These definitions are as follows. 361 | 362 | [source, lisp] 363 | ---- 364 | (export car (x) (Pure (-> ('(t)) (Option t))) 365 | (match x 366 | ((Cons n _) (Some n)) 367 | (_ None))) 368 | 369 | (export cdr (x) (Pure (-> ('(t)) '(t))) 370 | (match x 371 | ((Cons _ l) l) 372 | (_ '()))) 373 | ---- 374 | 375 | _t_ is a type variable. These functions can be used as follows. 376 | 377 | [source, lisp] 378 | ---- 379 | (car '(3 8 9)) ; returns (Some 3) 380 | (cdr '(8 10 4)) ; returns '(10 4) 381 | ---- 382 | 383 | Normal and type variables' first character must be lowercase. 384 | 385 | == If Expression 386 | 387 | Straightforward. 388 | 389 | [source, lisp] 390 | ---- 391 | (if (< 10 20) 392 | '(1 2 3) 393 | '()) 394 | ---- 395 | 396 | == Match Expression 397 | 398 | A list can be matched as follows. 399 | 400 | [source, lisp] 401 | ---- 402 | (match '(1 2 3) 403 | ((Cons n _) n) 404 | ('() 0)) 405 | ---- 406 | 407 | The expression 408 | 409 | (Cons n _) 410 | 411 | is a pattern. 412 | If the pattern is matched to '(1 2 3), 1 is assigned to a variable _n_. Then, _n_, namely 1, is returned. 413 | 414 | This is an example of pattern matching of tuple. 415 | 416 | [source, lisp] 417 | ---- 418 | (match [1 3] 419 | ([x y] [y x])) 420 | ---- 421 | 422 | This code swap 1st and 2nd elements of the tuple. 423 | 424 | Integer values can be also used for pattern matching. 425 | 426 | [source, lisp] 427 | ---- 428 | (match 20 429 | (20 true) 430 | (_ false)) 431 | ---- 432 | 433 | More complex example is a as follows. 434 | 435 | [source, lisp] 436 | ---- 437 | (match [(Some 10) true] 438 | ([(Some 10) false] 1) 439 | ([(Some 10) true] 2) 440 | (_ 0)) 441 | ---- 442 | 443 | BLisp checks exhaustively of pattern. 444 | So, following code will be rejected. 445 | 446 | [source, lisp] 447 | ---- 448 | (match '(1 2) 449 | ('() 0)) 450 | ---- 451 | 452 | == Let Expression 453 | 454 | Let expression is used to bind variables as follows. 455 | 456 | [source, lisp] 457 | ---- 458 | (let ((x 10) (y 20)) ; x is 10, y is 20 459 | (* x y)) 460 | 461 | (let ((x 10) (x (* x x)) (x (* x x))) ; x = 10, x = x * x, x = x * x 462 | x) 463 | ---- 464 | 465 | Destructuring can be also performed as follows. 466 | 467 | [source, lisp] 468 | ---- 469 | (let (((Some x) (Some 10))) ; x is 10 470 | (* x 2)) 471 | 472 | (let (([x y] [10 20])) ; x is 10, y is 20 473 | (* x y)) 474 | ---- 475 | 476 | == Lambda Expression 477 | 478 | Lambda expression is defined as follows. 479 | 480 | [source, lisp] 481 | ---- 482 | (lambda (x y) (* x y)) 483 | ---- 484 | 485 | This lambda takes 2 integers and return the multiplication. 486 | Applying arguments to this is simple as follows. 487 | 488 | [source, lisp] 489 | ---- 490 | ((lambda (x y) (* x y)) 10 20) 491 | ---- 492 | 493 | Every lambda expression is Pure. 494 | IO functions cannot be called in any lambda expressions. 495 | 496 | _map_ and _fold_ functions are internally defined as follows. 497 | 498 | [source, lisp] 499 | ---- 500 | (export map (f x) (Pure (-> ((Pure (-> (a) b)) '(a)) '(b))) 501 | (match x 502 | ((Cons h l) (Cons (f h) (map f l))) 503 | (_ '()))) 504 | 505 | (export fold (f init x) (Pure (-> ((Pure (-> (a b) b)) b '(a)) b)) 506 | (match x 507 | ((Cons h l) (fold f (f h init) l)) 508 | (_ init))) 509 | ---- 510 | 511 | _map_ can be used to apply functions to elements of a list as follows. 512 | 513 | [source, lisp] 514 | ---- 515 | ; square each element 516 | (let ((l '(1 2 3)) 517 | (f (lambda (x) (* x x)))) 518 | (map f l)) 519 | ---- 520 | 521 | _fold_ can be used to calculate over elements of a list. 522 | For example, summation can be computed as follows. 523 | 524 | [source, lisp] 525 | ---- 526 | ; summation 527 | (let ((l '(20 50 60)) 528 | (f (lambda (x y) (+ x y)))) 529 | (fold f 0 l)) ; 0 is an initial value 530 | ---- 531 | 532 | Of course, this can be written as follows. 533 | 534 | [source, lisp] 535 | ---- 536 | ; summation 537 | (fold + 0 '(20 50 60)) 538 | ---- 539 | 540 | == String and Character 541 | 542 | _chars_ converts String to (List Char). 543 | 544 | [source, lisp] 545 | ---- 546 | ; (Pure (-> (String) (List Char))) 547 | (chars "Hello") ; '(`H` `e` `l` `l` `o`) 548 | ---- 549 | 550 | _str_ converts (List Char) to String. 551 | 552 | [source, lisp] 553 | ---- 554 | ; (Pure (-> ((List Char)) String)) 555 | (str '(`H` `e` `l` `l` `o`)) ; "Hello" 556 | ---- 557 | 558 | == Foreign Function Interface 559 | 560 | _blisp::embedded_ is a macro for foreign function interface. 561 | By using this, Rust's functions can be called from BLisp easily. 562 | 563 | For example, first of all, define a Rust function as follows. 564 | 565 | [source, rust] 566 | ---- 567 | use blisp::embedded; 568 | use num_bigint::{BigInt, ToBigInt}; 569 | 570 | #[embedded] 571 | fn add_four_ints(a: BigInt, b: (BigInt, BigInt), c: Option) -> Result { 572 | let mut result = a + b.0 + b.1; 573 | if let Some(n) = c { 574 | result += n; 575 | } 576 | 577 | Ok(result) 578 | } 579 | ---- 580 | 581 | _blisp::embedded_ macro generates a type definition for FFI. 582 | This function can be called from BLisp as follows. 583 | 584 | [source, lisp] 585 | ---- 586 | (export call_add_four_ints (n) 587 | (IO (-> ((Option Int)) (Result Int String))) 588 | (add_four_ints 1 [2 3] n)) 589 | ---- 590 | 591 | To register FFIs, a vector of the definition generated by _embedded_ macro 592 | must be passed to _blisp::init_ as follows. 593 | 594 | [source, rust] 595 | ---- 596 | // add_for_ints 597 | let code = "(export call_add_four_ints (n) 598 | (IO (-> ((Option Int)) (Result Int String))) 599 | (add_four_ints 1 [2 3] n) 600 | )"; 601 | let exprs = blisp::init(code, vec![Box::new(AddFourInts)]).unwrap(); 602 | let ctx = blisp::typing(exprs).unwrap(); 603 | let result = blisp::eval("(call_add_four_ints (Some 4))", &ctx).unwrap(); 604 | ---- 605 | 606 | The function name is _add_four_ints_, then _AddFourInts_, which is camel case, 607 | must be passed to _blisp::init_ capsulated by _Box_ and _Vec_. 608 | 609 | FFIs in Rust take and return only types described as follows. 610 | Other types, like _Vec_, are not supported, 611 | but _Vec>_ is accepted. 612 | Types between BLisp and Rust are automatically converted by 613 | the function generated by _embedded_ macro. 614 | 615 | .Type Conversion between BLisp and Rust 616 | |=== 617 | |BLisp | Rust 618 | 619 | |_Int_ | _BigInt_ 620 | |_Bool_ | _bool_ 621 | |_Char_ | _char_ 622 | |_String_ | _String_ 623 | |_'(T)_ | _Vec_ 624 | |_[T0, T1]_ | _(T0, T1)_ 625 | |_(Option T)_ | _Option_ 626 | |_(Result T E)_ | _Result_ 627 | |=== 628 | 629 | Note that every FFI is treated as IO functions. 630 | 631 | == Transpilation to Coq (Experimental) 632 | 633 | BLisp experimentally implements a transpiler to Coq. 634 | It can be invoked by calling _blisp::transpile_ as follows. 635 | 636 | [source, coq] 637 | ---- 638 | let expr = " 639 | (defun snoc (l y) 640 | (Pure (-> ( 641 | '(t) t) 642 | '(t))) 643 | (match l 644 | (nil (Cons y nil)) 645 | ((Cons h b) (Cons h (snoc b y))))) 646 | 647 | (defun rev (l) 648 | (Pure (-> ( 649 | '(t)) 650 | '(t))) 651 | (match l 652 | (nil nil) 653 | ((Cons h t) (snoc (rev t) h)))) 654 | "; 655 | let exprs = blisp::init(expr, vec![]).unwrap(); 656 | let ctx = blisp::typing(exprs).unwrap(); 657 | 658 | println!("{}", blisp::transpile(&ctx)); 659 | ---- 660 | 661 | This outputs Coq code as follows. 662 | It includes as well as the prelude of BLisp. 663 | 664 | [source, coq] 665 | ---- 666 | Require Import ZArith. 667 | Require Import Coq.Lists.List. 668 | 669 | Inductive Option (t: Type): Type := 670 | | Some (x0: t) 671 | | None. 672 | 673 | Arguments Some{t}. 674 | Arguments None{t}. 675 | 676 | Inductive Result (t e: Type): Type := 677 | | Ok (x0: t) 678 | | Err (x0: e). 679 | 680 | Arguments Ok{t e}. 681 | Arguments Err{t e}. 682 | 683 | Definition car {t: Type} (x: list t): Option t := 684 | match x with 685 | | (cons n _) => (Some n) 686 | | _ => (None) 687 | end. 688 | 689 | Definition cdr {t: Type} (x: list t): list t := 690 | match x with 691 | | (cons _ l) => l 692 | | _ => nil 693 | end. 694 | 695 | Definition filter {t: Type} (f: t -> bool) (x: list t): list t := 696 | (reverse (filter' f x nil ) ). 697 | 698 | Fixpoint filter' {t: Type} (f: t -> bool) (x l: list t): list t := 699 | match x with 700 | | (cons h a) => match (f h ) with 701 | | true => (filter' f a (cons h l) ) 702 | | false => (filter' f a l ) 703 | end 704 | | _ => l 705 | end. 706 | 707 | Fixpoint fold {a b: Type} (f: a -> b -> b) (init: b) (x: list a): b := 708 | match x with 709 | | (cons h l) => (fold f (f h init ) l ) 710 | | _ => init 711 | end. 712 | 713 | Fixpoint map {a b: Type} (f: a -> b) (x: list a): list b := 714 | match x with 715 | | (cons h l) => (cons (f h ) (map f l )) 716 | | _ => nil 717 | end. 718 | 719 | Fixpoint rev {t: Type} (l: list t): list t := 720 | match l with 721 | | nil => nil 722 | | (cons h t) => (snoc (rev t ) h ) 723 | end. 724 | 725 | Definition reverse {t: Type} (x: list t): list t := 726 | (reverse' x nil ). 727 | 728 | Fixpoint reverse' {t: Type} (x l: list t): list t := 729 | match x with 730 | | (cons h a) => (reverse' a (cons h l) ) 731 | | _ => l 732 | end. 733 | 734 | Fixpoint snoc {t: Type} (l: list t) (y: t): list t := 735 | match l with 736 | | nil => (cons y nil) 737 | | (cons h b) => (cons h (snoc b y )) 738 | end. 739 | ---- 740 | 741 | Not that this transpiler is experimental. 742 | So, Coq cannot interpret some outputs. 743 | Please fix it manually when you encounter that situation. 744 | It is probably easy. 745 | 746 | == Examples 747 | 748 | === Reverse 749 | 750 | _reverse_ is a internally defined function. It reverses order of a list. 751 | 752 | [source, lisp] 753 | ---- 754 | (reverse '(1 2 3 4 5 6 7 8 9)) 755 | ---- 756 | 757 | This outputs as follows. 758 | 759 | '(9 8 7 6 5 4 3 2 1) 760 | 761 | === Filter 762 | 763 | _filter_ is a internally defined function. It filters the elements in a list. 764 | 765 | [source, lisp] 766 | ---- 767 | (filter (lambda (x) (= (% x 2) 0)) '(1 2 3 4 5 6 7 8 9)) 768 | ---- 769 | 770 | This outputs as follows. 771 | 772 | '(2 4 6 8) 773 | 774 | _filter_'s type is as follows. 775 | 776 | [source, lisp] 777 | ---- 778 | (Pure (-> 779 | ((Pure (-> (t) Bool)) ; take a function 780 | '(t)) ; take a list 781 | '(t))) ; return a list 782 | ---- 783 | 784 | === Factorial 785 | 786 | Tail call factorial can be coded as follows. 787 | 788 | [source, lisp] 789 | ---- 790 | (export factorial (n) (Pure (-> (Int) Int)) 791 | (fact n 1)) 792 | 793 | (defun fact (n total) (Pure (-> (Int Int) Int)) 794 | (if (<= n 0) 795 | total 796 | (fact (- n 1) (* n total)))) 797 | ---- 798 | 799 | This function can be called as follows. 800 | 801 | >> (factorial 10) 802 | 3628800 803 | >> 804 | >> (factorial 1000) 805 | 402387260077093773543702433923003985719374864210714632543799910429938512398629020592044208486969404800479988610197196058631666872994808558901323829669944590997424504087073759918823627727188732519779505950995276120874975462497043601418278094646496291056393887437886487337119181045825783647849977012476632889835955735432513185323958463075557409114262417474349347553428646576611667797396668820291207379143853719588249808126867838374559731746136085379534524221586593201928090878297308431392844403281231558611036976801357304216168747609675871348312025478589320767169132448426236131412508780208000261683151027341827977704784635868170164365024153691398281264810213092761244896359928705114964975419909342221566832572080821333186116811553615836546984046708975602900950537616475847728421889679646244945160765353408198901385442487984959953319101723355556602139450399736280750137837615307127761926849034352625200015888535147331611702103968175921510907788019393178114194545257223865541461062892187960223838971476088506276862967146674697562911234082439208160153780889893964518263243671616762179168909779911903754031274622289988005195444414282012187361745992642956581746628302955570299024324153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690897968482590125458327168226458066526769958652682272807075781391858178889652208164348344825993266043367660176999612831860788386150279465955131156552036093988180612138558600301435694527224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136493273497565513958720559654228749774011413346962715422845862377387538230483865688976461927383814900140767310446640259899490222221765904339901886018566526485061799702356193897017860040811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614839657382291123125024186649353143970137428531926649875337218940694281434118520158014123344828015051399694290153483077644569099073152433278288269864602789864321139083506217095002597389863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826280956121450994871701244516461260379029309120889086942028510640182154399457156805941872748998094254742173582401063677404595741785160829230135358081840096996372524230560855903700624271243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 806 | >> 807 | >> (factorial 100) 808 | 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000 809 | >> 810 | >> (factorial 500) 811 | 1220136825991110068701238785423046926253574342803192842192413588385845373153881997605496447502203281863013616477148203584163378722078177200480785205159329285477907571939330603772960859086270429174547882424912726344305670173270769461062802310452644218878789465754777149863494367781037644274033827365397471386477878495438489595537537990423241061271326984327745715546309977202781014561081188373709531016356324432987029563896628911658974769572087926928871281780070265174507768410719624390394322536422605234945850129918571501248706961568141625359056693423813008856249246891564126775654481886506593847951775360894005745238940335798476363944905313062323749066445048824665075946735862074637925184200459369692981022263971952597190945217823331756934581508552332820762820023402626907898342451712006207714640979456116127629145951237229913340169552363850942885592018727433795173014586357570828355780158735432768888680120399882384702151467605445407663535984174430480128938313896881639487469658817504506926365338175055478128640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -------------------------------------------------------------------------------- /src/runtime.rs: -------------------------------------------------------------------------------- 1 | use crate::r#macro; 2 | 3 | use super::{parser, semantics, LispErr, Pos}; 4 | use alloc::{ 5 | boxed::Box, 6 | collections::{btree_map::BTreeMap, linked_list::LinkedList, vec_deque::VecDeque}, 7 | format, 8 | string::{String, ToString}, 9 | vec, 10 | vec::Vec, 11 | }; 12 | use core::{ 13 | cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}, 14 | ops::{Shl, Shr}, 15 | pin::Pin, 16 | ptr::{read_volatile, write_volatile}, 17 | }; 18 | use num_bigint::BigInt; 19 | use num_traits::{ToPrimitive, Zero}; 20 | 21 | type Expr = semantics::LangExpr; 22 | type Pattern = semantics::Pattern; 23 | 24 | struct RuntimeErr { 25 | msg: String, 26 | pos: Pos, 27 | } 28 | 29 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] 30 | pub struct Variables { 31 | vars: VecDeque>, 32 | } 33 | 34 | impl Variables { 35 | fn new() -> Variables { 36 | let mut list = VecDeque::new(); 37 | list.push_back(BTreeMap::new()); 38 | Variables { vars: list } 39 | } 40 | 41 | fn push(&mut self) { 42 | self.vars.push_back(BTreeMap::new()); 43 | } 44 | 45 | fn pop(&mut self) { 46 | self.vars.pop_back(); 47 | } 48 | 49 | fn insert(&mut self, id: String, data: RTData) { 50 | let m = self.vars.back_mut().unwrap(); 51 | m.insert(id, data); 52 | } 53 | 54 | fn get(&mut self, id: &str) -> Option<&RTData> { 55 | for m in self.vars.iter().rev() { 56 | if let Some(val) = m.get(id) { 57 | return Some(val); 58 | } 59 | } 60 | None 61 | } 62 | } 63 | 64 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] 65 | pub enum TCall { 66 | Defun(String), 67 | Lambda(u64), 68 | } 69 | 70 | #[derive(Eq, Debug, Clone)] 71 | pub struct IntType(*mut (BigInt, bool)); 72 | 73 | impl IntType { 74 | fn get_int(&self) -> &BigInt { 75 | unsafe { &(*self.0).0 } 76 | } 77 | 78 | fn get_ref(&mut self) -> &mut bool { 79 | unsafe { &mut (*self.0).1 } 80 | } 81 | } 82 | 83 | impl Ord for IntType { 84 | fn cmp(&self, other: &Self) -> Ordering { 85 | let s1 = self.get_int(); 86 | let s2 = other.get_int(); 87 | s1.cmp(s2) 88 | } 89 | } 90 | 91 | impl PartialOrd for IntType { 92 | fn partial_cmp(&self, other: &Self) -> Option { 93 | let s1 = self.get_int(); 94 | let s2 = other.get_int(); 95 | Some(s1.cmp(s2)) 96 | } 97 | } 98 | 99 | impl PartialEq for IntType { 100 | fn eq(&self, other: &Self) -> bool { 101 | let s1 = self.get_int(); 102 | let s2 = other.get_int(); 103 | s1 == s2 104 | } 105 | } 106 | 107 | #[derive(Eq, Debug, Clone)] 108 | pub struct StrType(*mut (String, bool)); 109 | 110 | impl StrType { 111 | fn get_string(&self) -> &String { 112 | unsafe { &(*self.0).0 } 113 | } 114 | 115 | fn get_ref(&mut self) -> &mut bool { 116 | unsafe { &mut (*self.0).1 } 117 | } 118 | } 119 | 120 | impl Ord for StrType { 121 | fn cmp(&self, other: &Self) -> Ordering { 122 | let s1 = self.get_string(); 123 | let s2 = other.get_string(); 124 | s1.cmp(s2) 125 | } 126 | } 127 | 128 | impl PartialOrd for StrType { 129 | fn partial_cmp(&self, other: &Self) -> Option { 130 | let s1 = self.get_string(); 131 | let s2 = other.get_string(); 132 | Some(s1.cmp(s2)) 133 | } 134 | } 135 | 136 | impl PartialEq for StrType { 137 | fn eq(&self, other: &Self) -> bool { 138 | let s1 = self.get_string(); 139 | let s2 = other.get_string(); 140 | s1 == s2 141 | } 142 | } 143 | 144 | #[derive(Eq, Debug, Clone)] 145 | pub struct ClojureType(*mut (Clojure, bool)); 146 | 147 | impl ClojureType { 148 | fn get_clojure(&self) -> &Clojure { 149 | unsafe { &(*self.0).0 } 150 | } 151 | 152 | fn get_clojure_mut(&mut self) -> &mut Clojure { 153 | unsafe { &mut (*self.0).0 } 154 | } 155 | 156 | fn get_ref(&mut self) -> &mut bool { 157 | unsafe { &mut (*self.0).1 } 158 | } 159 | } 160 | 161 | impl Ord for ClojureType { 162 | fn cmp(&self, other: &Self) -> Ordering { 163 | let s1 = self.get_clojure(); 164 | let s2 = other.get_clojure(); 165 | s1.cmp(s2) 166 | } 167 | } 168 | 169 | impl PartialOrd for ClojureType { 170 | fn partial_cmp(&self, other: &Self) -> Option { 171 | let s1 = self.get_clojure(); 172 | let s2 = other.get_clojure(); 173 | Some(s1.cmp(s2)) 174 | } 175 | } 176 | 177 | impl PartialEq for ClojureType { 178 | fn eq(&self, other: &Self) -> bool { 179 | let s1 = self.get_clojure(); 180 | let s2 = other.get_clojure(); 181 | s1 == s2 182 | } 183 | } 184 | 185 | #[derive(Eq, Debug, Clone)] 186 | pub struct LDataType(*mut (LabeledData, bool)); 187 | 188 | impl LDataType { 189 | fn get_ldata(&self) -> &LabeledData { 190 | unsafe { &(*self.0).0 } 191 | } 192 | 193 | fn get_ldata_mut(&mut self) -> &mut LabeledData { 194 | unsafe { &mut (*self.0).0 } 195 | } 196 | 197 | fn get_ref(&mut self) -> &mut bool { 198 | unsafe { &mut (*self.0).1 } 199 | } 200 | } 201 | 202 | impl Ord for LDataType { 203 | fn cmp(&self, other: &Self) -> Ordering { 204 | let s1 = self.get_ldata(); 205 | let s2 = other.get_ldata(); 206 | s1.cmp(s2) 207 | } 208 | } 209 | 210 | impl PartialOrd for LDataType { 211 | fn partial_cmp(&self, other: &Self) -> Option { 212 | let s1 = self.get_ldata(); 213 | let s2 = other.get_ldata(); 214 | Some(s1.cmp(s2)) 215 | } 216 | } 217 | 218 | impl PartialEq for LDataType { 219 | fn eq(&self, other: &Self) -> bool { 220 | let s1 = self.get_ldata(); 221 | let s2 = other.get_ldata(); 222 | s1 == s2 223 | } 224 | } 225 | 226 | #[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)] 227 | pub enum RTData { 228 | Str(StrType), 229 | Char(char), 230 | Int(IntType), 231 | Bool(bool), 232 | Defun(String), 233 | Lambda(ClojureType), 234 | LData(LDataType), 235 | TailCall(TCall, Variables), 236 | } 237 | 238 | fn escape_char(c: char) -> String { 239 | match c { 240 | '\n' => "\\n".to_string(), 241 | '\r' => "\\r".to_string(), 242 | '\t' => "\\t".to_string(), 243 | '\0' => "\\0".to_string(), 244 | _ => c.to_string(), 245 | } 246 | } 247 | 248 | impl RTData { 249 | fn get_in_lisp(&self, list_head: bool) -> String { 250 | match self { 251 | RTData::Str(n) => { 252 | let mut str = "\"".to_string(); 253 | for s in n.get_string().chars() { 254 | if s == '"' { 255 | str.push_str("\\\""); 256 | } else { 257 | str.push_str(&escape_char(s)); 258 | } 259 | } 260 | str.push('"'); 261 | str 262 | } 263 | RTData::Char(c) => { 264 | if *c == '`' { 265 | "`\\``".to_string() 266 | } else { 267 | let s = escape_char(*c); 268 | format!("`{}`", s) 269 | } 270 | } 271 | RTData::Int(n) => { 272 | format!("{}", n.get_int()) 273 | } 274 | RTData::Bool(n) => n.to_string(), 275 | RTData::Defun(n) => n.to_string(), 276 | RTData::Lambda(n) => format!("(Lambda {})", n.get_clojure().ident), 277 | RTData::LData(n) => { 278 | let label = &n.get_ldata().label; 279 | if label == "Cons" { 280 | let e1; 281 | let e2; 282 | match n.get_ldata().data.as_ref() { 283 | Some(ld) => { 284 | e1 = ld[0].get_in_lisp(true); 285 | e2 = ld[1].get_in_lisp(false); 286 | } 287 | None => panic!("invalid list"), 288 | } 289 | if list_head { 290 | if e2.is_empty() { 291 | format!("'({})", e1) 292 | } else { 293 | format!("'({} {})", e1, e2) 294 | } 295 | } else if e2.is_empty() { 296 | e1 297 | } else { 298 | format!("{} {}", e1, e2) 299 | } 300 | } else if label == "Nil" { 301 | if list_head { 302 | "'()".to_string() 303 | } else { 304 | "".to_string() 305 | } 306 | } else if label == "Tuple" { 307 | match n.get_ldata().data.as_ref() { 308 | Some(ld) => { 309 | let mut msg = "".to_string(); 310 | let len = (*ld).len(); 311 | let mut i = 1; 312 | for d in ld.iter() { 313 | if i == len { 314 | msg = format!("{}{}", msg, d.get_in_lisp(true)); 315 | } else { 316 | msg = format!("{}{} ", msg, d.get_in_lisp(true)); 317 | } 318 | i += 1; 319 | } 320 | format!("[{}]", msg) 321 | } 322 | None => "[]".to_string(), 323 | } 324 | } else { 325 | match n.get_ldata().data.as_ref() { 326 | Some(ld) => { 327 | let mut msg = format!("({}", label); 328 | for d in ld.iter() { 329 | msg = format!("{} {}", msg, d.get_in_lisp(true)); 330 | } 331 | format!("{})", msg) 332 | } 333 | None => label.to_string(), 334 | } 335 | } 336 | } 337 | RTData::TailCall(TCall::Defun(f), _) => format!("(TailCall (Defun {}))", f), 338 | RTData::TailCall(TCall::Lambda(f), _) => format!("(TailCall (Lambda {}))", f), 339 | } 340 | } 341 | } 342 | 343 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] 344 | struct LabeledData { 345 | label: String, 346 | data: Option>, 347 | } 348 | 349 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] 350 | struct Clojure { 351 | ident: u64, 352 | data: Option>, 353 | } 354 | 355 | const MIN_GC_NUM: usize = 1024; 356 | 357 | #[derive(Debug)] 358 | pub(crate) struct RootObject { 359 | objects: LinkedList>>, 360 | clojure: LinkedList>>, 361 | integers: LinkedList>>, 362 | strings: LinkedList>>, 363 | threshold: usize, 364 | } 365 | 366 | impl RootObject { 367 | fn new() -> RootObject { 368 | RootObject { 369 | objects: LinkedList::new(), 370 | clojure: LinkedList::new(), 371 | integers: LinkedList::new(), 372 | strings: LinkedList::new(), 373 | threshold: MIN_GC_NUM, 374 | } 375 | } 376 | 377 | fn len(&self) -> usize { 378 | self.objects.len() + self.clojure.len() + self.integers.len() + self.strings.len() 379 | } 380 | 381 | fn make_int(&mut self, n: BigInt) -> IntType { 382 | self.integers.push_back(Box::pin((n, false))); 383 | let ptr = self.integers.back_mut().unwrap(); 384 | IntType(unsafe { ptr.as_mut().get_unchecked_mut() as *mut (BigInt, bool) }) 385 | } 386 | 387 | fn make_str(&mut self, str: String) -> StrType { 388 | self.strings.push_back(Box::pin((str, false))); 389 | let ptr = self.strings.back_mut().unwrap(); 390 | StrType(unsafe { ptr.as_mut().get_unchecked_mut() as *mut (String, bool) }) 391 | } 392 | 393 | fn make_obj(&mut self, label: String, data: Option>) -> LDataType { 394 | let obj = LabeledData { label, data }; 395 | self.objects.push_back(Box::pin((obj, false))); 396 | let ptr = self.objects.back_mut().unwrap(); 397 | LDataType(unsafe { ptr.as_mut().get_unchecked_mut() as *mut (LabeledData, bool) }) 398 | } 399 | 400 | fn make_clojure(&mut self, ident: u64, data: Option>) -> ClojureType { 401 | let obj = Clojure { ident, data }; 402 | self.clojure.push_back(Box::pin((obj, false))); 403 | let ptr = self.clojure.back_mut().unwrap(); 404 | ClojureType(unsafe { ptr.as_mut().get_unchecked_mut() as *mut (Clojure, bool) }) 405 | } 406 | } 407 | 408 | pub struct Environment<'a> { 409 | pub(crate) ctx: &'a semantics::Context, 410 | pub(crate) lambda: &'a BTreeMap, 411 | pub(crate) root: &'a mut RootObject, 412 | pub(crate) vars: &'a mut VecDeque, 413 | } 414 | 415 | pub(crate) fn eval( 416 | code: &str, 417 | ctx: &semantics::Context, 418 | ) -> Result>, LispErr> { 419 | let mut ps = parser::Parser::new(code, crate::FileType::Eval); 420 | let mut exprs: LinkedList = match ps.parse() { 421 | Ok(e) => e, 422 | Err(e) => { 423 | let msg = format!("Syntax Error: {}", e.msg); 424 | return Err(LispErr { msg, pos: e.pos }); 425 | } 426 | }; 427 | 428 | if let Err(e) = r#macro::process_macros(&mut exprs) { 429 | let msg = format!("Macro Error: {}", e.msg); 430 | return Err(LispErr::new(msg, e.pos)); 431 | } 432 | 433 | for expr in exprs.iter_mut() { 434 | if let Err(e) = r#macro::apply(expr, &ctx.macros) { 435 | let msg: String = format!("Macro Error: {}", e.msg); 436 | return Err(LispErr { msg, pos: e.pos }); 437 | } 438 | } 439 | 440 | let mut typed_exprs = LinkedList::new(); 441 | for expr in &exprs { 442 | match semantics::typing_expr(expr, ctx) { 443 | Ok(e) => { 444 | typed_exprs.push_back(e); 445 | } 446 | Err(e) => { 447 | let msg = format!("Typing Error: {}", e.msg); 448 | return Err(LispErr { msg, pos: e.pos }); 449 | } 450 | } 451 | } 452 | 453 | let mut root = RootObject::new(); 454 | let mut result = LinkedList::new(); 455 | for (expr, lambda) in &typed_exprs { 456 | let mut vars = VecDeque::new(); 457 | vars.push_back(Variables::new()); 458 | 459 | let mut env = Environment { 460 | ctx, 461 | lambda, 462 | root: &mut root, 463 | vars: &mut vars, 464 | }; 465 | 466 | match eval_expr(expr, &mut env) { 467 | Ok(val) => { 468 | result.push_back(Ok(val.get_in_lisp(true))); 469 | } 470 | Err(e) => { 471 | let msg = format!( 472 | "(RuntimeErr [{} (Pos {} {})])", 473 | e.msg, e.pos.line, e.pos.column 474 | ); 475 | result.push_back(Err(msg)); 476 | return Ok(result); 477 | } 478 | } 479 | } 480 | 481 | Ok(result) 482 | } 483 | 484 | fn get_data_of_id(id: &str, vars: &mut VecDeque) -> RTData { 485 | match vars.back_mut().unwrap().get(id) { 486 | Some(data) => data.clone(), 487 | None => RTData::Defun(id.to_string()), 488 | } 489 | } 490 | 491 | fn eval_expr(expr: &Expr, env: &mut Environment<'_>) -> Result { 492 | match expr { 493 | Expr::LitStr(e) => Ok(RTData::Str(env.root.make_str(e.str.clone()))), 494 | Expr::LitNum(e) => Ok(RTData::Int(env.root.make_int(e.num.clone()))), 495 | Expr::LitChar(e) => Ok(RTData::Char(e.c)), 496 | Expr::LitBool(e) => Ok(RTData::Bool(e.val)), 497 | Expr::IfExpr(e) => eval_if(e, env), 498 | Expr::DataExpr(e) => eval_data(e, env), 499 | Expr::ListExpr(e) => eval_list(e, env), 500 | Expr::LetExpr(e) => eval_let(e, env), 501 | Expr::MatchExpr(e) => eval_match(e, env), 502 | Expr::IDExpr(e) => Ok(eval_id(e, env.vars)), 503 | Expr::ApplyExpr(e) => eval_apply(e, env), 504 | Expr::TupleExpr(e) => eval_tuple(e, env), 505 | Expr::LambdaExpr(e) => Ok(eval_lambda(e, env)), 506 | } 507 | } 508 | 509 | fn eval_lambda(expr: &semantics::Lambda, env: &mut Environment<'_>) -> RTData { 510 | let data = if !expr.vars.is_empty() { 511 | let mut m = BTreeMap::new(); 512 | for v in &expr.vars { 513 | m.insert(v.to_string(), get_data_of_id(v, env.vars)); 514 | } 515 | Some(m) 516 | } else { 517 | None 518 | }; 519 | 520 | let ptr = env.root.make_clojure(expr.ident, data); 521 | RTData::Lambda(ptr) 522 | } 523 | 524 | fn eval_tuple(expr: &semantics::Exprs, env: &mut Environment<'_>) -> Result { 525 | let mut v = Vec::new(); 526 | for e in expr.exprs.iter() { 527 | v.push(eval_expr(e, env)?); 528 | } 529 | 530 | let ptr = env.root.make_obj("Tuple".to_string(), Some(v)); 531 | 532 | Ok(RTData::LData(ptr)) 533 | } 534 | 535 | fn get_fun<'a>( 536 | ctx: &'a semantics::Context, 537 | fun_name: &str, 538 | expr: &Expr, 539 | ) -> Result<&'a semantics::Defun, RuntimeErr> { 540 | let fun = match ctx.funs.get(fun_name) { 541 | Some(f) => f, 542 | None => { 543 | let pos = expr.get_pos(); 544 | let msg = format!("{} is not defined", fun_name); 545 | return Err(RuntimeErr { msg, pos }); 546 | } 547 | }; 548 | 549 | Ok(fun) 550 | } 551 | 552 | fn get_lambda<'a>( 553 | ctx: &'a semantics::Context, 554 | lambda: &'a BTreeMap, 555 | id: u64, 556 | expr: &Expr, 557 | ) -> Result<&'a semantics::Lambda, RuntimeErr> { 558 | let fun; 559 | match ctx.lambda.get(&id) { 560 | Some(f) => { 561 | fun = f; 562 | } 563 | None => match lambda.get(&id) { 564 | Some(f) => { 565 | fun = f; 566 | } 567 | None => { 568 | let pos = expr.get_pos(); 569 | let msg = format!("could not find (Lambda {})", id); 570 | return Err(RuntimeErr { msg, pos }); 571 | } 572 | }, 573 | } 574 | 575 | Ok(fun) 576 | } 577 | 578 | fn call_lambda( 579 | expr: &semantics::Apply, 580 | env: &mut Environment<'_>, 581 | cloj: &Clojure, 582 | iter: core::slice::Iter, 583 | fun_expr: &semantics::LangExpr, 584 | ) -> Result { 585 | // look up lambda 586 | let ident = cloj.ident; 587 | let fun = get_lambda(env.ctx, env.lambda, ident, fun_expr)?; 588 | 589 | // set up arguments 590 | let mut vars_fun = Variables::new(); 591 | for (e, arg) in iter.zip(fun.args.iter()) { 592 | let data = eval_expr(e, env)?; 593 | vars_fun.insert(arg.id.to_string(), data); 594 | } 595 | 596 | // set up free variables 597 | match &cloj.data { 598 | Some(d) => { 599 | for (key, val) in d { 600 | vars_fun.insert(key.to_string(), val.clone()); 601 | } 602 | } 603 | None => (), 604 | } 605 | 606 | // tail call optimization 607 | if expr.is_tail { 608 | Ok(RTData::TailCall(TCall::Lambda(ident), vars_fun)) 609 | } else { 610 | env.vars.push_back(vars_fun); 611 | let result = eval_tail_call(&fun.expr, env); 612 | env.vars.pop_back(); 613 | result 614 | } 615 | } 616 | 617 | fn eval_apply(expr: &semantics::Apply, env: &mut Environment<'_>) -> Result { 618 | let mut iter = expr.exprs.iter(); 619 | let fun_expr = match iter.next() { 620 | Some(e) => e, 621 | None => { 622 | let pos = expr.pos; 623 | return Err(RuntimeErr { 624 | msg: "empty application".to_string(), 625 | pos, 626 | }); 627 | } 628 | }; 629 | 630 | match eval_expr(fun_expr, env)? { 631 | RTData::Defun(fun_name) => { 632 | // call built-in function 633 | if env.ctx.built_in.contains(&fun_name) { 634 | let mut v = Vec::new(); 635 | for e in iter { 636 | let data = eval_expr(e, env)?; 637 | v.push(data); 638 | } 639 | return eval_built_in(fun_name, &v, expr.pos, env); 640 | } 641 | 642 | if let Some(ffi) = env.ctx.ext_ffi.get(fun_name.as_str()) { 643 | let mut v = Vec::new(); 644 | for e in iter { 645 | let data = eval_expr(e, env)?; 646 | v.push(data); 647 | } 648 | return Ok(ffi(env, &v)); 649 | } 650 | 651 | // look up defun 652 | if let Ok(fun) = get_fun(env.ctx, &fun_name, fun_expr) { 653 | // set up arguments 654 | let mut vars_fun = Variables::new(); 655 | for (e, arg) in iter.zip(fun.args.iter()) { 656 | let data = eval_expr(e, env)?; 657 | vars_fun.insert(arg.id.to_string(), data); 658 | } 659 | 660 | // tail call optimization 661 | if expr.is_tail { 662 | Ok(RTData::TailCall(TCall::Defun(fun_name), vars_fun)) 663 | } else { 664 | env.vars.push_back(vars_fun); 665 | let result = eval_tail_call(&fun.expr, env)?; 666 | env.vars.pop_back(); 667 | Ok(result) 668 | } 669 | } else { 670 | // call clojure 671 | if let Some(RTData::Lambda(cloj)) = env.vars.back_mut().unwrap().get(&fun_name) { 672 | let c = cloj.clone(); 673 | return call_lambda(expr, env, c.get_clojure(), iter, fun_expr); 674 | } 675 | 676 | // could not find such function 677 | let pos = fun_expr.get_pos(); 678 | let msg = format!("{} is not defined", fun_name); 679 | Err(RuntimeErr { msg, pos }) 680 | } 681 | } 682 | RTData::Lambda(f) => { 683 | let f = f.get_clojure(); 684 | call_lambda(expr, env, f, iter, fun_expr) 685 | } 686 | _ => { 687 | let pos = fun_expr.get_pos(); 688 | Err(RuntimeErr { 689 | msg: "not function".to_string(), 690 | pos, 691 | }) 692 | } 693 | } 694 | } 695 | 696 | fn eval_tail_call<'a>( 697 | mut expr: &'a Expr, 698 | env: &'a mut Environment<'_>, 699 | ) -> Result { 700 | loop { 701 | match eval_expr(expr, env)? { 702 | RTData::TailCall(TCall::Defun(fun_name), vars_fun) => { 703 | let fun = get_fun(env.ctx, &fun_name, expr)?; 704 | expr = &fun.expr; 705 | env.vars.pop_back(); 706 | env.vars.push_back(vars_fun); 707 | collect_garbage(env.vars, env.root); // mark and sweep 708 | } 709 | RTData::TailCall(TCall::Lambda(id), vars_fun) => { 710 | let fun = get_lambda(env.ctx, env.lambda, id, expr)?; 711 | expr = &fun.expr; 712 | env.vars.pop_back(); 713 | env.vars.push_back(vars_fun); 714 | collect_garbage(env.vars, env.root); // mark and sweep 715 | } 716 | x => { 717 | return Ok(x); 718 | } 719 | } 720 | } 721 | } 722 | 723 | fn get_int(args: &[RTData], pos: Pos) -> Result<*const BigInt, RuntimeErr> { 724 | match &args[0] { 725 | RTData::Int(n) => Ok(n.get_int()), 726 | _ => Err(RuntimeErr { 727 | msg: "there must be exactly 2 integers".to_string(), 728 | pos, 729 | }), 730 | } 731 | } 732 | 733 | fn get_int_int(args: &[RTData], pos: Pos) -> Result<(*const BigInt, *const BigInt), RuntimeErr> { 734 | match (&args[0], &args[1]) { 735 | (RTData::Int(n1), RTData::Int(n2)) => Ok((n1.get_int(), n2.get_int())), 736 | _ => Err(RuntimeErr { 737 | msg: "there must be exactly 2 integers".to_string(), 738 | pos, 739 | }), 740 | } 741 | } 742 | 743 | fn get_int_int_int( 744 | args: &[RTData], 745 | pos: Pos, 746 | ) -> Result<(*const BigInt, *const BigInt, *const BigInt), RuntimeErr> { 747 | match (&args[0], &args[1], &args[2]) { 748 | (RTData::Int(n1), RTData::Int(n2), RTData::Int(n3)) => { 749 | Ok((n1.get_int(), n2.get_int(), n3.get_int())) 750 | } 751 | _ => Err(RuntimeErr { 752 | msg: "there must be exactly 3 integers".to_string(), 753 | pos, 754 | }), 755 | } 756 | } 757 | 758 | fn get_bool_bool(args: &[RTData], pos: Pos) -> Result<(bool, bool), RuntimeErr> { 759 | match (args[0].clone(), args[1].clone()) { 760 | (RTData::Bool(n1), RTData::Bool(n2)) => Ok((n1, n2)), 761 | _ => Err(RuntimeErr { 762 | msg: "there must be exactly 2 boolean values".to_string(), 763 | pos, 764 | }), 765 | } 766 | } 767 | 768 | fn get_bool(args: &[RTData], pos: Pos) -> Result { 769 | match args[0].clone() { 770 | RTData::Bool(n) => Ok(n), 771 | _ => Err(RuntimeErr { 772 | msg: "there must be exactly 1 boolean value".to_string(), 773 | pos, 774 | }), 775 | } 776 | } 777 | 778 | fn eval_built_in( 779 | fun_name: String, 780 | args: &[RTData], 781 | pos: Pos, 782 | env: &mut Environment<'_>, 783 | ) -> Result { 784 | match fun_name.as_str() { 785 | "+" => { 786 | let (n1, n2) = get_int_int(args, pos)?; 787 | let n = unsafe { &*n1 + &*n2 }; 788 | Ok(RTData::Int(env.root.make_int(n))) 789 | } 790 | "-" => { 791 | let (n1, n2) = get_int_int(args, pos)?; 792 | let n = unsafe { &*n1 - &*n2 }; 793 | Ok(RTData::Int(env.root.make_int(n))) 794 | } 795 | "*" => { 796 | let (n1, n2) = get_int_int(args, pos)?; 797 | let n = unsafe { &*n1 * &*n2 }; 798 | Ok(RTData::Int(env.root.make_int(n))) 799 | } 800 | "/" => { 801 | let (n1, n2) = get_int_int(args, pos)?; 802 | let n = unsafe { &*n1 / &*n2 }; 803 | Ok(RTData::Int(env.root.make_int(n))) 804 | } 805 | "%" => { 806 | let (n1, n2) = get_int_int(args, pos)?; 807 | let n = unsafe { &*n1 % &*n2 }; 808 | Ok(RTData::Int(env.root.make_int(n))) 809 | } 810 | "=" | "eq" => Ok(RTData::Bool(args[0] == args[1])), 811 | "!=" | "neq" => Ok(RTData::Bool(args[0] != args[1])), 812 | "<=" | "leq" => Ok(RTData::Bool(args[0] <= args[1])), 813 | ">=" | "geq" => Ok(RTData::Bool(args[0] >= args[1])), 814 | ">" | "gt" => Ok(RTData::Bool(args[0] > args[1])), 815 | "<" | "lt" => Ok(RTData::Bool(args[0] < args[1])), 816 | "and" => { 817 | let (n1, n2) = get_bool_bool(args, pos)?; 818 | Ok(RTData::Bool(n1 && n2)) 819 | } 820 | "or" => { 821 | let (n1, n2) = get_bool_bool(args, pos)?; 822 | Ok(RTData::Bool(n1 || n2)) 823 | } 824 | "xor" => { 825 | let (n1, n2) = get_bool_bool(args, pos)?; 826 | Ok(RTData::Bool(n1 ^ n2)) 827 | } 828 | "not" => { 829 | let n = get_bool(args, pos)?; 830 | Ok(RTData::Bool(!n)) 831 | } 832 | "band" => { 833 | let (n1, n2) = get_int_int(args, pos)?; 834 | let n = unsafe { &*n1 & &*n2 }; 835 | Ok(RTData::Int(env.root.make_int(n))) 836 | } 837 | "bor" => { 838 | let (n1, n2) = get_int_int(args, pos)?; 839 | let n = unsafe { &*n1 | &*n2 }; 840 | Ok(RTData::Int(env.root.make_int(n))) 841 | } 842 | "bxor" => { 843 | let (n1, n2) = get_int_int(args, pos)?; 844 | let n = unsafe { &*n1 ^ &*n2 }; 845 | Ok(RTData::Int(env.root.make_int(n))) 846 | } 847 | "sqrt" => { 848 | let n = get_int(args, pos)?; 849 | if unsafe { (*n) >= Zero::zero() } { 850 | let n = unsafe { (*n).sqrt() }; 851 | let n = RTData::Int(env.root.make_int(n)); 852 | let ptr = env.root.make_obj("Some".to_string(), Some(vec![n])); 853 | Ok(RTData::LData(ptr)) 854 | } else { 855 | let ptr = env.root.make_obj("None".to_string(), None); 856 | Ok(RTData::LData(ptr)) 857 | } 858 | } 859 | "pow" => { 860 | let (n1, n2) = get_int_int(args, pos)?; 861 | if let Some(e) = unsafe { (*n2).to_u32() } { 862 | let n = unsafe { (*n1).pow(e) }; 863 | let n = RTData::Int(env.root.make_int(n)); 864 | let ptr = env.root.make_obj("Some".to_string(), Some(vec![n])); 865 | Ok(RTData::LData(ptr)) 866 | } else { 867 | let ptr = env.root.make_obj("None".to_string(), None); 868 | Ok(RTData::LData(ptr)) 869 | } 870 | } 871 | ">>" => { 872 | let (n1, n2) = get_int_int(args, pos)?; 873 | if let Some(e) = unsafe { (*n2).to_u64() } { 874 | let n = unsafe { (*n1).clone() }; 875 | let n = n.shr(e); 876 | let n = RTData::Int(env.root.make_int(n)); 877 | let ptr = env.root.make_obj("Some".to_string(), Some(vec![n])); 878 | Ok(RTData::LData(ptr)) 879 | } else { 880 | let ptr = env.root.make_obj("None".to_string(), None); 881 | Ok(RTData::LData(ptr)) 882 | } 883 | } 884 | "<<" => { 885 | let (n1, n2) = get_int_int(args, pos)?; 886 | if let Some(e) = unsafe { (*n2).to_u64() } { 887 | let n = unsafe { (*n1).clone() }; 888 | let n = n.shl(e); 889 | let n = RTData::Int(env.root.make_int(n)); 890 | let ptr = env.root.make_obj("Some".to_string(), Some(vec![n])); 891 | Ok(RTData::LData(ptr)) 892 | } else { 893 | let ptr = env.root.make_obj("None".to_string(), None); 894 | Ok(RTData::LData(ptr)) 895 | } 896 | } 897 | "chars" => { 898 | let mut tail = RTData::LData(env.root.make_obj("Nil".to_string(), None)); 899 | if let RTData::Str(st) = &args[0] { 900 | let s = st.get_string(); 901 | for c in s.chars().rev() { 902 | let c = RTData::Char(c); 903 | let cons = 904 | RTData::LData(env.root.make_obj("Cons".to_string(), Some(vec![c, tail]))); 905 | tail = cons; 906 | } 907 | } 908 | Ok(tail) 909 | } 910 | "str" => { 911 | let mut head = &args[0]; 912 | let mut s = "".to_string(); 913 | loop { 914 | if let RTData::LData(data) = head { 915 | if data.get_ldata().label == "Cons" { 916 | if let Some(d) = &data.get_ldata().data { 917 | if let RTData::Char(c) = &d[0] { 918 | s.push(*c); 919 | head = &d[1]; 920 | } else { 921 | return Err(RuntimeErr { 922 | msg: "not char".to_string(), 923 | pos, 924 | }); 925 | } 926 | } else { 927 | return Err(RuntimeErr { 928 | msg: "invalid cons".to_string(), 929 | pos, 930 | }); 931 | } 932 | } else if data.get_ldata().label == "Nil" { 933 | break; 934 | } else { 935 | return Err(RuntimeErr { 936 | msg: "not list".to_string(), 937 | pos, 938 | }); 939 | } 940 | } 941 | } 942 | let ptr = env.root.make_str(s); 943 | Ok(RTData::Str(ptr)) 944 | } 945 | "call-rust" => { 946 | let (n1, n2, n3) = get_int_int_int(args, pos)?; 947 | let n = unsafe { (env.ctx.callback)(&*n1, &*n2, &*n3) }; 948 | if let Some(n) = n { 949 | let n = RTData::Int(env.root.make_int(n)); 950 | let ptr = env.root.make_obj("Some".to_string(), Some(vec![n])); 951 | Ok(RTData::LData(ptr)) 952 | } else { 953 | let ptr = env.root.make_obj("None".to_string(), None); 954 | Ok(RTData::LData(ptr)) 955 | } 956 | } 957 | _ => Err(RuntimeErr { 958 | msg: "unknown built-in function".to_string(), 959 | pos, 960 | }), 961 | } 962 | } 963 | 964 | fn eval_match( 965 | expr: &semantics::MatchNode, 966 | env: &mut Environment<'_>, 967 | ) -> Result { 968 | let data = eval_expr(&expr.expr, env)?; 969 | 970 | for c in &expr.cases { 971 | env.vars.back_mut().unwrap().push(); 972 | if eval_pat(&c.pattern, data.clone(), env.vars) { 973 | let retval = eval_expr(&c.expr, env)?; 974 | env.vars.back_mut().unwrap().pop(); 975 | return Ok(retval); 976 | } 977 | env.vars.back_mut().unwrap().pop(); 978 | } 979 | 980 | let pos = expr.pos; 981 | Err(RuntimeErr { 982 | msg: "pattern-matching is not exhaustive".to_string(), 983 | pos, 984 | }) 985 | } 986 | 987 | fn eval_id(expr: &semantics::IDNode, vars: &mut VecDeque) -> RTData { 988 | let id = expr.id.to_string(); 989 | get_data_of_id(&id, vars) 990 | } 991 | 992 | fn eval_list(expr: &semantics::Exprs, env: &mut Environment<'_>) -> Result { 993 | let mut elm = env.root.make_obj("Nil".to_string(), None); 994 | for e in expr.exprs.iter().rev() { 995 | let val = eval_expr(e, env)?; 996 | elm = env 997 | .root 998 | .make_obj("Cons".to_string(), Some(vec![val, RTData::LData(elm)])); 999 | } 1000 | 1001 | Ok(RTData::LData(elm)) 1002 | } 1003 | 1004 | fn eval_if(expr: &semantics::IfNode, env: &mut Environment<'_>) -> Result { 1005 | let cond = eval_expr(&expr.cond_expr, env)?; 1006 | let flag = match cond { 1007 | RTData::Bool(e) => e, 1008 | _ => { 1009 | let pos = expr.cond_expr.get_pos(); 1010 | return Err(RuntimeErr { 1011 | msg: "type mismatched".to_string(), 1012 | pos, 1013 | }); 1014 | } 1015 | }; 1016 | 1017 | if flag { 1018 | eval_expr(&expr.then_expr, env) 1019 | } else { 1020 | eval_expr(&expr.else_expr, env) 1021 | } 1022 | } 1023 | 1024 | fn eval_data(expr: &semantics::DataNode, env: &mut Environment<'_>) -> Result { 1025 | let data = if expr.exprs.is_empty() { 1026 | None 1027 | } else { 1028 | let mut v = Vec::new(); 1029 | for e in &expr.exprs { 1030 | v.push(eval_expr(e, env)?); 1031 | } 1032 | Some(v) 1033 | }; 1034 | 1035 | let ptr = env.root.make_obj(expr.label.id.to_string(), data); 1036 | 1037 | Ok(RTData::LData(ptr)) 1038 | } 1039 | 1040 | fn eval_let(expr: &semantics::LetNode, env: &mut Environment<'_>) -> Result { 1041 | env.vars.back_mut().unwrap().push(); 1042 | 1043 | for def in &expr.def_vars { 1044 | let data = eval_expr(&def.expr, env)?; 1045 | if !eval_pat(&def.pattern, data, env.vars) { 1046 | let pos = def.pattern.get_pos(); 1047 | return Err(RuntimeErr { 1048 | msg: "failed pattern matching".to_string(), 1049 | pos, 1050 | }); 1051 | } 1052 | } 1053 | 1054 | let result = eval_expr(&expr.expr, env)?; 1055 | env.vars.back_mut().unwrap().pop(); 1056 | 1057 | Ok(result) 1058 | } 1059 | 1060 | fn eval_pat(pat: &Pattern, data: RTData, vars: &mut VecDeque) -> bool { 1061 | match pat { 1062 | Pattern::PatID(p) => { 1063 | vars.back_mut().unwrap().insert(p.id.to_string(), data); 1064 | true 1065 | } 1066 | Pattern::PatStr(p) => match data { 1067 | RTData::Str(n) => n.get_string() == &p.str, 1068 | _ => false, 1069 | }, 1070 | Pattern::PatChar(p) => match data { 1071 | RTData::Char(n) => n == p.c, 1072 | _ => false, 1073 | }, 1074 | Pattern::PatNum(p) => match data { 1075 | RTData::Int(n) => n.get_int() == &p.num, 1076 | _ => false, 1077 | }, 1078 | Pattern::PatBool(p) => match data { 1079 | RTData::Bool(n) => n == p.val, 1080 | _ => false, 1081 | }, 1082 | Pattern::PatNil(_) => match data { 1083 | RTData::LData(ptr) => ptr.get_ldata().label == "Nil", 1084 | _ => false, 1085 | }, 1086 | Pattern::PatTuple(p) => match data { 1087 | RTData::LData(ptr) => { 1088 | if ptr.get_ldata().label != "Tuple" { 1089 | return false; 1090 | } 1091 | 1092 | match &ptr.get_ldata().data { 1093 | Some(rds) => { 1094 | for (pat2, rd) in p.pattern.iter().zip(rds.iter()) { 1095 | if !eval_pat(pat2, rd.clone(), vars) { 1096 | return false; 1097 | } 1098 | } 1099 | true 1100 | } 1101 | None => true, 1102 | } 1103 | } 1104 | _ => false, 1105 | }, 1106 | Pattern::PatData(p) => match data { 1107 | RTData::LData(ptr) => { 1108 | if ptr.get_ldata().label != p.label.id { 1109 | return false; 1110 | } 1111 | 1112 | match &ptr.get_ldata().data { 1113 | Some(rds) => { 1114 | for (pat2, rd) in p.pattern.iter().zip(rds.iter()) { 1115 | if !eval_pat(pat2, rd.clone(), vars) { 1116 | return false; 1117 | } 1118 | } 1119 | true 1120 | } 1121 | None => true, 1122 | } 1123 | } 1124 | _ => false, 1125 | }, 1126 | } 1127 | } 1128 | 1129 | /// do garbage collection 1130 | fn collect_garbage(vars: &mut VecDeque, root: &mut RootObject) { 1131 | let n = root.len(); 1132 | if n < root.threshold { 1133 | return; 1134 | } 1135 | 1136 | mark(vars); 1137 | sweep(&mut root.clojure); 1138 | sweep(&mut root.objects); 1139 | sweep(&mut root.integers); 1140 | sweep(&mut root.strings); 1141 | 1142 | let n = root.len(); 1143 | root.threshold = n * 2; 1144 | if root.threshold < (MIN_GC_NUM >> 1) { 1145 | root.threshold = MIN_GC_NUM; 1146 | } 1147 | } 1148 | 1149 | /// mark reachable objects 1150 | fn mark(vars: &mut VecDeque) { 1151 | for v in vars.iter_mut() { 1152 | for var in v.vars.iter_mut() { 1153 | for (_, v) in var.iter_mut() { 1154 | mark_obj(v); 1155 | } 1156 | } 1157 | } 1158 | } 1159 | 1160 | /// mark reachable objects recursively 1161 | fn mark_obj(data: &mut RTData) { 1162 | match data { 1163 | RTData::Str(ptr) => unsafe { 1164 | write_volatile(ptr.get_ref(), true); 1165 | }, 1166 | RTData::Int(ptr) => unsafe { 1167 | write_volatile(ptr.get_ref(), true); 1168 | }, 1169 | RTData::Lambda(ptr) => unsafe { 1170 | if !read_volatile(ptr.get_ref()) { 1171 | write_volatile(ptr.get_ref(), true); 1172 | if let Some(data) = &mut ptr.get_clojure_mut().data { 1173 | for (_, v) in data.iter_mut() { 1174 | mark_obj(v); 1175 | } 1176 | } 1177 | } 1178 | }, 1179 | RTData::LData(ptr) => unsafe { 1180 | if !read_volatile(ptr.get_ref()) { 1181 | write_volatile(ptr.get_ref(), true); 1182 | if let Some(data) = &mut ptr.get_ldata_mut().data { 1183 | for v in data.iter_mut() { 1184 | mark_obj(v); 1185 | } 1186 | } 1187 | } 1188 | }, 1189 | _ => (), 1190 | } 1191 | } 1192 | 1193 | /// remove unreachable objects 1194 | fn sweep(root: &mut LinkedList>>) { 1195 | let mut tail = root.split_off(0); 1196 | loop { 1197 | if tail.is_empty() { 1198 | break; 1199 | } 1200 | 1201 | // take head 1202 | let mut head; 1203 | if tail.len() == 1 { 1204 | head = tail.split_off(0); 1205 | } else { 1206 | let tmp = tail.split_off(1); 1207 | head = tail; 1208 | tail = tmp; 1209 | }; 1210 | 1211 | // check the head is reachable or not 1212 | let h = head.front_mut().unwrap(); 1213 | let marked = unsafe { read_volatile(&h.as_ref().1) }; 1214 | let flag = if marked { 1215 | // the head is reachable 1216 | let h = h.as_mut(); 1217 | unsafe { 1218 | h.get_unchecked_mut().1 = false; 1219 | } 1220 | true 1221 | } else { 1222 | // the head unreachable 1223 | false 1224 | }; 1225 | 1226 | // if reachable, append the head 1227 | if flag { 1228 | root.append(&mut head); 1229 | } 1230 | } 1231 | } 1232 | 1233 | pub trait RTDataToRust { 1234 | fn into(&self) -> T; 1235 | } 1236 | 1237 | /// Get a BigInt value. 1238 | impl RTDataToRust for RTData { 1239 | fn into(&self) -> BigInt { 1240 | if let RTData::Int(data) = self { 1241 | data.get_int().clone() 1242 | } else { 1243 | panic!("data is not BigInt"); 1244 | } 1245 | } 1246 | } 1247 | 1248 | /// Get a char value. 1249 | impl RTDataToRust for RTData { 1250 | fn into(&self) -> char { 1251 | if let RTData::Char(data) = self { 1252 | *data 1253 | } else { 1254 | panic!("data is not Char"); 1255 | } 1256 | } 1257 | } 1258 | 1259 | /// Get a String value. 1260 | impl RTDataToRust for RTData { 1261 | fn into(&self) -> String { 1262 | if let RTData::Str(data) = self { 1263 | data.get_string().clone() 1264 | } else { 1265 | panic!("data is not String"); 1266 | } 1267 | } 1268 | } 1269 | 1270 | /// Get a boolean value. 1271 | impl RTDataToRust for RTData { 1272 | fn into(&self) -> bool { 1273 | if let RTData::Bool(data) = self { 1274 | *data 1275 | } else { 1276 | panic!("data is not Bool"); 1277 | } 1278 | } 1279 | } 1280 | 1281 | /// Convert a BLisp's List to a Rust's Vec. 1282 | impl RTDataToRust> for RTData 1283 | where 1284 | RTData: RTDataToRust, 1285 | { 1286 | fn into(&self) -> Vec { 1287 | if let RTData::LData(data) = self { 1288 | let ldata = data.get_ldata(); 1289 | let mut result = Vec::new(); 1290 | list_to_vec(ldata, &mut result); 1291 | 1292 | return result; 1293 | } 1294 | 1295 | panic!("data is not List"); 1296 | } 1297 | } 1298 | 1299 | /// Convert a BLisp's Option to a Rust's Option. 1300 | impl RTDataToRust> for RTData 1301 | where 1302 | RTData: RTDataToRust, 1303 | { 1304 | fn into(&self) -> Option { 1305 | if let RTData::LData(data) = self { 1306 | let ldata = data.get_ldata(); 1307 | match ldata.label.as_str() { 1308 | "Some" => { 1309 | if let Some(v) = &ldata.data { 1310 | let e: T = RTDataToRust::into(&v[0]); 1311 | Some(e) 1312 | } else { 1313 | panic!("invalid Some") 1314 | } 1315 | } 1316 | "None" => None, 1317 | _ => panic!("label is neither Some nor None"), 1318 | } 1319 | } else { 1320 | panic!("data is not Option"); 1321 | } 1322 | } 1323 | } 1324 | 1325 | /// Convert a BLisp's list to a Rust's Vec. 1326 | fn list_to_vec(mut ldata: &LabeledData, result: &mut Vec) 1327 | where 1328 | RTData: RTDataToRust, 1329 | { 1330 | loop { 1331 | match ldata.label.as_str() { 1332 | "Cons" => { 1333 | if let Some(v) = &ldata.data { 1334 | let e: T = RTDataToRust::into(&v[0]); 1335 | result.push(e); 1336 | 1337 | if let RTData::LData(data) = &v[1] { 1338 | ldata = data.get_ldata(); 1339 | } else { 1340 | panic!("no next in Cons") 1341 | } 1342 | } else { 1343 | panic!("invalid Cons"); 1344 | } 1345 | } 1346 | "Nil" => break, 1347 | _ => panic!("label is neither Cons nor Nil"), 1348 | } 1349 | } 1350 | } 1351 | 1352 | /// Convert a BLisp's Result to a Rust's Result. 1353 | impl RTDataToRust> for RTData 1354 | where 1355 | RTData: RTDataToRust + RTDataToRust, 1356 | { 1357 | fn into(&self) -> Result { 1358 | if let RTData::LData(data) = self { 1359 | let ldata = data.get_ldata(); 1360 | match ldata.label.as_str() { 1361 | "Ok" => { 1362 | if let Some(v) = &ldata.data { 1363 | let e: T = RTDataToRust::into(&v[0]); 1364 | Ok(e) 1365 | } else { 1366 | panic!("invalid Ok") 1367 | } 1368 | } 1369 | "Err" => { 1370 | if let Some(v) = &ldata.data { 1371 | let e: E = RTDataToRust::into(&v[0]); 1372 | Err(e) 1373 | } else { 1374 | panic!("invalid Err") 1375 | } 1376 | } 1377 | _ => panic!("label is neither Ok nor Err"), 1378 | } 1379 | } else { 1380 | panic!("data is not Result"); 1381 | } 1382 | } 1383 | } 1384 | 1385 | macro_rules! impl_rt_data_to_rust_tuple { 1386 | ($($(#[$impl_attrs:meta])* 1387 | [ $(($index:literal, $name_snake:ident, $name_pascal:ident)),+ $(,)? ]),+ $(,)?) => { 1388 | $($(#[$impl_attrs])* 1389 | impl<$($name_pascal),*> RTDataToRust<($($name_pascal),*)> for RTData 1390 | where $(RTData: RTDataToRust<$name_pascal>),* { 1391 | fn into(&self) -> ($($name_pascal),*) { 1392 | if let RTData::LData(data) = self { 1393 | let ldata = data.get_ldata(); 1394 | if ldata.label.as_str() == "Tuple" { 1395 | if let Some(v) = &ldata.data { 1396 | $(let $name_snake: $name_pascal = RTDataToRust::into(&v[$index]);)* 1397 | 1398 | ($($name_snake),*) 1399 | } else { 1400 | panic!("invalid Tuple") 1401 | } 1402 | } else { 1403 | panic!("label is not Tuple") 1404 | } 1405 | } else { 1406 | panic!("data is not a Tuple") 1407 | } 1408 | } 1409 | })* 1410 | } 1411 | } 1412 | impl_rt_data_to_rust_tuple![ 1413 | /// Convert a BLisp's Tuple to a Rust's Tuple 1414 | /// where the length is 2. 1415 | [ 1416 | (0, v0, T0), 1417 | (1, v1, T1), 1418 | ], 1419 | /// Convert a BLisp's Tuple to a Rust's Tuple 1420 | /// where the length is 3. 1421 | [ 1422 | (0, v0, T0), 1423 | (1, v1, T1), 1424 | (2, v2, T2), 1425 | ], 1426 | /// Convert a BLisp's Tuple to a Rust's Tuple 1427 | /// where the length is 4. 1428 | [ 1429 | (0, v0, T0), 1430 | (1, v1, T1), 1431 | (2, v2, T2), 1432 | (3, v3, T3), 1433 | ], 1434 | /// Convert a BLisp's Tuple to a Rust's Tuple 1435 | /// where the length is 5. 1436 | [ 1437 | (0, v0, T0), 1438 | (1, v1, T1), 1439 | (2, v2, T2), 1440 | (3, v3, T3), 1441 | (4, v4, T4), 1442 | ], 1443 | /// Convert a BLisp's Tuple to a Rust's Tuple 1444 | /// where the length is 6. 1445 | [ 1446 | (0, v0, T0), 1447 | (1, v1, T1), 1448 | (2, v2, T2), 1449 | (3, v3, T3), 1450 | (4, v4, T4), 1451 | (5, v5, T5), 1452 | ], 1453 | /// Convert a BLisp's Tuple to a Rust's Tuple 1454 | /// where the length is 7. 1455 | [ 1456 | (0, v0, T0), 1457 | (1, v1, T1), 1458 | (2, v2, T2), 1459 | (3, v3, T3), 1460 | (4, v4, T4), 1461 | (5, v5, T5), 1462 | (6, v6, T6), 1463 | ], 1464 | /// Convert a BLisp's Tuple to a Rust's Tuple 1465 | /// where the length is 8. 1466 | [ 1467 | (0, v0, T0), 1468 | (1, v1, T1), 1469 | (2, v2, T2), 1470 | (3, v3, T3), 1471 | (4, v4, T4), 1472 | (5, v5, T5), 1473 | (6, v6, T6), 1474 | (7, v7, T7), 1475 | ], 1476 | /// Convert a BLisp's Tuple to a Rust's Tuple 1477 | /// where the length is 9. 1478 | [ 1479 | (0, v0, T0), 1480 | (1, v1, T1), 1481 | (2, v2, T2), 1482 | (3, v3, T3), 1483 | (4, v4, T4), 1484 | (5, v5, T5), 1485 | (6, v6, T6), 1486 | (7, v7, T7), 1487 | (8, v8, T8), 1488 | ], 1489 | /// Convert a BLisp's Tuple to a Rust's Tuple 1490 | /// where the length is 10. 1491 | [ 1492 | (0, v0, T0), 1493 | (1, v1, T1), 1494 | (2, v2, T2), 1495 | (3, v3, T3), 1496 | (4, v4, T4), 1497 | (5, v5, T5), 1498 | (6, v6, T6), 1499 | (7, v7, T7), 1500 | (8, v8, T8), 1501 | (9, v9, T9), 1502 | ], 1503 | /// Convert a BLisp's Tuple to a Rust's Tuple 1504 | /// where the length is 11. 1505 | [ 1506 | (0, v0, T0), 1507 | (1, v1, T1), 1508 | (2, v2, T2), 1509 | (3, v3, T3), 1510 | (4, v4, T4), 1511 | (5, v5, T5), 1512 | (6, v6, T6), 1513 | (7, v7, T7), 1514 | (8, v8, T8), 1515 | (9, v9, T9), 1516 | (10, v10, T10), 1517 | ], 1518 | /// Convert a BLisp's Tuple to a Rust's Tuple 1519 | /// where the length is 12. 1520 | [ 1521 | (0, v0, T0), 1522 | (1, v1, T1), 1523 | (2, v2, T2), 1524 | (3, v3, T3), 1525 | (4, v4, T4), 1526 | (5, v5, T5), 1527 | (6, v6, T6), 1528 | (7, v7, T7), 1529 | (8, v8, T8), 1530 | (9, v9, T9), 1531 | (10, v10, T10), 1532 | (11, v11, T11), 1533 | ], 1534 | ]; 1535 | 1536 | pub trait RustToRTData { 1537 | fn from(env: &mut Environment<'_>, value: T) -> Self; 1538 | } 1539 | 1540 | impl RustToRTData for RTData { 1541 | fn from(env: &mut Environment<'_>, value: BigInt) -> Self { 1542 | RTData::Int(env.root.make_int(value)) 1543 | } 1544 | } 1545 | 1546 | impl RustToRTData for RTData { 1547 | fn from(_env: &mut Environment<'_>, value: char) -> Self { 1548 | RTData::Char(value) 1549 | } 1550 | } 1551 | 1552 | impl RustToRTData for RTData { 1553 | fn from(_env: &mut Environment<'_>, value: bool) -> Self { 1554 | RTData::Bool(value) 1555 | } 1556 | } 1557 | 1558 | impl RustToRTData for RTData { 1559 | fn from(env: &mut Environment<'_>, value: String) -> Self { 1560 | RTData::Str(env.root.make_str(value)) 1561 | } 1562 | } 1563 | 1564 | impl RustToRTData> for RTData 1565 | where 1566 | RTData: RustToRTData, 1567 | { 1568 | fn from(env: &mut Environment<'_>, value: Option) -> Self { 1569 | if let Some(value) = value { 1570 | let value = RustToRTData::from(env, value); 1571 | RTData::LData(env.root.make_obj("Some".to_string(), Some(vec![value]))) 1572 | } else { 1573 | RTData::LData(env.root.make_obj("None".to_string(), None)) 1574 | } 1575 | } 1576 | } 1577 | 1578 | impl RustToRTData> for RTData 1579 | where 1580 | RTData: RustToRTData + RustToRTData, 1581 | { 1582 | fn from(env: &mut Environment<'_>, value: Result) -> Self { 1583 | match value { 1584 | Ok(value) => { 1585 | let value = RustToRTData::from(env, value); 1586 | RTData::LData(env.root.make_obj("Ok".to_string(), Some(vec![value]))) 1587 | } 1588 | Err(value) => { 1589 | let value = RustToRTData::from(env, value); 1590 | RTData::LData(env.root.make_obj("Err".to_string(), Some(vec![value]))) 1591 | } 1592 | } 1593 | } 1594 | } 1595 | 1596 | impl RustToRTData<()> for RTData { 1597 | fn from(env: &mut Environment<'_>, _: ()) -> Self { 1598 | RTData::LData(env.root.make_obj("Tuple".to_string(), Some(vec![]))) 1599 | } 1600 | } 1601 | 1602 | macro_rules! impl_rust_to_rt_data_tuple { 1603 | ($([ $(($name_snake:ident, $name_pascal:ident)),+ $(,)? ]),+ $(,)?) => { 1604 | $(impl<$($name_pascal),*> RustToRTData<($($name_pascal),*)> for RTData 1605 | where $(RTData: RustToRTData<$name_pascal>),* { 1606 | fn from(env: &mut Environment<'_>, ($($name_snake),*): ($($name_pascal),*)) -> Self { 1607 | $(let $name_snake = >::from(env, $name_snake);)* 1608 | RTData::LData(env.root.make_obj("Tuple".to_string(), Some(vec![$($name_snake),*]))) 1609 | } 1610 | })* 1611 | } 1612 | } 1613 | impl_rust_to_rt_data_tuple![ 1614 | [(v0, V0), (v1, V1),], 1615 | [(v0, V0), (v1, V1), (v2, V2),], 1616 | [(v0, V0), (v1, V1), (v2, V2), (v3, V3),], 1617 | [(v0, V0), (v1, V1), (v2, V2), (v3, V3), (v4, V4),], 1618 | [(v0, V0), (v1, V1), (v2, V2), (v3, V3), (v4, V4), (v5, V5),], 1619 | [ 1620 | (v0, V0), 1621 | (v1, V1), 1622 | (v2, V2), 1623 | (v3, V3), 1624 | (v4, V4), 1625 | (v5, V5), 1626 | (v6, V6), 1627 | ], 1628 | [ 1629 | (v0, V0), 1630 | (v1, V1), 1631 | (v2, V2), 1632 | (v3, V3), 1633 | (v4, V4), 1634 | (v5, V5), 1635 | (v6, V6), 1636 | (v7, V7), 1637 | ], 1638 | [ 1639 | (v0, V0), 1640 | (v1, V1), 1641 | (v2, V2), 1642 | (v3, V3), 1643 | (v4, V4), 1644 | (v5, V5), 1645 | (v6, V6), 1646 | (v7, V7), 1647 | (v8, V8), 1648 | ], 1649 | [ 1650 | (v0, V0), 1651 | (v1, V1), 1652 | (v2, V2), 1653 | (v3, V3), 1654 | (v4, V4), 1655 | (v5, V5), 1656 | (v6, V6), 1657 | (v7, V7), 1658 | (v8, V8), 1659 | (v9, V9), 1660 | ], 1661 | [ 1662 | (v0, V0), 1663 | (v1, V1), 1664 | (v2, V2), 1665 | (v3, V3), 1666 | (v4, V4), 1667 | (v5, V5), 1668 | (v6, V6), 1669 | (v7, V7), 1670 | (v8, V8), 1671 | (v9, V9), 1672 | (v10, V10), 1673 | ], 1674 | [ 1675 | (v0, V0), 1676 | (v1, V1), 1677 | (v2, V2), 1678 | (v3, V3), 1679 | (v4, V4), 1680 | (v5, V5), 1681 | (v6, V6), 1682 | (v7, V7), 1683 | (v8, V8), 1684 | (v9, V9), 1685 | (v10, V10), 1686 | (v11, V11), 1687 | ], 1688 | ]; 1689 | 1690 | impl RustToRTData> for RTData 1691 | where 1692 | RTData: RustToRTData, 1693 | { 1694 | fn from(env: &mut Environment<'_>, vec: Vec) -> Self { 1695 | Self::LData(collection_to_list(env, vec.into_iter())) 1696 | } 1697 | } 1698 | impl RustToRTData<[T; N]> for RTData 1699 | where 1700 | RTData: RustToRTData, 1701 | { 1702 | fn from(env: &mut Environment<'_>, slice: [T; N]) -> Self { 1703 | Self::LData(collection_to_list(env, slice.into_iter())) 1704 | } 1705 | } 1706 | 1707 | /// Convert a collection into a cons list. 1708 | fn collection_to_list(env: &mut Environment<'_>, iter: I) -> LDataType 1709 | where 1710 | I: DoubleEndedIterator, 1711 | RTData: RustToRTData, 1712 | { 1713 | let mut iter = iter 1714 | .map(|item| { 1715 | ( 1716 | "Cons".to_string(), 1717 | Some(vec![>::from(env, item)]), 1718 | ) 1719 | }) 1720 | .chain([("Nil".to_string(), None)].into_iter()) 1721 | .collect::>() 1722 | .into_iter() 1723 | .map(|(label, data)| env.root.make_obj(label, data)) 1724 | .rev() 1725 | .peekable(); 1726 | 1727 | let mut root_cons = None; 1728 | while let Some(item) = iter.next() { 1729 | match iter.peek_mut() { 1730 | Some(next) => { 1731 | next.get_ldata_mut() 1732 | .data 1733 | .as_mut() 1734 | .expect("all items after a nil should contain a value") 1735 | .push(RTData::LData(item)); 1736 | } 1737 | None => { 1738 | root_cons = Some(item); 1739 | } 1740 | } 1741 | } 1742 | 1743 | root_cons.expect("chaining a nil should ensure that the iterator always has a value") 1744 | } 1745 | 1746 | pub trait FFI { 1747 | /// Extern expression of BLisp 1748 | fn blisp_extern(&self) -> &'static str; 1749 | 1750 | /// Return the corresponding FFI. 1751 | fn ffi(&self) -> fn(env: &mut Environment<'_>, args: &[RTData]) -> RTData; 1752 | 1753 | /// The function name. 1754 | fn name(&self) -> &'static str; 1755 | } 1756 | --------------------------------------------------------------------------------