├── .gitignore ├── install.sh ├── Dockerfile ├── steak.cabal ├── LICENSE ├── examples ├── lazy.cpp ├── steak_demo.stk.cpp └── cpp17_demo.cpp ├── stack.yaml ├── src ├── steak_h_codegen.hs └── compiler.hs ├── include ├── ret_type_impl.h ├── runtime.h └── steak.h ├── Readme.md └── doc ├── Learning C++ 11 By Looking Inside Steak.md └── steak 语言的设计与实现.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.out 2 | *.tmp 3 | *.log 4 | .vscode/ 5 | .stack-work/ 6 | .stack-work-win/ 7 | test* 8 | .DS* -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | stack install 3 | sudo mkdir /usr/include/steak 4 | sudo cp /tmp/steak/include/steak.h /usr/include/steak 5 | sudo cp /tmp/steak/include/runtime.h /usr/include/steak -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | LABEL maintainer="nicekingwei@foxmail.com" 4 | 5 | 6 | RUN apt update 7 | RUN apt install -y gcc g++ git haskell-stack 8 | RUN /usr/bin/stack upgrade 9 | RUN cd /tmp && git clone http://www.github.com/nicekingwei/steak && cd steak && /root/.local/bin/stack install 10 | RUN mv /root/.local/bin/steak /usr/bin 11 | RUN mkdir /usr/include/steak && cp /tmp/steak/include/steak.h /usr/include/steak && cp /tmp/steak/include/runtime.h /usr/include/steak && cp /tmp/steak/include/ret_type_impl.h /usr/include/steak -------------------------------------------------------------------------------- /steak.cabal: -------------------------------------------------------------------------------- 1 | name: steak 2 | version: 1.0 3 | homepage: https://github.com/nicekingwei/steak 4 | license: MIT 5 | license-file: LICENSE 6 | author: Long Jinwei 7 | maintainer: nicekingwei@foxmail.com 8 | copyright: 2019 Long Jinwei 9 | build-type: Simple 10 | extra-source-files: Readme.md 11 | cabal-version: >=1.20 12 | 13 | executable steak 14 | hs-source-dirs: src 15 | main-is: compiler.hs 16 | ghc-options: -threaded -rtsopts -with-rtsopts=-N 17 | build-depends: base >=4.7 && <5, 18 | bytestring >= 0.10.8.2, 19 | transformers >= 0.5.2.0, 20 | parsec>=3.1.13.0, 21 | filepath>=1.4.1.2, 22 | directory>=1.3.0.2 23 | default-language: Haskell2010 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Long Jinwei 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 | -------------------------------------------------------------------------------- /examples/lazy.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../include/runtime.h" 4 | 5 | using namespace std; 6 | using namespace steak; 7 | 8 | int time_consuming_compute() { 9 | cout << "start computing\n"; 10 | sleep(1); 11 | cout << "end computing\n"; 12 | return 666; 13 | } 14 | 15 | void f(std::pair x) { 16 | cout << x.second << endl; 17 | } 18 | 19 | void lazy_unit_test() { 20 | auto l4 = lazy_call(time_consuming_compute).async(); 21 | 22 | sleep(1); 23 | 24 | auto l1 = lazy_type_t(1); 25 | auto l2 = l1.transform([](int x) { return 'a'; }); 26 | const auto l3 = lazy_call([]() { 27 | cout << "eval l3\n"; 28 | return 'v'; 29 | }); 30 | 31 | cout << l2 << endl; 32 | cout << l3 << endl; 33 | 34 | cout << l4 << endl; 35 | cout << l4 << endl; 36 | 37 | lazy_type_t> l5(std::pair(1, 'a')); 38 | cout << l5.get().first; 39 | f(l5); 40 | 41 | auto l6 = lazy_type_t(1) 42 | | [](int x) { return x + 1; } 43 | | [](int x) { return x * 2; } 44 | | [](int x) { 45 | cout << x << endl; 46 | return "hello world"; 47 | }; 48 | 49 | cout << l6 << endl; 50 | } 51 | 52 | 53 | int main() { 54 | lazy_unit_test(); 55 | return 0; 56 | } -------------------------------------------------------------------------------- /examples/steak_demo.stk.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../include/runtime.h" 3 | 4 | using namespace std; 5 | 6 | int f() 7 | { 8 | char c; 9 | switch(c) 10 | { 11 | case 0: 12 | cout<<1; 13 | default: 14 | cout<<3; 15 | } 16 | return 0; 17 | } 18 | 19 | dataclass Peano 20 | { 21 | Peano Z(); /* zero */ 22 | Peano S(Peano); /* succ */ 23 | }; 24 | 25 | 26 | template 27 | dataclass Maybe 28 | { 29 | Maybe Nothing(); 30 | Maybe Just(a); 31 | }; 32 | 33 | template 34 | dataclass List 35 | { 36 | List Nil(); 37 | List Cons(a,List); 38 | }; 39 | 40 | int maybe_sum(List> l) 41 | { 42 | int x=0,y=0; 43 | bool flag=true; 44 | while(flag) 45 | { 46 | match(l) 47 | { 48 | case Cons(Just(y),l): 49 | x+=y; 50 | break; 51 | case Cons(Nothing(),l): 52 | break; 53 | case Nil(): 54 | flag=false; 55 | break; 56 | } 57 | } 58 | return x; 59 | } 60 | 61 | int main() 62 | { 63 | auto l = Cons(Just(1),Nil>()); 64 | l = Cons(Nothing(),l); 65 | l = Cons(Just(3),l); 66 | cout< 6 | #include "../include/steak.h" 7 | 8 | using namespace std; 9 | 10 | RegCons(Nil) RegCons(Cons) 11 | Data(Va,List,RealType(Nil),RealType(Cons,a,List)) 12 | Cons0(Va,Nil,List) 13 | Cons2(Va,Cons,List,a,List) 14 | DerivingShow(Va,List) 15 | 16 | RegCons(Nothing) RegCons(Just) 17 | Data(Va,Maybe,RealType(Nothing),RealType(Just,a)) 18 | Cons0(Va,Nothing,Maybe) 19 | Cons1(Va,Just,Maybe,a) 20 | DerivingShow(Va,Maybe) 21 | 22 | RegCons(Z) RegCons(S) 23 | Data(V,Peano,RealType(Z),RealType(S,Peano)) 24 | Cons0(V,Z,Peano) 25 | Cons1(V,S,Peano,Peano) 26 | DerivingShow(V,Peano) 27 | 28 | 29 | Va std::ostream& operator << (std::ostream& out,List l) 30 | { 31 | out<<'['; 32 | bool first=true; 33 | while(true) 34 | { 35 | a x; 36 | With(l) 37 | Case(Cons,x,l) 38 | if(first) first=false; 39 | else out<<','; 40 | out<>> l) 49 | { 50 | int x=0,y=0; 51 | while(1) 52 | { 53 | With(l) 54 | Case(Cons,UnZip(Just,y),l) 55 | x+=y; 56 | Case(Nil) 57 | break; 58 | Case(Cons,UnZip(Nothing),l) 59 | ; 60 | EndWith() 61 | } 62 | return x; 63 | } 64 | 65 | int main() 66 | { 67 | auto l1=Nil>(); 68 | l1=Cons(Just(4),l1); 69 | l1=Cons(Nothing(),l1); 70 | l1=Cons(Just(6),l1); 71 | cout<(1,Nil()); 75 | auto y=Cons(steak::lazy_type_t(1),Nil()); 76 | 77 | return 0; 78 | } -------------------------------------------------------------------------------- /stack.yaml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by 'stack init' 2 | # 3 | # Some commonly used options have been documented as comments in this file. 4 | # For advanced use and comprehensive documentation of the format, please see: 5 | # https://docs.haskellstack.org/en/stable/yaml_configuration/ 6 | 7 | # Resolver to choose a 'specific' stackage snapshot or a compiler version. 8 | # A snapshot resolver dictates the compiler version and the set of packages 9 | # to be used for project dependencies. For example: 10 | # 11 | # resolver: lts-3.5 12 | # resolver: nightly-2015-09-21 13 | # resolver: ghc-7.10.2 14 | # resolver: ghcjs-0.1.0_ghc-7.10.2 15 | # resolver: 16 | # name: custom-snapshot 17 | # location: "./custom-snapshot.yaml" 18 | resolver: lts-10.8 19 | 20 | # User packages to be built. 21 | # Various formats can be used as shown in the example below. 22 | # 23 | # packages: 24 | # - some-directory 25 | # - https://example.com/foo/bar/baz-0.0.2.tar.gz 26 | # - location: 27 | # git: https://github.com/commercialhaskell/stack.git 28 | # commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a 29 | # - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a 30 | # extra-dep: true 31 | # subdirs: 32 | # - auto-update 33 | # - wai 34 | # 35 | # A package marked 'extra-dep: true' will only be built if demanded by a 36 | # non-dependency (i.e. a user package), and its test suites and benchmarks 37 | # will not be run. This is useful for tweaking upstream packages. 38 | packages: 39 | - . 40 | # Dependency packages to be pulled from upstream that are not in the resolver 41 | # (e.g., acme-missiles-0.3) 42 | extra-deps: [ 43 | bytestring-0.10.8.2, 44 | transformers-0.5.2.0, 45 | parsec-3.1.13.0, 46 | filepath-1.4.1.2, 47 | directory-1.3.0.2 48 | ] 49 | 50 | # Override default flag values for local packages and extra-deps 51 | flags: {} 52 | 53 | # Extra package databases containing global packages 54 | extra-package-dbs: [] 55 | 56 | # Control whether we use the GHC we find on the path 57 | # system-ghc: true 58 | # 59 | # Require a specific version of stack, using version ranges 60 | # require-stack-version: -any # Default 61 | # require-stack-version: ">=1.5" 62 | # 63 | # Override the architecture used by stack, especially useful on Windows 64 | # arch: i386 65 | # arch: x86_64 66 | # 67 | # Extra directories used by stack for building 68 | # extra-include-dirs: [/path/to/dir] 69 | # extra-lib-dirs: [/path/to/dir] 70 | # 71 | # Allow a newer minor version of GHC than the snapshot specifies 72 | # compiler-check: newer-minor -------------------------------------------------------------------------------- /src/steak_h_codegen.hs: -------------------------------------------------------------------------------- 1 | import Text.Printf 2 | import Data.Monoid 3 | 4 | mapJoin :: (a -> String) -> [a] -> String 5 | mapJoin f xs@(_ : _) = foldl1 (\acc s -> acc ++ "," ++ s) (map f xs) 6 | mapJoin _ [] = [] 7 | 8 | unsplit :: Char -> [String] -> String 9 | unsplit _ [] = mempty 10 | unsplit ss xs = foldr1 (\x acc -> mconcat [x, [ss], acc]) xs 11 | 12 | genCons :: Int -> String 13 | genCons n = 14 | let 15 | wrapLazyFun b = "return steak::lazy_call(std::function ( [=](){" <> b <> "} ) );" 16 | types = concatMap (printf ",t%d") [1 .. n] :: String 17 | genCon 18 | = let type_var = zip (map (\i -> "t" <> show i) [1 .. n]) [1 .. n] 19 | in 20 | "T inline steak::lazy_type_t name(" 21 | <> (unsplit ',') 22 | (map 23 | (\(a, i) -> 24 | "steak::lazy_type_t<" <> a <> "> v" <> show i 25 | ) 26 | type_var 27 | ) 28 | <> "){" 29 | <> wrapLazyFun 30 | ( "return ret(" 31 | <> printf 32 | "steak::case_class_t" 33 | types 34 | <> "(" 35 | <> ((unsplit ',') 36 | (map (\i -> "v" <> show i <> ".get()") 37 | [1 .. n] 38 | ) 39 | ) 40 | <> "));" 41 | ) 42 | <> "}\\\n" 43 | 44 | 45 | macro = 46 | printf "\t#define Cons%d(T,name,ret%s)\\\n" n types 47 | ++ genCon 48 | ++ "\n" 49 | in 50 | macro 51 | 52 | genVMacros :: Int -> String 53 | genVMacros n 54 | | n > 0 = printf "\t\t#define V%s template<%s>\n" 55 | (take n ['a' ..]) 56 | (mapJoin (printf "typename %c") (take n ['a' ..])) 57 | | n == 0 = "\t\t#define V\n" 58 | 59 | main = do 60 | let n = 20 61 | let 62 | head = 63 | "#ifndef _STEAK_H_\n#define _STEAK_H_\n" 64 | ++ "/**\n" 65 | ++ " * @brief a functional library for cpp\n" 66 | ++ " * @author nicekingwei(Long Jinwei)\n" 67 | ++ " */ \n" 68 | ++ "#include \n" 69 | ++ "#include \n" 70 | ++ "#include \n" 71 | ++ "#include \n" 72 | ++ "#include \n" 73 | ++ "#include \"runtime.h\"\n\n" 74 | ++ "#define With(x) if(1){auto match_var=x.get();if(0){;\n" 75 | ++ "#define Case(name,...) }else if(match_var.match(steak::attach_ghost(steak::forward_and_zip(__VA_ARGS__)))){;\n" 76 | ++ "#define Default }else{;\n" 77 | ++ "#define EndWith() }}\n" 78 | ++ "#define UnZip(name,...) steak::attach_ghost(steak::forward_and_zip(__VA_ARGS__))\n" 79 | ++ "#define Data(T,name,...) T struct name;T struct name:public steak::data_class_t<__VA_ARGS__>{name(){} templatename(K...args):steak::data_class_t<__VA_ARGS__>(args...){}};\n" 80 | ++ "#define RegCons(name) namespace steak_constructors{struct name {static constexpr const char* consname=#name;};};\n" 81 | ++ "#define RealType(name,...) steak::cal_cons_type::R<__VA_ARGS__>\n" 82 | ++ "#define DerivingShow(T,name) \\\nT std::ostream& operator << (std::ostream& out,name& x)\\\n{\\\n\treturn x.show(out);\\\n}\n\n" 83 | putStr head 84 | putStr "namespace steak\n{\n" 85 | putStr $ foldl1 (++) $ map genCons [0 .. n] 86 | putStr $ foldl1 (++) $ map genVMacros [0 .. n] 87 | putStr "};\n#endif" 88 | -------------------------------------------------------------------------------- /include/ret_type_impl.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @brief return_type_impl 3 | * @author https://stackoverflow.com/questions/27822277/finding-out-the-return-type-of-a-function-lambda-or-function 4 | */ 5 | template 6 | struct return_type_impl; 7 | 8 | template 9 | struct return_type_impl { 10 | using type = R; 11 | }; 12 | 13 | template 14 | struct return_type_impl { 15 | using type = R; 16 | }; 17 | 18 | template 19 | struct return_type_impl { 20 | using type = R; 21 | }; 22 | 23 | template 24 | struct return_type_impl { 25 | using type = R; 26 | }; 27 | 28 | template 29 | struct return_type_impl { 30 | using type = R; 31 | }; 32 | 33 | template 34 | struct return_type_impl { 35 | using type = R; 36 | }; 37 | 38 | template 39 | struct return_type_impl { 40 | using type = R; 41 | }; 42 | 43 | template 44 | struct return_type_impl { 45 | using type = R; 46 | }; 47 | 48 | template 49 | struct return_type_impl { 50 | using type = R; 51 | }; 52 | 53 | template 54 | struct return_type_impl { 55 | using type = R; 56 | }; 57 | 58 | template 59 | struct return_type_impl { 60 | using type = R; 61 | }; 62 | 63 | template 64 | struct return_type_impl { 65 | using type = R; 66 | }; 67 | 68 | template 69 | struct return_type_impl { 70 | using type = R; 71 | }; 72 | 73 | template 74 | struct return_type_impl { 75 | using type = R; 76 | }; 77 | 78 | template 79 | struct return_type_impl { 80 | using type = R; 81 | }; 82 | 83 | template 84 | struct return_type_impl { 85 | using type = R; 86 | }; 87 | 88 | template 89 | struct return_type_impl { 90 | using type = R; 91 | }; 92 | 93 | template 94 | struct return_type_impl { 95 | using type = R; 96 | }; 97 | 98 | template 99 | struct return_type_impl { 100 | using type = R; 101 | }; 102 | 103 | template 104 | struct return_type_impl { 105 | using type = R; 106 | }; 107 | 108 | template 109 | struct return_type_impl { 110 | using type = R; 111 | }; 112 | 113 | template 114 | struct return_type_impl { 115 | using type = R; 116 | }; 117 | 118 | template 119 | struct return_type_impl { 120 | using type = R; 121 | }; 122 | 123 | template 124 | struct return_type_impl { 125 | using type = R; 126 | }; 127 | 128 | template 129 | struct return_type_impl { 130 | using type = R; 131 | }; 132 | 133 | template 134 | struct return_type_impl { 135 | using type = R; 136 | }; 137 | 138 | template 139 | struct return_type_impl { 140 | using type = R; 141 | }; 142 | 143 | template 144 | struct return_type_impl { 145 | using type = R; 146 | }; 147 | 148 | template 149 | struct return_type_impl { 150 | using type = R; 151 | }; 152 | 153 | template 154 | struct return_type_impl { 155 | using type = R; 156 | }; 157 | 158 | template 159 | struct return_type 160 | : return_type_impl { 161 | }; 162 | 163 | template 164 | struct return_type 165 | : return_type_impl { 166 | }; 167 | 168 | template 169 | using return_type_t = typename return_type::type; -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Steak 1.0 2 | 3 | ### A programming language which provides generalized algebraic data types, pattern matching and lazy evaluation based on C++17. 4 | 5 | # Installation 6 | 7 | * As a domain specific language powered by C++ macros 8 | 9 | Just copy include/runtime.h, include/steak.h, include/ret_type_impl.h into your project and #include "steak.h". Be sure your C++ compiler support C++17 standard. 10 | 11 | * As a programming language powered by a compiler with C++17 backend. 12 | 13 | 1. install `haskell-stack` 14 | 1. clone this repository 15 | 1. run `stack install` 16 | 17 | * You can try it with docker, see the [Dockerfile](Dockerfile) in this repo. 18 | 19 | # Steak Language Documentation 20 | 21 | Steak programming language inherits C++17 completely except that `match` and `dataclass` are keywords. 22 | 23 | The extension names of Steak are `.stk.cpp`,`.stk.hpp` or `.stk.h`. The compiler will compile source files to c++17 code. 24 | 25 | ## Usage 26 | 27 | * steak file: compile the source file and print the object code to stdout 28 | * steak dirctory: search the directory recursively and compile all `*.stk.cpp`,`*.stk.h`,`*.stk.hpp` to `*.cpp`,`*.h`,`*.hpp` 29 | * steak --version: version and license 30 | * steak --help: help 31 | 32 | ## Language Specification 33 | * `dataclass` is for defining generalized algebraic data types. 34 | Usage: 35 | `[]` means optional 36 | 37 | ```c++ 38 | [template<...>] dataclass adt 39 | { 40 | return_type constructor1(...); 41 | return_type constructor2(...); 42 | ... 43 | }; 44 | ``` 45 | 46 | * `match` is for pattern matching. It's similar to `switch` expression. 47 | Usage: 48 | 49 | ```c++ 50 | match(var) 51 | { 52 | case Cons1(...): 53 | ... 54 | case Cons2(...): 55 | ... 56 | default: 57 | ... 58 | } 59 | ``` 60 | 61 | Examples are available at `examples/` 62 | 63 | ## Warnings 64 | 65 | * Remember that compiler won't deal with macros. As a result, don't use macros in `dataclass` or `match` expressions. 66 | * In the `case Cons(...)` expression, only literals and identifiers are allowed, operators and function call are not allowed.For example,`Just(x)` and `Just(1)` are valid, while `Just(x.c)` or `Just(1+3)` are invalid. Constructor with no paramenter also need to be followed by `()`, for example, `case Nothing()`. 67 | * If you know the implementations of the library and the compiler, you will understand all the strange things. 68 | 69 | # C++ DSL Documentation 70 | 71 | * generalized algebraic data types 72 | 73 | Generalized algebraic data types can be defined via C++ macros. 74 | 75 | Let's take 76 | ```haskell 77 | data List a = Cons a (List a) deriving Show 78 | ``` 79 | as an example: 80 | ```C++ 81 | RegCons(Nil) RegCons(Cons) 82 | Data(Va,List,RealType(Nil),RealType(Cons,a,List)) 83 | Cons0(Va,Nil,List) 84 | Cons2(Va,Cons,a,List,List) 85 | DerivingShow(Va,List) 86 | ``` 87 | Another example : 88 | ```haskell 89 | data Peano = Z | S Peano deriving Show 90 | ``` 91 | ```C++ 92 | RegCons(Z) RegCons(S) 93 | Data(V,Peano,RealType(Z),RealType(S,Peano)) 94 | Cons0(V,Z,Peano) 95 | Cons1(V,S,Peano,Peano) 96 | DerivingShow(V,Peano) 97 | ``` 98 | * At first, you should register the constructors' name using `RegCons`. 99 | * macro Data is for defining the data structure in C++, and macro RealType is for inferencing the actual type of case classes. 100 | * Va is short for `template`, while V means Nothing. If you want to use 2 type variables,you can use Vab. 101 | * Cons[d] can be used to define constructors,d is the number of parameters. `Cons2(Va,Cons,a,List,List)` means `Cons::a->List a->List a` 102 | * DerivingShow is not necessary. 103 | 104 | * pattern matching 105 | ```C++ 106 | int maybe_sum(steak::lazy_type_t>> l) 107 | { 108 | int x=0,y=0; 109 | while(1) 110 | { 111 | With(l) 112 | Case(Cons,UnZip(Just,y),l) 113 | x+=y; 114 | Case(Nil) 115 | break; /* break while(1) */ 116 | Case(Cons,UnZip(Nothing),l) 117 | ; 118 | EndWith() 119 | } 120 | return x; 121 | } 122 | ``` 123 | * With(x) starts a pattern matching for x and x must be `lazy_type_t` or `adt` 124 | * Case(constructor name,parameters...) is for matching, {} and break is not necessary. You can also use Unzip for nested pattern matching. 125 | 126 | * lazy evaluation 127 | * `lazy_type_t` is a template class to seal data types in C++. Constructor functions with the actual name returns a `adt` value, while functions followed by `_` returns a `lazy_type_t` value. For example, `Cons` is a strict constructor and `Cons_` is a lazy constructor. **warning**: If the constructor takes more than 5 parameters, the parameters must be all instant values or all `lazy_type_t` s. 128 | * Apply[d] are macros for calling function and putting off evaluation. Similarly, they only receieve all instant values or all `lazy_type_t` s as parameters. 129 | * currying functions 130 | * use `std::bind` to construct paritial applied functions. 131 | 132 | # Note 133 | The compiler and the library works well with `GCC 7.2.0` 134 | 135 | Feel free to contact me if you want to know more about Steak. -------------------------------------------------------------------------------- /doc/Learning C++ 11 By Looking Inside Steak.md: -------------------------------------------------------------------------------- 1 | # Learning C++ 11 By Looking Inside Steak 2 | 3 | ## Time for advertisement 4 | 5 | ### Steak 1.0 6 | ### A programming language which provides generalized algebraic data types, pattern matching and lazy evaluation based on C++17. 7 | 8 | ### Installation 9 | 10 | * As a domain specific language powered by C++ macros 11 | 12 | Just copy include/runtime.h and include/steak.h into your project and #include "steak.h". Be sure your C++ compiler support C++17 standard. 13 | 14 | * As a programming language powered by a compiler with C++17 backend. 15 | 16 | 1. install `haskell-stack` 17 | 1. clone this repository 18 | 1. run `stack install` 19 | 20 | ### Steak Language Documentation 21 | 22 | Steak programming language inherits C++17 completely except that `match` and `dataclass` are keywords. 23 | 24 | The extension names of Steak are `.stk.cpp`,`.stk.hpp` or `.stk.h`. The compiler will compile source files to c++17 code. 25 | 26 | #### Usage 27 | 28 | * steak file: compile the source file and print the object code to stdout 29 | * steak dirctory: search the directory recursively and compile all `*.stk.cpp`,`*.stk.h`,`*.stk.hpp` to `*.cpp`,`*.h`,`*.hpp` 30 | * steak --version: version and license 31 | * steak --help: help 32 | 33 | #### Language Specification 34 | * `dataclass` is for defining generalized algebraic data types. 35 | Usage: 36 | `[]` means optional 37 | 38 | ```c++ 39 | [template<...>] dataclass adt 40 | { 41 | return_type constructor1(...); 42 | return_type constructor2(...); 43 | ... 44 | }; 45 | ``` 46 | 47 | * `match` is for pattern matching. It's similar to `switch` expression. 48 | Usage: 49 | 50 | ```c++ 51 | match(var) 52 | { 53 | case Cons1(...): 54 | ... 55 | case Cons2(...): 56 | ... 57 | default: 58 | ... 59 | } 60 | ``` 61 | 62 | Examples are available at `examples/` 63 | 64 | #### Warnings 65 | 66 | * Remember that compiler won't deal with macros. As a result, don't use macros in `dataclass` or `match` expressions. 67 | * In the `case Cons(...)` expression, only literals and identifiers are allowed, operators and function call are not allowed.For example,`Just(x)` and `Just(1)` are valid, while `Just(x.c)` or `Just(1+3)` are invalid. Constructor with no paramenter also need to be followed by `()`, for example, `case Nothing()`. 68 | * If you know the implementations of the library and the compiler, you will understand all the strange things. 69 | 70 | # Let's go to our topic 71 | 72 | * topics we will cover 73 | * Points 74 | * reinterpret_cast 75 | * using 76 | * variadic template 77 | * rvalue reference 78 | * perfect forwarding 79 | * reference folds 80 | * decltype 81 | * fold expression 82 | * SFINAE 83 | * if constexpr 84 | * Tricks 85 | * currying template 86 | * dependent type 87 | * macro tricks 88 | 89 | 90 | * ghost_t 91 | 92 | attch a ghost type to a certain type. 93 | ```C++ 94 | template 95 | struct ghost_t 96 | { 97 | B data; /* Point 0. reinterpret_cast */ 98 | ghost_t(const B& x):data(x){} 99 | }; 100 | ``` 101 | 102 | * place_holder_t 103 | 104 | ```C++ 105 | struct place_holder_t{char c;}; 106 | place_holder_t _; 107 | ``` 108 | 109 | * lazy_type_t 110 | 111 | Use function to put off evaluation and `shared_ptr` to manage memory. After `eval()` is called, the function will be replaced by a function that returns the result directly. 112 | ```C++ 113 | template 114 | struct lazy_type_t 115 | { 116 | /* Point 1. using */ 117 | using B=std::function; 118 | std::shared_ptr data; 119 | T eval() const; 120 | }; 121 | ``` 122 | 123 | * zipped_pair_t 124 | 125 | Use `std::pair` to construct tuple for convenience of pattern matching. All types in `zipped_pair_t` are lazy. use lvalue reference and rvalue reference to distinguish lvalue and rvalue. `std::pair` is equivalent to T. `ghost_t` is for nested patterning. 126 | 127 | ```C++ 128 | /* Point 2. variadic template */ 129 | template struct zipped_pair_t; 130 | 131 | /* Point 3. specializations */ 132 | template 133 | struct zipped_pair_t 134 | { 135 | bool match(T1& v1); 136 | /* Point 4. rvalue reference (can be mutable) */ 137 | bool match(T1&& v1); 138 | bool match(lazy_type_t& f1); 139 | bool match(lazy_type_t&& f1); 140 | bool match(std::pair v1); 141 | bool match(std::pair v1); 142 | bool match(std::pair&,place_holder_t> f1); 143 | bool match(std::pair&&,place_holder_t> f1); 144 | template 145 | bool match(ghost_t&& tp); 146 | bool match(place_holder_t); 147 | 148 | template 149 | bool match(K...) 150 | }; 151 | ``` 152 | 153 | ```C++ 154 | template 155 | struct zipped_pair_t:public std::pair,zipped_pair_t> 156 | { 157 | zipped_pair_t(); 158 | zipped_pair_t(const zipped_pair_t&x); 159 | 160 | template 161 | zipped_pair_t(const Tx& v1,const Ts&...args); 162 | 163 | bool operator == (const zipped_pair_t& x) const; 164 | 165 | 166 | template 167 | bool match(std::pair& tp) 168 | { 169 | if(zipped_pair_t(this->first).match(std::forward(tp.first))) 170 | /* Point 5. perfect forwarding(skip) */ 171 | return this->second.match(std::forward(tp.second)); 172 | else 173 | return false; 174 | } 175 | 176 | template 177 | bool match(K...); 178 | }; 179 | ``` 180 | 181 | * forward_and_zip 182 | 183 | forward some arguments and zip them into nested `std::pair` in a similar way of `zipped_pair_t`. 184 | ```C++ 185 | auto forward_and_zip() 186 | { 187 | return _; 188 | } 189 | 190 | template 191 | /* Point 6. reference folds */ 192 | // & && == && & == & 193 | // T & &x == T &x 194 | // T && && x == T &&x 195 | auto forward_and_zip(T1&& v1) 196 | { 197 | auto sec=_; 198 | /* Point 6. decltype */ 199 | return std::pair(std::forward(v1),std::forward(sec)); 200 | } 201 | 202 | template 203 | auto forward_and_zip(T1&& v1,Ts&&...vs) 204 | { 205 | /* Point 7. fold expression */ 206 | auto sec = forward_and_zip(std::forward(vs)...); 207 | return std::pair(std::forward(v1),std::forward(sec)); 208 | } 209 | ``` 210 | 211 | * case_class_t 212 | 213 | Just attach a ghost type to `zipped_pair_t`.If we discard the ghost type, B and C in `data A = B Int | C Int` are both `case_class`. 214 | 215 | `unapply` just calls `data`'s `match` function. 216 | ```C++ 217 | template 218 | struct case_class_t 219 | { 220 | using D=zipped_pair_t; 221 | D data; 222 | 223 | template 224 | bool unapply(K& args); 225 | }; 226 | ``` 227 | 228 | * data_class_t 229 | 230 | The base class of all generalized algebraic data types. 231 | ```C++ 232 | template 233 | struct data_class_t:public std::variant 234 | { 235 | using data_class_label=char; 236 | }; 237 | 238 | #define Data(T,name,...) T struct name;T struct name:public data_class_t<__VA_ARGS__>{}; 239 | 240 | /* Point 8. SFINAE */ 241 | template 242 | struct is_data_class 243 | { 244 | template static auto check(_T)->typename std::decay::type; 245 | static void check(...); 246 | using type=decltype(check(std::declval())); 247 | enum{value=!std::is_void::value}; 248 | }; 249 | ``` 250 | 251 | * pattern matching 252 | * `With`, `Case`, `EndWith` are all macros , they can form a complete if block. 253 | * Perfect forwarding is important. 254 | 255 | * constructor 256 | 257 | Constructor is a function which returns adt or the lazy type of it. 258 | ```C++ 259 | inline lazy_type_t construtor_(const t1& v1); 260 | inline lazy_type_t construtor_(steak::lazy_type_t v1); 261 | inline adt construtor(const t1& v1); 262 | inline adt construtor(steak::lazy_type_t v1); 263 | ``` 264 | 265 | * more details 266 | ```c++ 267 | /* Trick 0. currying template */ 268 | template 269 | struct cal_cons_type 270 | { 271 | template 272 | using R=case_class_t; 273 | }; 274 | 275 | /* Point 9. if constexpr */ 276 | template 277 | std::ostream& operator << (std::ostream& out,const zipped_pair_t& tp) 278 | { 279 | if constexpr(is_data_class>::value) out<<'('; 280 | out<>::value) out<<')'; 282 | return out; 283 | } 284 | 285 | /* Trick 1. dependent types for visiting std::variant */ 286 | template 287 | struct search_t 288 | { 289 | template 290 | static bool search(ghost_t* t,V* var) 291 | { 292 | try 293 | { 294 | auto data = std::get(*var); 295 | if constexpr(std::is_same_v>) 296 | { 297 | return data.unapply(t->data); 298 | } 299 | else 300 | { 301 | return search_t::search(t,var); 302 | } 303 | } 304 | catch(std::bad_variant_access&) 305 | { 306 | return search_t::search(t,var); 307 | } 308 | } 309 | }; 310 | 311 | template 312 | struct search_t<-1,V> 313 | { 314 | template 315 | static bool search(ghost_t*,V*); 316 | }; 317 | 318 | /* Trick 2. macro tricks */ 319 | #define With(x) if(1){auto match_var=x.eval();if(0){; 320 | #define Case(name,...) }else if(match_var.match(steak::attach_ghost(steak::forward_and_zip(__VA_ARGS__)))){; 321 | #define Default }else{; 322 | #define EndWith() }} 323 | ``` 324 | 325 | # Note 326 | The compiler and the library works well with `GCC 7.2.0` 327 | 328 | Feel free to contact me if you want to know more about Steak. -------------------------------------------------------------------------------- /doc/steak 语言的设计与实现.md: -------------------------------------------------------------------------------- 1 | # Steak 语言的设计与实现 2 | 3 | 阅读本系列文章需要读者了解一些基本的函数式语言概念和 C++ 11 知识。 4 | 5 | Steak 语言是我两年前写的一个项目,最近又翻出来优化了一下,早就说要写篇文章总结一下,结果一直咕咕咕( 6 | 7 | [steak](https://github.com/nicekingwei/steak) 8 | 9 | Steak 语言设计的初衷是把 GADT、惰性求值、模式匹配等函数式语言特性引入 C++。C++ 11 以后,语言里逐渐出现了一些支持函数式编程的特性,例如 lambda 表达式,但他们用起来又不是特别好用(等以后有了 Ranges,情况会好转一些)。因此我想设计一个库或语言,让 C++ 使用者能更舒服地编写函数式程序。当然目前的结果只是个玩具,因为编译太慢了:)。 10 | 11 | 12 | 13 | ## 惰性求值 14 | 15 | C++11 原生支持了 lambda 表达式,因此我们可以用一个 lambda 闭包来表达一个待求值的块,例如 16 | 17 | 18 | ```c++ 19 | auto lazy_double = []() { return sqrt(2.0); }; 20 | ``` 21 | 这时的 lazy_double 即是一个惰性求值的块,`sqrt(2.0)` 只有在你用到 `lazy_double` 的时候才会被计算。但如果我们多次引用 `lazy_double`,`sqrt(2.0)` 便会被执行多次,我们希望它能只计算一次。所以,`lazy_double` 要么是 double,要么是返回一个 double 的函数。因为我们知道,C++ 有隐式转换特性,我们只需要在类里实现一个形如 22 | 23 | ```c++ 24 | operator const T &() const 25 | { 26 | return T{}; 27 | } 28 | ``` 29 | 30 | 的函数,即可将该类的实例隐式转换为 T 的实例。 31 | 32 | 更进一步,我们还可以表达异步计算的语义。如果我们之前声明了一个块,这个块的计算需要耗费大量的时间,于是我们利用 C++ 标准库的 `std::async` 来获得一个 `std::future` ,发起异步的计算。在我们需要用它的时候,再阻塞地拿到值。 33 | 34 | 不知不觉中,我们抽象掉了具体的计算过程及方法。在我们主要的程序中,我们只是声明了数据流动的方式,而并非给机器施加了计算的指令。这其实也是函数式编程与命令式编程在思维方式上的微小差异。扯远了,我们回到 `lazy_type` 的问题上来。 35 | 36 | 于是我们就可以利用 `std::variant` 定义出 `lazy_type` ,为了方便,我统一使用了智能指针: 37 | 38 | ```c++ 39 | template 40 | struct lazy_type_t 41 | { 42 | using block_t = std::function; 43 | using data_t = T; 44 | using future_t = std::future; 45 | using store_t = std::variant; 46 | 47 | template 48 | friend class lazy_type_t; 49 | 50 | private: 51 | std::shared_ptr ptr_data; 52 | 53 | public: 54 | lazy_type_t() : ptr_data(std::make_shared()) {} 55 | lazy_type_t(const data_t &v) : ptr_data(std::make_shared(v)) {} 56 | lazy_type_t(const lazy_type_t &x) : ptr_data(x.ptr_data) {} 57 | 58 | explicit lazy_type_t(std::shared_ptr p) : ptr_data(std::move(p)) {} 59 | 60 | const T &get() const; 61 | 62 | lazy_type_t &async(); 63 | 64 | operator const T &() const; 65 | 66 | bool operator==(const lazy_type_t &x) const; 67 | 68 | template 69 | lazy_type_t> transform(const Y &fun) const; 70 | 71 | template 72 | lazy_type_t> operator|(const Y &fun) const; 73 | }; 74 | ``` 75 | 76 | `block_t` 是返回 T 的函数,`future_t` 是返回 T 的 `future` ,`store_t` 即是 `block_t`、`future_t`、`T` 三种类型的和类型,即任意时刻,`store_t` 存的值是上述三种类型的一种。`block_t` 可以通过 `async` 调用进入异步计算模式,即转变为 `future_t`,而两者均在`get` 时转换为 `T` 类型存下来。 77 | 78 | `transform` 函数和 `|` 运算符用于把一个函数应用到该类型上,产生一个新的 `lazy_type_t` 的值。这里用到了一个模板 `return_type_t`,这是用来计算类型 `Y` 的返回值类型的。它的实现我是抄的 Stack Overflow 上的答案。链接在[这里](https://stackoverflow.com/questions/27822277/finding-out-the-return-type-of-a-function-lambda-or-function)。 79 | 80 | 最终实现的效果如下,以下代码节选自 [lazy.cpp](https://github.com/nicekingwei/steak/blob/master/examples/lazy.cpp) 81 | 82 | ```c++ 83 | auto l4 = lazy_call(time_consuming_compute).async(); 84 | 85 | sleep(1); 86 | 87 | auto l1 = lazy_type_t(1); 88 | auto l2 = l1.transform([](int x) { return 'a'; }); 89 | const auto l3 = lazy_call([]() { 90 | cout << "eval l3\n"; 91 | return 'v'; 92 | }); 93 | 94 | cout << l2 << endl; 95 | cout << l3 << endl; 96 | 97 | cout << l4 << endl; 98 | cout << l4 << endl; 99 | 100 | lazy_type_t> l5(std::pair(1, 'a')); 101 | cout << l5.get().first; 102 | f(l5); 103 | 104 | auto l6 = lazy_type_t(1) 105 | | [](int x) { return x + 1; } 106 | | [](int x) { return x * 2; } 107 | | [](int x) { 108 | cout << x << endl; 109 | return "hello world"; 110 | }; 111 | 112 | cout << l6 << endl; 113 | ``` 114 | 115 | 116 | 117 | ## 积类型 118 | 119 | 单纯实现积类型非常简单,直接使用 `std::tuple` 即可,但在支持模式匹配路上遇到了许多困难,最后我选择了自己实现积类型 `zipped_pair_t`。 120 | 121 | 首先定义一个接收任意数量的类型参数的实现 122 | 123 | ```c++ 124 | template 125 | struct zipped_pair_t 126 | { 127 | template 128 | bool match(K...) 129 | { 130 | return true; 131 | } 132 | }; 133 | ``` 134 | 135 | 然后进行特化,首先需要用 `lazy_type_t` 存一个数据,然后定义各种情况下的模式匹配函数,区别对待左值、右值和占位符。模式匹配时,碰到左值则进行数据绑定(在这里是拷贝),碰到右值进行比较,碰到占位符默认返回成功。 136 | 137 | ```c++ 138 | template 139 | struct zipped_pair_t 140 | { 141 | lazy_type_t data; 142 | 143 | zipped_pair_t() {} 144 | 145 | zipped_pair_t(const T1 &x) { data = x; } 146 | 147 | zipped_pair_t(const lazy_type_t &x) { data = x; } 148 | 149 | bool operator==(const zipped_pair_t &x) const 150 | { 151 | return x.data == data; 152 | } 153 | 154 | bool match(std::pair v1) 155 | { 156 | v1.first = data.get(); 157 | return true; 158 | } 159 | 160 | bool match(std::pair v1) 161 | { 162 | return v1.first == data.get(); 163 | } 164 | 165 | bool match(T1 &v1) 166 | { 167 | v1 = data.get(); 168 | return true; 169 | } 170 | 171 | bool match(T1 &&v1) 172 | { 173 | return v1 == data.get(); 174 | } 175 | 176 | // ...... 177 | }; 178 | ``` 179 | 180 | 然后是多个参数的情况,这里采用了一个递归定义的类模板,进行具体处理时,只需处理第一个参数,然后对第二个参数递归对应的处理函数。成员函数的具体实现略去。 181 | 182 | ```c++ 183 | template 184 | struct zipped_pair_t : public std::pair, zipped_pair_t> 185 | { 186 | // ....... 187 | }; 188 | ``` 189 | 190 | 191 | 192 | 对于传进来进行模式匹配的参数,我定义了一个 `forward_and_zip` 函数将他们打包成 `zipped_pair`,并且通过完美转发保留了他们原本的类型。 193 | 194 | ```c++ 195 | inline auto forward_and_zip() 196 | { 197 | return _; 198 | } 199 | 200 | template 201 | inline auto forward_and_zip(T1 &&v1) 202 | { 203 | auto sec = _; 204 | return std::pair(std::forward(v1), std::forward(sec)); 205 | } 206 | 207 | template 208 | inline auto forward_and_zip(T1 &&v1, Ts &&... vs) 209 | { 210 | auto sec = forward_and_zip(std::forward(vs)...); 211 | return std::pair(std::forward(v1), std::forward(sec)); 212 | } 213 | ``` 214 | 215 | 216 | 217 | ### 和类型 218 | 219 | 和类型直接封装 `std::variant`,并提供模式匹配接口即可。 220 | 221 | ```c++ 222 | template 223 | struct data_class_t : public std::variant 224 | { 225 | using data_class_label = char; 226 | 227 | data_class_t() {} 228 | 229 | template 230 | data_class_t(const K &x) : std::variant(x) {} 231 | 232 | template 233 | inline bool match(ghost_t t) 234 | { 235 | return search_t>::search(&t, this); 236 | } 237 | 238 | bool operator==(const data_class_t &x) const 239 | { 240 | return search_t>::compare(&x, this); 241 | } 242 | 243 | std::ostream &show(std::ostream &out) const 244 | { 245 | return search_t>::show(out, this); 246 | } 247 | 248 | auto get() 249 | { 250 | return *this; 251 | } 252 | }; 253 | ``` 254 | 255 | 在类模板参数不固定的情况下,遍历每个模板参数显得有些困难,于是不得不采用一些取巧的方法。上面出现的 `search_t` 就是这样的帮助递归遍历每个模板参数的辅助类。 256 | 257 | ```c++ 258 | template 259 | struct search_t 260 | { 261 | static std::ostream &show(std::ostream &out, const V *var) 262 | { 263 | try 264 | { 265 | auto &data = std::get(*var); 266 | out << data; 267 | return out; 268 | } 269 | catch (std::bad_variant_access &) 270 | { 271 | return search_t::show(out, var); 272 | } 273 | } 274 | // ...... 275 | }; 276 | 277 | template 278 | struct search_t<-1, V> 279 | { 280 | static std::ostream &show(std::ostream &out, const V *) 281 | { 282 | return out; 283 | } 284 | 285 | // ...... 286 | }; 287 | ``` 288 | 289 | 上面这段节选了 show 函数,即将一个 `std::variant` 打印出来的函数。递归尝试 `std::variant` 的类型,失败就尝试下一个类型,成功就打印该变量。 290 | 291 | 292 | 293 | ## 用法 294 | 295 | 你可以通过宏来使用 Steak 语言,大概长这样: 296 | 297 | ```c++ 298 | RegCons(Nil) RegCons(Cons) 299 | Data(Va,List,RealType(Nil),RealType(Cons,a,List)) 300 | Cons0(Va,Nil,List) 301 | Cons2(Va,Cons,List,a,List) 302 | DerivingShow(Va,List) 303 | 304 | RegCons(Nothing) RegCons(Just) 305 | Data(Va,Maybe,RealType(Nothing),RealType(Just,a)) 306 | Cons0(Va,Nothing,Maybe) 307 | Cons1(Va,Just,Maybe,a) 308 | DerivingShow(Va,Maybe) 309 | 310 | RegCons(Z) RegCons(S) 311 | Data(V,Peano,RealType(Z),RealType(S,Peano)) 312 | Cons0(V,Z,Peano) 313 | Cons1(V,S,Peano,Peano) 314 | DerivingShow(V,Peano) 315 | 316 | 317 | Va std::ostream& operator << (std::ostream& out,List l) 318 | { 319 | out<<'['; 320 | bool first=true; 321 | while(true) 322 | { 323 | a x; 324 | With(l) 325 | Case(Cons,x,l) 326 | if(first) first=false; 327 | else out<<','; 328 | out<>> l) 337 | { 338 | int x=0,y=0; 339 | while(1) 340 | { 341 | With(l) 342 | Case(Cons,UnZip(Just,y),l) 343 | x+=y; 344 | Case(Nil) 345 | break; 346 | Case(Cons,UnZip(Nothing),l) 347 | ; 348 | EndWith() 349 | } 350 | return x; 351 | } 352 | ``` 353 | 354 | 或者使用我用 Haskell 写的编译器进行代码生成,代码会清爽一些: 355 | 356 | ```c++ 357 | template 358 | dataclass Maybe 359 | { 360 | Maybe Nothing(); 361 | Maybe Just(a); 362 | }; 363 | 364 | template 365 | dataclass List 366 | { 367 | List Nil(); 368 | List Cons(a,List); 369 | }; 370 | 371 | int maybe_sum(List> l) 372 | { 373 | int x=0,y=0; 374 | bool flag=true; 375 | while(flag) 376 | { 377 | match(l) 378 | { 379 | case Cons(Just(y),l): 380 | x+=y; 381 | break; 382 | case Cons(Nothing(),l): 383 | break; 384 | case Nil(): 385 | flag=false; 386 | break; 387 | } 388 | } 389 | return x; 390 | } 391 | ``` 392 | 393 | 394 | 395 | ## 结尾 396 | 397 | Steak 语言运行时的核心就是这些啦,对于这些需求,我的实现方式可能也不是最好的,欢迎大家在评论区讨论 C++ 更好的用法。没展示出来的代码都是用来处理一些边界情况,或让代码看起来更漂亮的。 398 | 399 | 400 | 401 | (给自己打个广告,我最近在找工作,如果各位朋友知道有 PL、编译器、DB 相关的岗位,可以私信告诉我,谢谢大家!) -------------------------------------------------------------------------------- /include/runtime.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * @brief a functional library for cpp 5 | * @author nicekingwei(Long Jinwei) 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "ret_type_impl.h" 16 | 17 | namespace steak 18 | { 19 | 20 | /** 21 | * @class ghost_t 22 | * @brief attach a ghost type G to a base type 23 | */ 24 | template 25 | struct ghost_t 26 | { 27 | B data; // store the value 28 | ghost_t(const B &x) : data(x) {} 29 | }; 30 | 31 | template 32 | inline auto attach_ghost(const B &v) 33 | { 34 | return static_cast>(v); 35 | } 36 | 37 | /** 38 | * @class place_holder_t 39 | * @brief placeholder for pattern matching 40 | */ 41 | struct place_holder_t 42 | { 43 | char ch; 44 | template 45 | bool operator==(T) { return true; } 46 | }; 47 | 48 | place_holder_t _; 49 | 50 | /** 51 | * @class lazy_type_t 52 | * @brief data block to postpone uation(not thread-safe) 53 | */ 54 | 55 | template 56 | struct lazy_type_t; 57 | 58 | template 59 | lazy_type_t> lazy_call(const T &f) 60 | { 61 | using R = return_type_t; 62 | using L = lazy_type_t; 63 | using S = typename L::store_t; 64 | return lazy_type_t(std::make_shared(f)); 65 | } 66 | 67 | template 68 | struct lazy_type_t 69 | { 70 | 71 | using block_t = std::function; 72 | using data_t = T; 73 | using future_t = std::future; 74 | using store_t = std::variant; 75 | 76 | template 77 | friend class lazy_type_t; 78 | 79 | private: 80 | std::shared_ptr ptr_data; 81 | 82 | public: 83 | lazy_type_t() : ptr_data(std::make_shared()) {} 84 | 85 | lazy_type_t(const data_t &v) : ptr_data(std::make_shared(v)) {} 86 | 87 | lazy_type_t(const lazy_type_t &x) : ptr_data(x.ptr_data) {} 88 | 89 | explicit lazy_type_t(std::shared_ptr p) : ptr_data(std::move(p)) {} 90 | 91 | const T &get() const 92 | { 93 | store_t *p = ptr_data.get(); 94 | 95 | if (auto p_block = std::get_if(p)) 96 | { 97 | *ptr_data = (*p_block)(); 98 | } 99 | else if (auto p_future = std::get_if(p)) 100 | { 101 | *ptr_data = p_future->get(); 102 | } 103 | 104 | return std::get(*p); 105 | } 106 | 107 | lazy_type_t &async() 108 | { 109 | auto p = ptr_data.get(); 110 | 111 | if (auto p_block = std::get_if(p)) 112 | { 113 | *ptr_data = std::async(*p_block); 114 | } 115 | 116 | return *this; 117 | } 118 | 119 | operator const T &() const 120 | { 121 | return get(); 122 | } 123 | 124 | bool operator==(const lazy_type_t &x) const 125 | { 126 | return x.get() == get(); 127 | } 128 | 129 | template 130 | lazy_type_t> transform(const Y &fun) const 131 | { 132 | auto copy = *this; 133 | return lazy_call([=]() { 134 | auto x = copy.get(); 135 | return fun(x); 136 | }); 137 | } 138 | 139 | template 140 | lazy_type_t> operator|(const Y &fun) const 141 | { 142 | auto copy = *this; 143 | return lazy_call([=]() { 144 | auto x = copy.get(); 145 | return fun(x); 146 | }); 147 | } 148 | }; 149 | 150 | /** 151 | * @class zipped_pair_t 152 | */ 153 | template 154 | struct zipped_pair_t 155 | { 156 | template 157 | bool match(K...) 158 | { 159 | return true; 160 | } 161 | }; 162 | 163 | template 164 | struct zipped_pair_t 165 | { 166 | lazy_type_t data; 167 | 168 | zipped_pair_t() {} 169 | 170 | zipped_pair_t(const T1 &x) { data = x; } 171 | 172 | zipped_pair_t(const lazy_type_t &x) { data = x; } 173 | 174 | bool operator==(const zipped_pair_t &x) const 175 | { 176 | return x.data == data; 177 | } 178 | 179 | bool match(std::pair v1) 180 | { 181 | v1.first = data.get(); 182 | return true; 183 | } 184 | 185 | bool match(std::pair v1) 186 | { 187 | return v1.first == data.get(); 188 | } 189 | 190 | bool match(T1 &v1) 191 | { 192 | v1 = data.get(); 193 | return true; 194 | } 195 | 196 | bool match(T1 &&v1) 197 | { 198 | return v1 == data.get(); 199 | } 200 | 201 | bool match(lazy_type_t &f1) 202 | { 203 | f1 = data.get(); 204 | return true; 205 | } 206 | 207 | bool match(lazy_type_t &&f1) 208 | { 209 | return f1.get() == data.get(); 210 | } 211 | 212 | bool match(std::pair &, place_holder_t> f1) 213 | { 214 | f1.first = data.get(); 215 | return true; 216 | } 217 | 218 | bool match(std::pair &&, place_holder_t> f1) 219 | { 220 | return f1.first.get() == data.get(); 221 | } 222 | 223 | template 224 | bool match(ghost_t &&tp) 225 | { 226 | auto m = data.get(); 227 | return m.match(tp); 228 | } 229 | 230 | bool match(place_holder_t) 231 | { 232 | return true; 233 | } 234 | 235 | template 236 | bool match(K...) 237 | { 238 | return false; 239 | } 240 | }; 241 | 242 | template 243 | struct zipped_pair_t : public std::pair, zipped_pair_t> 244 | { 245 | zipped_pair_t() {} 246 | 247 | zipped_pair_t(const zipped_pair_t &x) 248 | { 249 | this->first = x.first; 250 | this->second = x.second; 251 | } 252 | 253 | template 254 | zipped_pair_t(const Tx &v1, const Ts &... args) 255 | { 256 | this->first = v1; 257 | this->second = zipped_pair_t(args...); 258 | } 259 | 260 | bool operator==(const zipped_pair_t &x) const 261 | { 262 | return x.first == this->first && x.second == this->second; 263 | } 264 | 265 | template 266 | bool match(std::pair &tp) 267 | { 268 | if (zipped_pair_t(this->first).match(std::forward(tp.first))) 269 | return this->second.match(std::forward(tp.second)); 270 | else 271 | return false; 272 | } 273 | 274 | template 275 | bool match(K...) 276 | { 277 | return false; 278 | } 279 | }; 280 | 281 | /** 282 | * @brief forward and zip 283 | */ 284 | inline auto forward_and_zip() 285 | { 286 | return _; 287 | } 288 | 289 | template 290 | inline auto forward_and_zip(T1 &&v1) 291 | { 292 | auto sec = _; 293 | return std::pair(std::forward(v1), std::forward(sec)); 294 | } 295 | 296 | template 297 | inline auto forward_and_zip(T1 &&v1, Ts &&... vs) 298 | { 299 | auto sec = forward_and_zip(std::forward(vs)...); 300 | return std::pair(std::forward(v1), std::forward(sec)); 301 | } 302 | 303 | /** 304 | * @class case_class_t 305 | */ 306 | template 307 | struct case_class_t 308 | { 309 | using D = zipped_pair_t; 310 | D data; 311 | const char *name = G::consname; 312 | static const G constype; 313 | 314 | case_class_t() {} 315 | 316 | case_class_t(const case_class_t &x) : data(x.data) {} 317 | 318 | case_class_t(const T &... args) : data(args...) {} 319 | 320 | template 321 | bool unapply(K &args) 322 | { 323 | return data.match(args); 324 | } 325 | 326 | bool operator==(const case_class_t &x) const 327 | { 328 | return x.data == this->data; 329 | } 330 | }; 331 | 332 | template 333 | struct case_class_t 334 | { 335 | char data = 0; 336 | const char *name = G::consname; 337 | static const G constype; 338 | 339 | case_class_t() {} 340 | 341 | template 342 | bool unapply(K) 343 | { 344 | return true; 345 | } 346 | 347 | bool operator==(const case_class_t &) const 348 | { 349 | return true; 350 | } 351 | }; 352 | 353 | template 354 | struct is_data_class 355 | { 356 | template 357 | static auto check(_T) -> typename std::decay::type; 358 | 359 | static void check(...); 360 | 361 | using type = decltype(check(std::declval())); 362 | enum 363 | { 364 | value = !std::is_void::value 365 | }; 366 | }; 367 | 368 | template 369 | struct cal_cons_type 370 | { 371 | template 372 | using R = case_class_t; 373 | }; 374 | 375 | template 376 | std::ostream &operator<<(std::ostream &out, const zipped_pair_t &tp) 377 | { 378 | if constexpr (is_data_class>::value) 379 | out << '('; 380 | out << tp.data.get(); 381 | if constexpr (is_data_class>::value) 382 | out << ')'; 383 | return out; 384 | } 385 | 386 | template 387 | std::ostream &operator<<(std::ostream &out, const zipped_pair_t &tp) 388 | { 389 | if constexpr (is_data_class>::value) 390 | out << '('; 391 | out << tp.first.get(); 392 | if constexpr (is_data_class>::value) 393 | out << ')'; 394 | out << " " << tp.second; 395 | return out; 396 | } 397 | 398 | template 399 | std::ostream &operator<<(std::ostream &out, const case_class_t &x) 400 | { 401 | if constexpr (std::is_same_v>) 402 | { 403 | return out << x.name; 404 | } 405 | else 406 | { 407 | return (out << x.name << " " << x.data); 408 | } 409 | } 410 | 411 | template 412 | struct search_t 413 | { 414 | template 415 | static bool search(ghost_t *t, V *var) 416 | { 417 | try 418 | { 419 | auto data = std::get(*var); 420 | if constexpr (std::is_same_v>) 421 | { 422 | return data.unapply(t->data); 423 | } 424 | else 425 | { 426 | return search_t::search(t, var); 427 | } 428 | } 429 | catch (std::bad_variant_access &) 430 | { 431 | return search_t::search(t, var); 432 | } 433 | } 434 | 435 | static bool compare(V *var1, V *var2) 436 | { 437 | try 438 | { 439 | auto v1 = std::get(*var1); 440 | auto v2 = std::get(*var2); 441 | return v1 == v2; 442 | } 443 | catch (std::bad_variant_access &) 444 | { 445 | return search_t::compare(var1, var2); 446 | } 447 | } 448 | 449 | static std::ostream &show(std::ostream &out, const V *var) 450 | { 451 | try 452 | { 453 | auto &data = std::get(*var); 454 | out << data; 455 | return out; 456 | } 457 | catch (std::bad_variant_access &) 458 | { 459 | return search_t::show(out, var); 460 | } 461 | } 462 | }; 463 | 464 | template 465 | struct search_t<-1, V> 466 | { 467 | template 468 | static bool search(ghost_t *, V *) 469 | { 470 | return false; 471 | } 472 | 473 | static std::ostream &show(std::ostream &out, const V *) 474 | { 475 | return out; 476 | } 477 | 478 | static bool compare(V *, V *) { return false; } 479 | }; 480 | 481 | template 482 | struct data_class_t : public std::variant 483 | { 484 | using data_class_label = char; 485 | 486 | data_class_t() {} 487 | 488 | template 489 | data_class_t(const K &x) : std::variant(x) {} 490 | 491 | template 492 | inline bool match(ghost_t t) 493 | { 494 | return search_t>::search(&t, this); 495 | } 496 | 497 | bool operator==(const data_class_t &x) const 498 | { 499 | return search_t>::compare(&x, this); 500 | } 501 | 502 | std::ostream &show(std::ostream &out) const 503 | { 504 | return search_t>::show(out, this); 505 | } 506 | 507 | auto get() 508 | { 509 | return *this; 510 | } 511 | }; 512 | 513 | template 514 | std::ostream &operator<<(std::ostream &out, const data_class_t &d) 515 | { 516 | return d.show(out); 517 | } 518 | }; // namespace steak 519 | -------------------------------------------------------------------------------- /src/compiler.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TemplateHaskell #-} 2 | {-# LANGUAGE FlexibleInstances #-} 3 | {-# LANGUAGE MultiParamTypeClasses #-} 4 | 5 | module Main (main) where 6 | 7 | import Debug.Trace 8 | 9 | import Control.Monad 10 | import Data.Monoid 11 | import Data.Functor.Identity 12 | import qualified Data.ByteString.Lazy as BS 13 | import Data.String 14 | import Data.ByteString.Builder 15 | import Data.List 16 | import Data.Int 17 | import System.IO 18 | import System.Environment 19 | import System.Directory 20 | import System.FilePath 21 | import Text.Parsec 22 | import qualified Text.Parsec.Token as Token 23 | 24 | -- Note: 25 | -- 1.can't handle with macro 26 | 27 | -- compiler config 28 | totalConsN = 0 29 | version = 30 | "steak language compiler version 1.0\n" ++ 31 | "MIT License Copyright (c) 2017 Long Jinwei\n" ++ 32 | "more information at https://github.com/nicekingwei/steak" 33 | help = 34 | "steak language compiler 1.0\n" ++ 35 | "Usage: \n"++ 36 | "* steak file: compile the source file and print the object code to stdout\n" ++ 37 | "* steak dirctory: search the directory recursively and compile all *.stk.cpp,*.stk.h,*.stk.hpp to *.cpp,*.h,*.hpp\n" ++ 38 | "* steak --version: version and license\n" ++ 39 | "* steak --help: help" 40 | 41 | 42 | -- Builder utils 43 | instance Show Builder where 44 | show b = map (toEnum.fromEnum) $ BS.unpack (toLazyByteString b) 45 | 46 | instance Eq Builder where 47 | a==b = (toLazyByteString a) == (toLazyByteString b) 48 | 49 | class (Show a)=>Encodable a where 50 | encode::a->Builder 51 | encode = string7 . show 52 | 53 | instance Encodable Char where 54 | encode = char7 55 | 56 | instance Encodable String where 57 | encode = fromString 58 | 59 | instance Encodable BS.ByteString where 60 | encode = lazyByteString 61 | 62 | instance Encodable Double 63 | instance Encodable Integer 64 | instance Encodable Int64 65 | 66 | instance (Encodable a,Encodable b)=>Encodable (Either a b) where 67 | encode x = case x of Left y -> encode y 68 | Right z -> encode z 69 | 70 | 71 | -- ByteStream 72 | data ByteStream = ByteStream { 73 | pos::Int64, 74 | len::Int64, 75 | buffer::BS.ByteString 76 | } deriving (Eq,Show) 77 | 78 | instance (Monad m) => Stream ByteStream m Char where 79 | uncons s = let p = pos s in return $ if p==len s then Nothing else Just ((toEnum . fromEnum) $ BS.index (buffer s) p , s {pos=p+1} ) 80 | 81 | toByteStream::BS.ByteString -> ByteStream 82 | toByteStream s = ByteStream 0 (BS.length s) s 83 | 84 | 85 | -- Other utils 86 | 87 | toByteString::(Enum a)=>[a]->BS.ByteString 88 | toByteString = BS.pack . map (toEnum.fromEnum) 89 | 90 | slice::(Int64,Int64)->BS.ByteString->Builder 91 | slice (s,t) src = encode $ BS.take (t-s) (BS.drop s src) 92 | 93 | debugLevel x = do 94 | t<-getBraceLevel 95 | ts<-getResetLevels 96 | p<-getPosition 97 | trace (show t ++ " " ++ show ts ++ " " ++ show (sourceLine p)) x 98 | 99 | -- Code generator 100 | transNestedComment::Builder->Builder 101 | transNestedComment b = let s = toLazyByteString b 102 | len = fromIntegral (BS.length s)::Int64 103 | in snd $ foldl (\(p,acc) i-> 104 | if i==len then 105 | (p , acc <> (slice (p,len) s)) 106 | else if and [BS.index s (i-1) == ((toEnum.fromEnum) '*'),BS.index s i == ((toEnum.fromEnum) '/')] then 107 | (i , acc <> (slice (p,i-1) s) <> encode ' ') 108 | else 109 | (p , acc)) (0,mempty) [1..len] 110 | 111 | unsplitB::Char->[Builder]->Builder 112 | unsplitB _ [] = mempty 113 | unsplitB s xs = let ss = encode s in foldr1 (\x acc->mconcat [x,ss,acc]) xs 114 | 115 | unTwoWords::(Builder,Builder)->Builder 116 | unTwoWords (x,y) = x <> encode ' ' <> y 117 | 118 | wrapAngle::Builder->Builder 119 | wrapAngle b = encode '<' <> b <> encode '>' 120 | 121 | wrapBrace::Builder->Builder 122 | wrapBrace b = encode '{' <> b <> encode '}' 123 | 124 | wrapParenthese::Builder->Builder 125 | wrapParenthese b = encode '(' <> b <> encode ')' 126 | 127 | wrapLazyFun::Builder->Builder->Builder 128 | wrapLazyFun ret b = encode "return steak::lazy_call(std::function<" <> ret <> encode "()> ( [=](){" <> b <> encode "} ) );" 129 | 130 | 131 | -- copy from http://hackage.haskell.org/package/parsec-3.1.11/docs/src/Text.Parsec.Language.html#javaStyle 132 | cppStyle::Token.GenLanguageDef ByteStream st Identity 133 | cppStyle = Token.LanguageDef 134 | { Token.commentStart = "/*" 135 | , Token.commentEnd = "*/" 136 | , Token.commentLine = "//" 137 | , Token.nestedComments = True 138 | , Token.identStart = letter <|> char '_' 139 | , Token.identLetter = alphaNum <|> oneOf "_" 140 | , Token.opStart = oneOf ":!#$%&*+./<=>?@\\^|-~;(),[]" 141 | , Token.opLetter = oneOf ":!#$%&*+./<=>?@\\^|-~" 142 | , Token.reservedNames = ["case","default"] 143 | , Token.reservedOpNames= ["{}"] 144 | , Token.caseSensitive = False 145 | } 146 | 147 | -- simple tokenizer 148 | cppToken = Token.makeTokenParser cppStyle 149 | 150 | -- abstract syntax tree 151 | data Constructors = MkCons { 152 | retType::Builder, -- return type Maybe 153 | retTypeArgs::[Builder], -- parameters of return type a 154 | consName::Builder, -- constructor name Just 155 | argTypes::[Builder] -- arguments types a 156 | } deriving (Show) 157 | 158 | data ParserStates = MkParserStates { 159 | braceLevel:: Int, 160 | resetLevels::[Int] 161 | } 162 | 163 | type Parser = ParsecT ByteStream ParserStates Identity 164 | 165 | emptyState::ParserStates 166 | emptyState = MkParserStates 0 [] 167 | 168 | -- states : verb + state 169 | getBraceLevel::Parser Int 170 | getBraceLevel = do 171 | MkParserStates n _<- getState 172 | return n 173 | 174 | incBraceLevel::Parser () 175 | incBraceLevel = modifyState (\c->c{braceLevel=braceLevel c+1}) 176 | 177 | decBraceLevel::Parser () 178 | decBraceLevel = modifyState (\c->c{braceLevel=braceLevel c-1}) 179 | 180 | getResetLevels::Parser [Int] 181 | getResetLevels = do 182 | MkParserStates _ xs<-getState 183 | return xs 184 | 185 | topResetLevel::Parser Int 186 | topResetLevel = do 187 | MkParserStates _ xs <- getState 188 | if null xs then 189 | fail "error: unknown error" 190 | else 191 | return (head xs) 192 | 193 | popResetLevel::Parser () 194 | popResetLevel = do 195 | MkParserStates _ xs <- getState 196 | if null xs then 197 | fail "error: unknown error" 198 | else 199 | modifyState (\c->c {resetLevels = tail xs }) 200 | 201 | pushResetLevel::Parser () 202 | pushResetLevel = do 203 | MkParserStates t xs <- getState 204 | modifyState (\c->c{resetLevels = t:xs}) 205 | 206 | -- parser combinator: no extra space at end 207 | consumeWhite::Parser a->Parser a 208 | consumeWhite p = do 209 | x<-p 210 | optWhiteSpace 211 | return x 212 | 213 | 214 | lbraceS::Parser Builder 215 | lbraceS = do 216 | charW '{' 217 | incBraceLevel 218 | return $ encode '{' 219 | 220 | rbraceS::Parser Builder 221 | rbraceS = do 222 | charW '}' 223 | decBraceLevel 224 | return $ encode '}' 225 | 226 | encodeM::(Encodable a)=>Parser a->Parser Builder 227 | encodeM p = p>>=return . encode 228 | 229 | commaSep::Parser a->Parser [a] 230 | commaSep = Token.commaSep cppToken 231 | 232 | commaSep1::Parser a->Parser [a] 233 | commaSep1 = Token.commaSep1 cppToken 234 | 235 | charW::Char->Parser Builder 236 | charW = encodeM.consumeWhite.char 237 | 238 | stringW::String->Parser Builder 239 | stringW = encodeM.consumeWhite.string 240 | 241 | whiteSpace::Parser Builder 242 | whiteSpace = do 243 | Token.whiteSpace cppToken 244 | return mempty 245 | 246 | optWhiteSpace::Parser () 247 | optWhiteSpace = optional whiteSpace 248 | 249 | identifier::Parser Builder 250 | identifier = encodeM.consumeWhite $ Token.identifier cppToken 251 | 252 | operator::Parser Builder 253 | operator = encodeM.consumeWhite $ Token.operator cppToken 254 | 255 | parenthese::Parser a->Parser a 256 | parenthese = consumeWhite . between (charW '(') (charW ')') 257 | 258 | angleBracket::Parser a->Parser a 259 | angleBracket = consumeWhite . between (charW '<') (charW '>') 260 | 261 | brace::Parser a->Parser a 262 | brace = consumeWhite . between (charW '{') (charW '}') 263 | 264 | braceS::Parser a->Parser a 265 | braceS = consumeWhite . between (pushResetLevel>>lbraceS) (endBraceS) 266 | 267 | endBraceS::Parser Builder 268 | endBraceS = do 269 | c<-charW '}' 270 | l<-getBraceLevel 271 | t<-topResetLevel 272 | if l==t+1 then do 273 | decBraceLevel 274 | popResetLevel 275 | return c 276 | else 277 | fail "" 278 | 279 | endBrace::Parser Builder 280 | endBrace = do 281 | c<-charW '}' 282 | l<-getBraceLevel 283 | t<-topResetLevel 284 | if l==t+1 then do 285 | return c 286 | else 287 | fail "" 288 | 289 | literal::Parser Builder 290 | literal = consumeWhite $ 291 | (encodeM $ Token.stringLiteral cppToken) <|> 292 | (encodeM $ Token.charLiteral cppToken) <|> 293 | (encodeM $ Token.naturalOrFloat cppToken) 294 | 295 | reserved::String->Parser Builder 296 | reserved s = consumeWhite $ do 297 | Token.reserved cppToken s 298 | return (encode s) 299 | 300 | 301 | caseKeyWords::Parser Builder 302 | caseKeyWords = reserved "case" <|> reserved "default" 303 | 304 | 305 | cppType::Parser (Builder,[Builder]) 306 | cppType = do 307 | m<-identifier 308 | xs<-option [] $ angleBracket (commaSep1 identifier) 309 | if null xs then 310 | return (m,xs) 311 | else 312 | return (mconcat [m,encode '<',unsplitB ',' xs,encode '>'],xs) 313 | 314 | anyTokens::Parser Builder 315 | anyTokens = choice [identifier,operator,literal,lbraceS,rbraceS,caseKeyWords] 316 | 317 | dataclassHead::Parser Builder 318 | dataclassHead = do 319 | optional $ do 320 | stringW "template" 321 | angleBracket $ commaSep1 $ do 322 | cppType 323 | identifier 324 | reserved "dataclass" 325 | 326 | dataclass::Parser Builder 327 | dataclass = do 328 | -- Parse 329 | ByteStream start _ buf <- getInput 330 | tempDef<-try (do 331 | tempDef<-option [] $ do 332 | stringW "template" 333 | angleBracket $ commaSep1 $ do 334 | (t,_)<-cppType 335 | i<-identifier 336 | return (t,i) 337 | reserved "dataclass" 338 | return tempDef) 339 | dataName<-identifier 340 | consLst<-braceS $ many $ do 341 | (t,as)<-cppType 342 | n<-identifier 343 | args<-parenthese (commaSep cppType) 344 | charW ';' 345 | return $ MkCons {retType=t,retTypeArgs=as,consName=n,argTypes=map fst args} 346 | ByteStream end _ _ <- getInput 347 | charW ';' 348 | 349 | -- Code generator 350 | let 351 | genRegCons c = let name = consName c in encode "namespace steak_constructors{struct " <> name <> encode "{static constexpr const char* consname=\"" <> name <> encode "\";};};\n" 352 | genCaseClass c = let as = argTypes c 353 | name = consName c 354 | in encode "steak::case_class_t" <> 355 | wrapAngle (unsplitB ',' ( (encode "steak_constructors::" <> name) :as)) 356 | genConsTempVars xs = unsplitB ',' $ map unTwoWords xs 357 | genConsFunDefs c = let name = consName c 358 | rt = retType c 359 | ras = retTypeArgs c 360 | as = argTypes c 361 | n = length as 362 | tbs = foldl (\acc _->[x:y|y<-acc,x<-[True,False]]) [[]] [0..n] 363 | pbs = do 364 | x<-[False] 365 | y<-if n==0 then [[]] else [replicate n False] 366 | return (x:y) 367 | 368 | fas = do 369 | (t,p)<-tempDef 370 | a<-ras 371 | guard (a==p) 372 | return (t,p) 373 | 374 | temp = if null fas then mempty else encode "template" <> wrapAngle (genConsTempVars fas) 375 | 376 | -- bs:True for strict,False for lazy 377 | genCons (s:bs) = let cs = zip3 bs as ([1..]::[Int64]) in 378 | temp <> encode "inline " <> 379 | (if s then rt else (encode "steak::lazy_type_t<" <> rt <> encode '>')) <> encode ' ' <> 380 | name <> 381 | (wrapParenthese.unsplitB ',') (map (\(b,a,i)->if b then 382 | encode "const " <> a <> encode "& v" <> encode i else 383 | encode "steak::lazy_type_t<" <> a <> encode "> v" <> encode i) cs) <> 384 | (wrapBrace . (if s then id else wrapLazyFun rt)) ( 385 | encode "return " <> genCaseClass c <> ( (wrapParenthese.unsplitB ',') (map (\(b,a,i)-> if b then 386 | encode 'v' <> encode i else 387 | encode 'v' <> encode i <> encode ".get()") cs) ) <> encode ";" ) 388 | 389 | in (unsplitB '\n' $ map genCons pbs) 390 | 391 | sTotTemp = if null tempDef then mempty else encode "template" <> wrapAngle (genConsTempVars tempDef) 392 | sCaseClass = (wrapAngle . unsplitB ',' . map genCaseClass) consLst 393 | 394 | code = 395 | encode "/*\n" <> transNestedComment (slice (start,end) buf) <> encode ";\n*/\n" <> 396 | mconcat (map genRegCons consLst) <> 397 | sTotTemp <> encode "struct " <> dataName <> encode ";\n" <> 398 | sTotTemp <> encode "struct " <> dataName <> encode ":public steak::data_class_t" <> sCaseClass <> 399 | encode "\n{\n\t" <> dataName <> encode "(){}\n\ttemplate" <> dataName <> encode "(K...args):steak::data_class_t" <> sCaseClass <> encode "(args...){}\n};\n" <> 400 | (mconcat $ map (<> encode '\n') (map genConsFunDefs consLst)) <> encode '\n' 401 | return code 402 | 403 | matchExpr::Parser Builder 404 | matchExpr = try $ do 405 | cons<-identifier 406 | parenthese $ do 407 | paras<-option mempty $ commaSep (matchExpr <|> identifier) 408 | let code = encode "steak::attach_ghost cons <> encode ">" <> 409 | encode "(steak::forward_and_zip(" <> unsplitB ',' paras <> encode "))" 410 | return code 411 | 412 | matchHead::Parser Builder 413 | matchHead = do 414 | stringW "match" 415 | parenthese $ identifier 416 | 417 | match::Parser Builder 418 | match = do 419 | -- Parse 420 | ByteStream start _ buf <- getInput 421 | var<-try matchHead 422 | xs<-braceS $ many $ do 423 | notFollowedBy endBrace 424 | k<-caseKeyWords 425 | mexp<-if k==encode "case" then matchExpr else return mempty 426 | charW ':' 427 | bs<-many ((codeBlock (endBrace <|> caseKeyWords)) <|> match) 428 | return (mexp,mconcat bs) 429 | ByteStream end _ _ <- getInput 430 | 431 | -- Code Generator 432 | let blocks = zip xs ([1..]::[Int64]) 433 | code = 434 | encode "\n/*\n" <> slice (start,end) buf <> encode "\n*/\n" <> 435 | wrapBrace ( 436 | encode "\nint match_num=0;\nauto match_var__=" <> var <> encode ".get();\nif(0){;" <> 437 | mconcat (map (\((expr,_),i)->if expr == mempty then mempty else 438 | encode "}\nelse if(match_var__.match(" <> expr <> encode ")){match_num=" <> encode i <> encode ';') blocks) <> 439 | encode "}\nswitch(match_num)\n{\n" <> 440 | mconcat (map (\((expr,block),i) -> (if expr == mempty then encode "default:\n" else encode "case " <> encode i <> encode ":\n") <> block <> encode '\n') blocks) <> 441 | encode "}\n" 442 | ) 443 | return code 444 | 445 | codeBlock::Parser Builder->Parser Builder 446 | codeBlock endP = do 447 | ByteStream start _ buf <- getInput 448 | many1 $ do 449 | notFollowedBy (dataclassHead <|> matchHead <|> endP) 450 | anyTokens 451 | ByteStream end _ _ <- getInput 452 | if end==start then 453 | fail "" 454 | else 455 | return (slice (start,end) buf) 456 | 457 | 458 | globalParser::Parser Builder 459 | globalParser = do 460 | whiteSpace 461 | xs<-many ((codeBlock (fail "")) <|> dataclass <|> match) 462 | return $ mconcat xs 463 | 464 | compile::String->Handle->Handle->IO () 465 | compile name hin hout = do 466 | src<-BS.hGetContents hin 467 | case runParser globalParser emptyState name (toByteStream src) of 468 | Left x -> hPutStrLn stderr (show x) 469 | Right y -> BS.hPutStr hout (toLazyByteString y) 470 | return () 471 | 472 | judgeFile::FilePath->IO (Either FilePath [FilePath]) 473 | judgeFile f = do 474 | b1<-doesDirectoryExist f 475 | if b1 then do 476 | fs<-getDirectoryContents f 477 | return (Right (map (\s->f s) $ filter (\c->and [c/=".",c/=".."]) fs)) 478 | else do 479 | b2<-doesFileExist f 480 | if b2 then do 481 | let (fn,ext)=splitExtensions f 482 | (stk,cpp) = splitAt 5 ext 483 | if and [stk == ".stk.",or [cpp=="cpp",cpp=="hpp",cpp=="h"]] then 484 | return (Left (addExtension fn cpp)) 485 | else 486 | return (Left "") 487 | else 488 | return (Left "") 489 | 490 | compilePathOrDir::FilePath->IO () 491 | compilePathOrDir fs = do 492 | let search f = do 493 | jf<-judgeFile f 494 | case jf of 495 | Left file->when (null file==False) $ do 496 | hin<-openFile f ReadMode 497 | hout<-openFile file WriteMode 498 | compile f hin hout 499 | hClose hout 500 | hClose hin 501 | Right dir-> sequence_ (map search dir) 502 | return () 503 | 504 | b1<-doesFileExist fs 505 | b2<-doesDirectoryExist fs 506 | if b1 then do 507 | hin<-openFile fs ReadMode 508 | compile fs hin stdout 509 | hClose hin 510 | else if b2 then 511 | search fs 512 | else do 513 | hPutStrLn stderr "fatal error: no such file or directory." 514 | putStrLn help 515 | return () 516 | 517 | 518 | main = do 519 | args<-getArgs 520 | if null args then 521 | hPutStrLn stderr "fatal error: no input file or directory." 522 | else do 523 | let arg = head args 524 | if arg == "--version" then 525 | putStrLn version 526 | else if arg == "--help" then 527 | putStrLn help 528 | else 529 | compilePathOrDir arg 530 | return () -------------------------------------------------------------------------------- /include/steak.h: -------------------------------------------------------------------------------- 1 | #ifndef _STEAK_H_ 2 | #define _STEAK_H_ 3 | /** 4 | * @brief a functional library for cpp 5 | * @author nicekingwei(Long Jinwei) 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "runtime.h" 13 | 14 | #define With(x) \ 15 | if (1) \ 16 | { \ 17 | auto match_var = x.get(); \ 18 | if (0) \ 19 | { \ 20 | ; 21 | #define Case(name, ...) \ 22 | } \ 23 | else if (match_var.match(steak::attach_ghost(steak::forward_and_zip(__VA_ARGS__)))) \ 24 | { \ 25 | ; 26 | #define Default \ 27 | } \ 28 | else \ 29 | { \ 30 | ; 31 | #define EndWith() \ 32 | } \ 33 | } 34 | #define UnZip(name, ...) steak::attach_ghost(steak::forward_and_zip(__VA_ARGS__)) 35 | #define Data(T, name, ...) \ 36 | T struct name; \ 37 | T struct name : public steak::data_class_t<__VA_ARGS__> \ 38 | { \ 39 | name() {} \ 40 | template \ 41 | name(K... args) : steak::data_class_t<__VA_ARGS__>(args...) {} \ 42 | }; 43 | #define RegCons(name) \ 44 | namespace steak_constructors \ 45 | { \ 46 | struct name \ 47 | { \ 48 | static constexpr const char *consname = #name; \ 49 | }; \ 50 | }; 51 | #define RealType(name, ...) steak::cal_cons_type::R<__VA_ARGS__> 52 | #define DerivingShow(T, name) \ 53 | T std::ostream &operator<<(std::ostream &out, name &x) \ 54 | { \ 55 | return x.show(out); \ 56 | } 57 | 58 | namespace steak 59 | { 60 | #define Cons0(T, name, ret) \ 61 | T inline steak::lazy_type_t name() \ 62 | { \ 63 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t()); })); \ 64 | } 65 | 66 | #define Cons1(T, name, ret, t1) \ 67 | T inline steak::lazy_type_t name(steak::lazy_type_t v1) \ 68 | { \ 69 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t(v1.get())); })); \ 70 | } 71 | 72 | #define Cons2(T, name, ret, t1, t2) \ 73 | T inline steak::lazy_type_t name(steak::lazy_type_t v1, steak::lazy_type_t v2) \ 74 | { \ 75 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t(v1.get(), v2.get())); })); \ 76 | } 77 | 78 | #define Cons3(T, name, ret, t1, t2, t3) \ 79 | T inline steak::lazy_type_t name(steak::lazy_type_t v1, steak::lazy_type_t v2, steak::lazy_type_t v3) \ 80 | { \ 81 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t(v1.get(), v2.get(), v3.get())); })); \ 82 | } 83 | 84 | #define Cons4(T, name, ret, t1, t2, t3, t4) \ 85 | T inline steak::lazy_type_t name(steak::lazy_type_t v1, steak::lazy_type_t v2, steak::lazy_type_t v3, steak::lazy_type_t v4) \ 86 | { \ 87 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t(v1.get(), v2.get(), v3.get(), v4.get())); })); \ 88 | } 89 | 90 | #define Cons5(T, name, ret, t1, t2, t3, t4, t5) \ 91 | T inline steak::lazy_type_t name(steak::lazy_type_t v1, steak::lazy_type_t v2, steak::lazy_type_t v3, steak::lazy_type_t v4, steak::lazy_type_t v5) \ 92 | { \ 93 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t(v1.get(), v2.get(), v3.get(), v4.get(), v5.get())); })); \ 94 | } 95 | 96 | #define Cons6(T, name, ret, t1, t2, t3, t4, t5, t6) \ 97 | T inline steak::lazy_type_t name(steak::lazy_type_t v1, steak::lazy_type_t v2, steak::lazy_type_t v3, steak::lazy_type_t v4, steak::lazy_type_t v5, steak::lazy_type_t v6) \ 98 | { \ 99 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t(v1.get(), v2.get(), v3.get(), v4.get(), v5.get(), v6.get())); })); \ 100 | } 101 | 102 | #define Cons7(T, name, ret, t1, t2, t3, t4, t5, t6, t7) \ 103 | T inline steak::lazy_type_t name(steak::lazy_type_t v1, steak::lazy_type_t v2, steak::lazy_type_t v3, steak::lazy_type_t v4, steak::lazy_type_t v5, steak::lazy_type_t v6, steak::lazy_type_t v7) \ 104 | { \ 105 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t(v1.get(), v2.get(), v3.get(), v4.get(), v5.get(), v6.get(), v7.get())); })); \ 106 | } 107 | 108 | #define Cons8(T, name, ret, t1, t2, t3, t4, t5, t6, t7, t8) \ 109 | T inline steak::lazy_type_t name(steak::lazy_type_t v1, steak::lazy_type_t v2, steak::lazy_type_t v3, steak::lazy_type_t v4, steak::lazy_type_t v5, steak::lazy_type_t v6, steak::lazy_type_t v7, steak::lazy_type_t v8) \ 110 | { \ 111 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t(v1.get(), v2.get(), v3.get(), v4.get(), v5.get(), v6.get(), v7.get(), v8.get())); })); \ 112 | } 113 | 114 | #define Cons9(T, name, ret, t1, t2, t3, t4, t5, t6, t7, t8, t9) \ 115 | T inline steak::lazy_type_t name(steak::lazy_type_t v1, steak::lazy_type_t v2, steak::lazy_type_t v3, steak::lazy_type_t v4, steak::lazy_type_t v5, steak::lazy_type_t v6, steak::lazy_type_t v7, steak::lazy_type_t v8, steak::lazy_type_t v9) \ 116 | { \ 117 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t(v1.get(), v2.get(), v3.get(), v4.get(), v5.get(), v6.get(), v7.get(), v8.get(), v9.get())); })); \ 118 | } 119 | 120 | #define Cons10(T, name, ret, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) \ 121 | T inline steak::lazy_type_t name(steak::lazy_type_t v1, steak::lazy_type_t v2, steak::lazy_type_t v3, steak::lazy_type_t v4, steak::lazy_type_t v5, steak::lazy_type_t v6, steak::lazy_type_t v7, steak::lazy_type_t v8, steak::lazy_type_t v9, steak::lazy_type_t v10) \ 122 | { \ 123 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t(v1.get(), v2.get(), v3.get(), v4.get(), v5.get(), v6.get(), v7.get(), v8.get(), v9.get(), v10.get())); })); \ 124 | } 125 | 126 | #define Cons11(T, name, ret, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) \ 127 | T inline steak::lazy_type_t name(steak::lazy_type_t v1, steak::lazy_type_t v2, steak::lazy_type_t v3, steak::lazy_type_t v4, steak::lazy_type_t v5, steak::lazy_type_t v6, steak::lazy_type_t v7, steak::lazy_type_t v8, steak::lazy_type_t v9, steak::lazy_type_t v10, steak::lazy_type_t v11) \ 128 | { \ 129 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t(v1.get(), v2.get(), v3.get(), v4.get(), v5.get(), v6.get(), v7.get(), v8.get(), v9.get(), v10.get(), v11.get())); })); \ 130 | } 131 | 132 | #define Cons12(T, name, ret, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) \ 133 | T inline steak::lazy_type_t name(steak::lazy_type_t v1, steak::lazy_type_t v2, steak::lazy_type_t v3, steak::lazy_type_t v4, steak::lazy_type_t v5, steak::lazy_type_t v6, steak::lazy_type_t v7, steak::lazy_type_t v8, steak::lazy_type_t v9, steak::lazy_type_t v10, steak::lazy_type_t v11, steak::lazy_type_t v12) \ 134 | { \ 135 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t(v1.get(), v2.get(), v3.get(), v4.get(), v5.get(), v6.get(), v7.get(), v8.get(), v9.get(), v10.get(), v11.get(), v12.get())); })); \ 136 | } 137 | 138 | #define Cons13(T, name, ret, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13) \ 139 | T inline steak::lazy_type_t name(steak::lazy_type_t v1, steak::lazy_type_t v2, steak::lazy_type_t v3, steak::lazy_type_t v4, steak::lazy_type_t v5, steak::lazy_type_t v6, steak::lazy_type_t v7, steak::lazy_type_t v8, steak::lazy_type_t v9, steak::lazy_type_t v10, steak::lazy_type_t v11, steak::lazy_type_t v12, steak::lazy_type_t v13) \ 140 | { \ 141 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t(v1.get(), v2.get(), v3.get(), v4.get(), v5.get(), v6.get(), v7.get(), v8.get(), v9.get(), v10.get(), v11.get(), v12.get(), v13.get())); })); \ 142 | } 143 | 144 | #define Cons14(T, name, ret, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14) \ 145 | T inline steak::lazy_type_t name(steak::lazy_type_t v1, steak::lazy_type_t v2, steak::lazy_type_t v3, steak::lazy_type_t v4, steak::lazy_type_t v5, steak::lazy_type_t v6, steak::lazy_type_t v7, steak::lazy_type_t v8, steak::lazy_type_t v9, steak::lazy_type_t v10, steak::lazy_type_t v11, steak::lazy_type_t v12, steak::lazy_type_t v13, steak::lazy_type_t v14) \ 146 | { \ 147 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t(v1.get(), v2.get(), v3.get(), v4.get(), v5.get(), v6.get(), v7.get(), v8.get(), v9.get(), v10.get(), v11.get(), v12.get(), v13.get(), v14.get())); })); \ 148 | } 149 | 150 | #define Cons15(T, name, ret, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15) \ 151 | T inline steak::lazy_type_t name(steak::lazy_type_t v1, steak::lazy_type_t v2, steak::lazy_type_t v3, steak::lazy_type_t v4, steak::lazy_type_t v5, steak::lazy_type_t v6, steak::lazy_type_t v7, steak::lazy_type_t v8, steak::lazy_type_t v9, steak::lazy_type_t v10, steak::lazy_type_t v11, steak::lazy_type_t v12, steak::lazy_type_t v13, steak::lazy_type_t v14, steak::lazy_type_t v15) \ 152 | { \ 153 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t(v1.get(), v2.get(), v3.get(), v4.get(), v5.get(), v6.get(), v7.get(), v8.get(), v9.get(), v10.get(), v11.get(), v12.get(), v13.get(), v14.get(), v15.get())); })); \ 154 | } 155 | 156 | #define Cons16(T, name, ret, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16) \ 157 | T inline steak::lazy_type_t name(steak::lazy_type_t v1, steak::lazy_type_t v2, steak::lazy_type_t v3, steak::lazy_type_t v4, steak::lazy_type_t v5, steak::lazy_type_t v6, steak::lazy_type_t v7, steak::lazy_type_t v8, steak::lazy_type_t v9, steak::lazy_type_t v10, steak::lazy_type_t v11, steak::lazy_type_t v12, steak::lazy_type_t v13, steak::lazy_type_t v14, steak::lazy_type_t v15, steak::lazy_type_t v16) \ 158 | { \ 159 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t(v1.get(), v2.get(), v3.get(), v4.get(), v5.get(), v6.get(), v7.get(), v8.get(), v9.get(), v10.get(), v11.get(), v12.get(), v13.get(), v14.get(), v15.get(), v16.get())); })); \ 160 | } 161 | 162 | #define Cons17(T, name, ret, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17) \ 163 | T inline steak::lazy_type_t name(steak::lazy_type_t v1, steak::lazy_type_t v2, steak::lazy_type_t v3, steak::lazy_type_t v4, steak::lazy_type_t v5, steak::lazy_type_t v6, steak::lazy_type_t v7, steak::lazy_type_t v8, steak::lazy_type_t v9, steak::lazy_type_t v10, steak::lazy_type_t v11, steak::lazy_type_t v12, steak::lazy_type_t v13, steak::lazy_type_t v14, steak::lazy_type_t v15, steak::lazy_type_t v16, steak::lazy_type_t v17) \ 164 | { \ 165 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t(v1.get(), v2.get(), v3.get(), v4.get(), v5.get(), v6.get(), v7.get(), v8.get(), v9.get(), v10.get(), v11.get(), v12.get(), v13.get(), v14.get(), v15.get(), v16.get(), v17.get())); })); \ 166 | } 167 | 168 | #define Cons18(T, name, ret, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18) \ 169 | T inline steak::lazy_type_t name(steak::lazy_type_t v1, steak::lazy_type_t v2, steak::lazy_type_t v3, steak::lazy_type_t v4, steak::lazy_type_t v5, steak::lazy_type_t v6, steak::lazy_type_t v7, steak::lazy_type_t v8, steak::lazy_type_t v9, steak::lazy_type_t v10, steak::lazy_type_t v11, steak::lazy_type_t v12, steak::lazy_type_t v13, steak::lazy_type_t v14, steak::lazy_type_t v15, steak::lazy_type_t v16, steak::lazy_type_t v17, steak::lazy_type_t v18) \ 170 | { \ 171 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t(v1.get(), v2.get(), v3.get(), v4.get(), v5.get(), v6.get(), v7.get(), v8.get(), v9.get(), v10.get(), v11.get(), v12.get(), v13.get(), v14.get(), v15.get(), v16.get(), v17.get(), v18.get())); })); \ 172 | } 173 | 174 | #define Cons19(T, name, ret, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19) \ 175 | T inline steak::lazy_type_t name(steak::lazy_type_t v1, steak::lazy_type_t v2, steak::lazy_type_t v3, steak::lazy_type_t v4, steak::lazy_type_t v5, steak::lazy_type_t v6, steak::lazy_type_t v7, steak::lazy_type_t v8, steak::lazy_type_t v9, steak::lazy_type_t v10, steak::lazy_type_t v11, steak::lazy_type_t v12, steak::lazy_type_t v13, steak::lazy_type_t v14, steak::lazy_type_t v15, steak::lazy_type_t v16, steak::lazy_type_t v17, steak::lazy_type_t v18, steak::lazy_type_t v19) \ 176 | { \ 177 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t(v1.get(), v2.get(), v3.get(), v4.get(), v5.get(), v6.get(), v7.get(), v8.get(), v9.get(), v10.get(), v11.get(), v12.get(), v13.get(), v14.get(), v15.get(), v16.get(), v17.get(), v18.get(), v19.get())); })); \ 178 | } 179 | 180 | #define Cons20(T, name, ret, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20) \ 181 | T inline steak::lazy_type_t name(steak::lazy_type_t v1, steak::lazy_type_t v2, steak::lazy_type_t v3, steak::lazy_type_t v4, steak::lazy_type_t v5, steak::lazy_type_t v6, steak::lazy_type_t v7, steak::lazy_type_t v8, steak::lazy_type_t v9, steak::lazy_type_t v10, steak::lazy_type_t v11, steak::lazy_type_t v12, steak::lazy_type_t v13, steak::lazy_type_t v14, steak::lazy_type_t v15, steak::lazy_type_t v16, steak::lazy_type_t v17, steak::lazy_type_t v18, steak::lazy_type_t v19, steak::lazy_type_t v20) \ 182 | { \ 183 | return steak::lazy_call(std::function([=]() { return ret(steak::case_class_t(v1.get(), v2.get(), v3.get(), v4.get(), v5.get(), v6.get(), v7.get(), v8.get(), v9.get(), v10.get(), v11.get(), v12.get(), v13.get(), v14.get(), v15.get(), v16.get(), v17.get(), v18.get(), v19.get(), v20.get())); })); \ 184 | } 185 | 186 | #define V 187 | #define Va template 188 | #define Vab template 189 | #define Vabc template 190 | #define Vabcd template 191 | #define Vabcde template 192 | #define Vabcdef template 193 | #define Vabcdefg template 194 | #define Vabcdefgh template 195 | #define Vabcdefghi template 196 | #define Vabcdefghij template 197 | #define Vabcdefghijk template 198 | #define Vabcdefghijkl template 199 | #define Vabcdefghijklm template 200 | #define Vabcdefghijklmn template 201 | #define Vabcdefghijklmno template 202 | #define Vabcdefghijklmnop template 203 | #define Vabcdefghijklmnopq template 204 | #define Vabcdefghijklmnopqr template 205 | #define Vabcdefghijklmnopqrs template 206 | #define Vabcdefghijklmnopqrst template 207 | }; // namespace steak 208 | #endif --------------------------------------------------------------------------------