├── .gitignore ├── README.md ├── cabal.project ├── examples ├── ackermann.spk ├── better_euler_sieve.spk ├── cartesian.spk ├── church.spk ├── collatz.spk ├── comparedna.spk ├── enumChar.spk ├── euler_sieve.spk ├── evenodd.spk ├── fac.spk ├── fib.spk ├── fix.spk ├── fix2.spk ├── fizzbuzz.spk ├── floats.spk ├── generalized_fizzbuzz.spk ├── hanoi.spk ├── helloworld.spk ├── hintrestrict.spk ├── interactive │ ├── iobind.spk │ └── zeroper.spk ├── iota.spk ├── lcg.spk ├── listmonad.spk ├── lsystem.spk ├── matrix.spk ├── mergesort.spk ├── multimod.spk ├── multipat.spk ├── n_queens.spk ├── newmod.spk ├── ninetyninebottles.spk ├── overloaded_fac.spk ├── printbool.spk ├── putchr.spk ├── quine.spk ├── show.spk ├── statet.spk ├── strpat.spk ├── superrels.spk ├── test_division.spk ├── testbool.spk ├── textbox.spk ├── typeof.spk ├── weird_syntax.spk └── zip.spk ├── runtests.sh ├── runtime ├── js │ └── spinnaker.js └── scm │ └── spinnaker.scm ├── spinnaker.cabal ├── src ├── ArgParser.hs ├── Backends │ ├── MLtoJS.hs │ └── MLtoSCM.hs ├── CompDefs.hs ├── HL │ ├── Defunctionalize.hs │ ├── HLOps.hs │ ├── HLOptimize.hs │ └── Monomorphizer.hs ├── HLDefs.hs ├── ML │ ├── HLtoML.hs │ ├── MLOps.hs │ └── MLOptimize.hs ├── MLDefs.hs ├── Main.hs ├── Parser │ ├── Demod.hs │ ├── MPCL.hs │ └── Parser.hs ├── PrettyPrinter.hs ├── ResultT.hs ├── SyntaxDefs.hs └── Typer │ ├── KindTyper.hs │ ├── MGUs.hs │ ├── TypeTyper.hs │ ├── Typer.hs │ ├── TypingDefs.hs │ └── VariantComplete.hs ├── stdlib ├── core.spk ├── debug.spk ├── either.spk ├── list.spk ├── maybe.spk ├── monad.spk ├── std.spk └── text.spk └── vim ├── ftdetect └── spinnaker.vim └── syntax └── spinnaker.vim /.gitignore: -------------------------------------------------------------------------------- 1 | /dist-newstyle 2 | /brainstorm 3 | /TODO 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Spinnaker programming language 2 | - [The general idea](#idea) 3 | - [Target audience](#target) 4 | - [Features](#features) 5 | - [Basics](#basics) 6 | - [Type relations and instance resolution](#types) 7 | - [Easy lists](#lists) 8 | - [Lets, binds, patterns](#lets) 9 | - [More types](#types) 10 | - [Mutual recursion](#recursion) 11 | - [Modules](#modules) 12 | - [FFI (sort of)](#ffi) 13 | - [Roadmap](#roadmap) 14 | 15 | Compile any example with `cabal run spinnaker -- -f=example_source_path` (if you don't want to install the compiler on your system), this will output an `out.js` , which you can run with `node` or `bun`. 16 | The compiler can emit Javascript or Scheme. To select a backend, simply supply the `--backend=` argument with one of the following: `js` or `scm`. 17 | It probably won't work on Windows, but I haven't tested it. 18 | 19 | ## The general idea 20 | Spinnaker is a purely functional, eagerly evaluated programming language. It's design lies somewhere between Haskell, PureScript and Roc, while it's syntax has had some opinionated changes (for example being whitespace-insensitive). 21 | I have no previous experience in compilers or computer science, but I decided to start implementing the language as a way to explore the problem space and figure out the best architecture. My end goal would be to rewrite the compiler from scratch once I'm satisfied with the features I implemented and how to do it. 22 | I've written my own parsing combinator library and command line argument parser. Of course, they don't work as well as the libraries available, but I want to *'reinvent the wheel'* as much as possible. 23 | 24 | ### Target audience 25 | I decided to make a programming language when I realized that no existing one offered the exact blend of features I wanted. For example, Haskell is awesome, but I don't really like whitespace-sensitivity or lazy evaluation. 26 | As such, don't expect Spinnaker to have some fancy new feature you've never heard of before, or to be revolutionary in any sense. This language will be as good as it needs to be to justify using it over other languages in my own projects. 27 | This document is not intended to sell you on the language, but showcase it to people that might be interested in offering feedback on it. 28 | 29 | ## Features 30 | ### Basics 31 | Spinnaker enables brevity. Here is a very simple Hello World: 32 | ``` 33 | def main = Core.putStrLn "Hello, World!" 34 | ``` 35 | Lambdas are the heart of the language, hence they require minimal syntax. 36 | Spinnaker features polymorphic type inference at the top-level: 37 | ``` 38 | def compose = \f, g, x -> f (g x) 39 | ``` 40 | Operators are easy to define, they are all right-associative and have the same precedence. All operator sections are supported. 41 | ``` 42 | def (<|) = compose 43 | 44 | def add = (+) 45 | def add1 = (1 +) 46 | def sub3 = (- 3) 47 | ``` 48 | Keep in mind that the space between the minus sign and the three is significant. If it weren't there the parser would read an integer literal of value `-3`. This doesn't apply to the plus sign. 49 | 50 | Type relations (analogous to Haskell's MultiParamTypeClasses) provide a mechanism for terse ad-hoc polymorphism: 51 | ``` 52 | def string_true_5 = show True ++ show 5 53 | ``` 54 | Pattern matching is concise: 55 | ``` 56 | def and = \x, y -> 57 | put x, y 58 | | True, True -> True 59 | | _, _ -> False 60 | ``` 61 | You can also write pattern-matching lambdas: 62 | ``` 63 | def and = 64 | \ True, True -> True 65 | | _, _ -> False 66 | 67 | def head = 68 | \ [] -> error "Error message here" 69 | | [x | _] -> x 70 | ``` 71 | Tuple sections are supported: 72 | ``` 73 | def need_fst = (, 1) 74 | def need_snd = (0, ) 75 | def need_middles = (0,,,3) 76 | 77 | def zip = List.map2 (,) 78 | ``` 79 | Spinnaker supports monadic IO, which can be nicely perfomed with the `bind` syntax: 80 | ``` 81 | use Std 82 | 83 | def main = 84 | _ <- putStr "What's your name? "; 85 | name <- getLn; 86 | putStrLn $ "Hello, " ++ name ++ "!" 87 | ``` 88 | You may have noticed that `main` has different types in these examples. We can get away with it because the compiler leverages the power of type relations to choose the best implementation of the entry point. This behavior is extensible to user-defined types by implementing the `ProgramTop` relation. 89 | 90 | ### Type relations and instance resolution 91 | You can define type relations, for example the classic `Functor` and `Monad`: 92 | ``` 93 | rel pub Functor f = fmap : forall a b. (a -> b) -> f a -> f b 94 | 95 | # A Monad relation requires a Functor super-relation 96 | rel pub {Functor m} => Monad m = 97 | return : forall a. a -> m a, 98 | bind : forall a b. m a -> (a -> m b) -> m b 99 | ``` 100 | An example of an instance: 101 | ``` 102 | inst Functor Maybe { 103 | def fmap = 104 | \ _, None -> None 105 | | f, Some x -> Some (f x) 106 | } 107 | ``` 108 | Overlapping instances are permitted, Spinnaker always chooses the most specific instance. If it can't, it fails. This can be useful, for example, to avoid implementing a `Functor` for every `Monad` if you don't want to, but you can still provide an efficient implementation for a specific type, which the compiler will choose. 109 | In fact, the standard library provides this instance: 110 | ``` 111 | inst forall m. {Monad m} => Functor m { 112 | def fmap = \f, m -> bind m (f |> return) 113 | } 114 | ``` 115 | 116 | ### Easy lists 117 | There's special syntax for lists, which refer to a user-defined `List` type in the standard library. For example, this removes the second element of a list: 118 | ``` 119 | def remove_second : forall a. [a] -> [a] 120 | = \[x, x' | xs] -> [x | xs] 121 | ``` 122 | 123 | ### Lets, binds, patterns 124 | Let and bind syntaxes respectively reduce to pattern matching and lambdas, so you can use any valid pattern in them. 125 | ``` 126 | let = e0 -> e1 127 | #same as 128 | put e0 | -> e1 129 | 130 | <- e0; e1 131 | #same as 132 | bind e0 (\ -> e1) 133 | ``` 134 | 135 | ### More types 136 | Data types are defined as you'd expect: 137 | ``` 138 | data Either a b = Left a | Right b 139 | ``` 140 | Type synonyms are experimental: 141 | ``` 142 | typesyn String = [Chr] 143 | typesyn Point a = (a, a) 144 | ``` 145 | You can provide type annotations to top-level definitions and expressions. While they are generally not needed, they are sometimes necessary to specify relation instances: 146 | ``` 147 | def fac : Int -> Int = 148 | \ 0 -> 1 149 | | n -> n * fac (n - 1) 150 | 151 | def map : forall a b. (a -> b) -> [a] -> [b] = 152 | \ _, [] -> [] 153 | | f, [x | xs] -> f x :: map f xs 154 | 155 | def false = toEnum 0 : Bool 156 | ``` 157 | 158 | ### Mutual recursion 159 | To aid type inference and clarity, mutually recursive values must be defined together with the `and` keyword: 160 | ``` 161 | def isEven = 162 | \ 0 -> True 163 | | n -> not $ isOdd $ n - 1 164 | 165 | and isOdd = 166 | \ 0 -> False 167 | | n -> not $ isEven $ n - 1 168 | ``` 169 | This also works for data types (but not for type synonyms): 170 | ``` 171 | data A a b = NilA a | ConsA a (B a b) 172 | and B a b = NilB b | ConsB b (A a b) 173 | ``` 174 | 175 | ### Modules 176 | You can define modules and import them from files: 177 | ``` 178 | # ./a.spk 179 | def pub answer_to_everything = 42 180 | 181 | mod pub Nums { 182 | def pub five = 5 183 | } 184 | 185 | # ./main.spk 186 | mod A "a.spk" 187 | use A 188 | 189 | def main = answer_to_everything + Nums.five 190 | ``` 191 | You can also bring a module in scope for a select expression with the inline use syntax. Note that parentheses aren't optional. 192 | ``` 193 | def main = Std.( 0..10 ) 194 | ``` 195 | 196 | ### FFI (sort of) 197 | As of now, you can only use functions defined in the host language (which, at the moment, is just the `js` backend). They can be declared in Spinnaker as follows (note that they must be monomorphic): 198 | ``` 199 | ext someExternalFunction "functionName" : Int, Flt -> Bool 200 | 201 | def getABool = someExternalFunction 3 1.2 202 | ``` 203 | This compiles to the following `js` call: 204 | ``` 205 | functionName(3, 1.2) 206 | ``` 207 | Such an approach fails when considering user-defined or polymorphic types, but it's sufficient to define a basic runtime. 208 | 209 | ## Roadmap 210 | Entries are in rough order of priority 211 | - Expand the standard library 212 | - Compilation to a C subset 213 | - Decent error messages 214 | - Document compiler behavior and restrictions 215 | - Support for defining libraries 216 | - Automatic generation of documentation from sources 217 | - The compiler is painfully slow, this needs fixing 218 | - Tail-call optimization 219 | - REPL 220 | - Redefine an FFI (in particular, function exports) 221 | - Add a `comptime` directive for certain types of expressions 222 | - Maybe implement the Perceus technique 223 | - Still on the fence about records and row polymorphism 224 | -------------------------------------------------------------------------------- /cabal.project: -------------------------------------------------------------------------------- 1 | packages: ./spinnaker.cabal 2 | -------------------------------------------------------------------------------- /examples/ackermann.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | 3 | def ackermann = 4 | \ 0, n -> n + 1 5 | | m, 0 -> ackermann (m - 1) 1 6 | | m, n -> ackermann (m - 1) (ackermann m (n - 1)) 7 | 8 | def main = map (ackermann 3) (0..=5) 9 | -------------------------------------------------------------------------------- /examples/better_euler_sieve.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | use List 3 | 4 | def filter_muls_aux = 5 | \ acc, _, _, [] -> reverse acc 6 | | acc, cur, n, l@[x | xs] -> 7 | if cur > x then filter_muls_aux (x :: acc) cur n xs 8 | else if cur == x then filter_muls_aux acc (cur+n) n xs 9 | else filter_muls_aux acc (cur+n) n l 10 | def filter_muls = \x -> filter_muls_aux [] x x 11 | 12 | def sieve_iter = 13 | \ lim, acc, l@[x | xs] -> 14 | if lim < x*x then reverse acc ++ l 15 | else sieve_iter lim (x :: acc) (filter_muls x xs) 16 | def sieve = \n -> sieve_iter n [] (2..=n) 17 | 18 | def main = putStrLn $ "There are " ++ show (length (sieve 10_000)) ++ " primes up to 10000" 19 | -------------------------------------------------------------------------------- /examples/cartesian.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | 3 | def cartesian = \l, l' -> 4 | x <- l; 5 | map (x,) l' 6 | 7 | def powerset = 8 | \ [] -> [[]] 9 | | [x | xs] -> 10 | s <- powerset xs; 11 | [s, x::s] 12 | 13 | def main = 14 | _ <- print $ cartesian ('a'..='d') (1..=3); 15 | print $ powerset (0..3) 16 | -------------------------------------------------------------------------------- /examples/church.spk: -------------------------------------------------------------------------------- 1 | use Core 2 | 3 | mod Church { 4 | def pub true = \a, b -> a 5 | def pub false = \a, b -> b 6 | 7 | def pub zero= \f, x -> x 8 | def pub succ= \a, f, x -> a f (f x) 9 | def pub one = succ zero 10 | 11 | def pub add = \a, b, f, x -> a f (b f x) 12 | def pub mul = \a, b, f, x -> a (b f) x 13 | def pub exp = \a, b -> b a 14 | 15 | # def pub pred : forall n. ((n -> n) -> n -> n) -> (n -> n) -> n -> n 16 | # = \n, f, x -> n(\g, h->h (g f)) (\u->x) (\u->u) 17 | # def pub sub = \a, b -> b pred a 18 | 19 | def pub iszero = \n -> n (\x -> false) true 20 | 21 | def pub ctoi = \a -> a (1+) 0 22 | def pub ctob = \a -> a True False 23 | } use Church 24 | 25 | def six = mul (succ one) (succ (succ one)) 26 | 27 | def main = 28 | print (ctoi $ exp six (succ one)) 29 | >> print (ctob $ iszero zero) 30 | >> print (ctob $ iszero one) 31 | -------------------------------------------------------------------------------- /examples/collatz.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | 3 | def collatz = \l, n -> 4 | if n == 1 then 1 :: l else 5 | collatz (n :: l) ( 6 | put rem n 2 7 | | 0 -> n / 2 8 | | 1 -> 1 + 3*n 9 | ) 10 | 11 | def main = reverse $ collatz [] 42 12 | -------------------------------------------------------------------------------- /examples/comparedna.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | use List 3 | 4 | # TODO: metti nella stdlib 5 | def findIndicesAux = 6 | \ _, acc, _, [] -> reverse acc 7 | | n, acc, f, [x | xs] -> 8 | findIndicesAux (n + 1) (if f x then n :: acc else acc) f xs 9 | and findIndices : forall a. (a -> Bool) -> [a] -> [Int] 10 | = findIndicesAux 0 [] 11 | 12 | def s0 = "gtcgattaca" 13 | def s1 = "atctcatcca" 14 | 15 | def main = putStrLn $ "DNA mismatches: " ++ show $ findIndices id $ map2 (!=) s0 s1 16 | -------------------------------------------------------------------------------- /examples/enumChar.spk: -------------------------------------------------------------------------------- 1 | def main = Std.('a'..='z') 2 | -------------------------------------------------------------------------------- /examples/euler_sieve.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | use Maybe use List 3 | 4 | def sieve = \n -> let sieve_iter = 5 | \ [] -> None 6 | | [x | xs] -> Some (x, xs \\ map (x*) $ x :: take (length xs / x) xs) 7 | -> unfoldr sieve_iter (2..=n) 8 | 9 | def main = sieve 100 10 | -------------------------------------------------------------------------------- /examples/evenodd.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | 3 | def even = 4 | \ 0 -> True 5 | | n -> odd (n - 1) 6 | and odd = 7 | \ 0 -> False 8 | | n -> even (n - 1) 9 | 10 | def main = (0..9) |-> map $ \n->(even n, odd n) 11 | -------------------------------------------------------------------------------- /examples/fac.spk: -------------------------------------------------------------------------------- 1 | use Core 2 | 3 | def fac = 4 | \ 0 -> 1 5 | | n -> n * fac (n - 1) 6 | 7 | def main = fac 5 8 | -------------------------------------------------------------------------------- /examples/fib.spk: -------------------------------------------------------------------------------- 1 | use Core 2 | 3 | def fibAux = 4 | \ seed0, _, 0 -> seed0 5 | | seed0, seed1, n -> fibAux seed1 (seed0+seed1) (n - 1) 6 | 7 | def fib = fibAux 0 1 8 | 9 | def main = fib 10 10 | -------------------------------------------------------------------------------- /examples/fix.spk: -------------------------------------------------------------------------------- 1 | use Core 2 | 3 | def fix : forall a b. ((a -> b) -> a -> b) -> a -> b 4 | = \f, x -> f (fix f) x 5 | 6 | def fac = fix $ 7 | \ _, 0 -> 1 8 | | f, n -> n * f (n - 1) 9 | 10 | def main = fac 5 11 | -------------------------------------------------------------------------------- /examples/fix2.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | 3 | def fix2 = \f, g, x -> f (fix2 g f) x 4 | 5 | def main = 6 | let fix2even = fix2 (\f, x -> if x == 0 then True else f (x - 1)) (\f, x -> if x == 0 then False else f (x - 1)) -> 7 | map fix2even (0..4) 8 | -------------------------------------------------------------------------------- /examples/fizzbuzz.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | 3 | def fizzbuzz = \n -> put rem n 3, rem n 5 4 | | 0, 0 -> "FizzBuzz" 5 | | 0, _ -> "Fizz" 6 | | _, 0 -> "Buzz" 7 | | _, _ -> show n 8 | 9 | def main = putStr $ Text.unlines $ map fizzbuzz $ 1..=15 10 | -------------------------------------------------------------------------------- /examples/floats.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | 3 | def ceiling = (0.0 -) |> floor |> (0 -) 4 | 5 | def round = (2.0 *) |> ceiling |> (/ 2) 6 | 7 | def main = 8 | let l = [0.0, 1.0, 2.3, 2.4999, 2.5, 2.50001, 2.8, 3.0000001, 3.000001] -> 9 | _ <- putStrLn $ "vec: " ++ show l; 10 | _ <- putStrLn $ "floors: " ++ show $ map floor l; 11 | _ <- putStrLn $ "ceilings: " ++ show $ map ceiling l; 12 | _ <- putStrLn $ "rounds: " ++ show $ map round l; 13 | return () 14 | -------------------------------------------------------------------------------- /examples/generalized_fizzbuzz.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | use List 3 | 4 | def fizzbuzz = \divs, words, n -> 5 | let rems = map (rem n) divs -> 6 | if all (0!=) rems then show n 7 | else concat $ map2 (\0, w -> w | _, _ -> "") rems words 8 | 9 | def main = putStr $ Text.unlines $ map (fizzbuzz [2, 3, 5] ["Fizz", "Buzz", "Bazz"]) $ 1..=30 10 | -------------------------------------------------------------------------------- /examples/hanoi.spk: -------------------------------------------------------------------------------- 1 | use Core 2 | 3 | def hanoiAux = 4 | \ _, _, 0 -> [] 5 | | from, to, n -> 6 | let interm = 3 - from + to -> 7 | hanoiAux from interm (n - 1) ++ (from, to) :: hanoiAux interm to (n - 1) 8 | and hanoi = hanoiAux 0 1 9 | 10 | def main = hanoi 4 11 | -------------------------------------------------------------------------------- /examples/helloworld.spk: -------------------------------------------------------------------------------- 1 | def main = Core.putStrLn "Ciao Mondo!" 2 | -------------------------------------------------------------------------------- /examples/hintrestrict.spk: -------------------------------------------------------------------------------- 1 | use Core 2 | 3 | def hinted : Int -> Int = \x->x 4 | def main = hinted (fromInt 5) 5 | -------------------------------------------------------------------------------- /examples/interactive/iobind.spk: -------------------------------------------------------------------------------- 1 | use Core 2 | 3 | def main : IO () 4 | = _ <- putStr "Scrivi il tuo nome: "; 5 | n <- getLn; 6 | putStrLn ("Ciao, " ++ n ++ "!") 7 | -------------------------------------------------------------------------------- /examples/interactive/zeroper.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | use List 3 | 4 | def intersperse = \e, l -> put l 5 | | [] -> [] 6 | | [ x] -> [x] 7 | | [x | xs] -> [x, e | intersperse e xs] 8 | 9 | def diagonal = \l -> put l 10 | | [] -> [] 11 | | [[x|_] | ls] -> x :: diagonal (map tail ls) 12 | 13 | data Segno = Zero | Per | Vuoto 14 | 15 | inst Show Segno { 16 | def show = \c -> put c 17 | | Zero -> "O" 18 | | Per -> "X" 19 | | Vuoto -> " " 20 | } 21 | inst Eq Segno { 22 | def (==) = 23 | \ Zero, Zero -> True 24 | | Per, Per -> True 25 | | Vuoto, Vuoto -> True 26 | | _, _ -> False 27 | def (!=) = \a, b -> not (a == b) 28 | } 29 | 30 | def vuoto = \b, x, y -> nth x (nth y b) == Vuoto 31 | 32 | def showBoard = map (map show |> intersperse "|" |> Text.unwords) 33 | |> intersperse "\n---------\n" |> concat 34 | 35 | def alternate = 36 | \ Zero -> Per 37 | | Per -> Zero 38 | 39 | def wins = \s, brd -> any (all (s==)) $ 40 | diagonal brd 41 | :: diagonal (reverse brd) 42 | :: brd 43 | ++ transpose brd 44 | 45 | def getInput : IO (Int, Int) 46 | = input <- getLn; 47 | put input 48 | | [cx, ',', cy] -> 49 | let x = fromEnum cx - 48 -> 50 | let y = fromEnum cy - 48 -> 51 | if all id [x >= 0, x <= 2, y >= 0, y <= 2] 52 | then return (x, y) 53 | else putStr "Invalid input: " >> getInput 54 | | _ -> putStr "Invalid input: " >> getInput 55 | 56 | def loop : Segno -> [[Segno]] -> IO () 57 | = \p, brd -> putStrLn (showBoard brd) >> 58 | putStr ("Player " ++ show p ++ " input x,y: ") >> 59 | (x, y) <- getInput; 60 | if vuoto brd x y then 61 | let brd' = updateAt y (updateAt x (const p)) brd -> 62 | if wins p brd' 63 | then putStrLn (showBoard brd') >> putStrLn ("Player " ++ show p ++ " won!") 64 | else if any (any (Vuoto==)) brd' 65 | then loop (alternate p) brd' 66 | else putStrLn (showBoard brd') >> putStrLn "Draw!" 67 | else putStrLn "This square is already occupied" >> loop p brd 68 | 69 | 70 | def main = loop Zero (replicate 3 (replicate 3 Vuoto)) 71 | -------------------------------------------------------------------------------- /examples/iota.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | 3 | data Iota 4 | = ILeaf 5 | | INode Iota Iota 6 | 7 | inst Show Iota { 8 | def show = 9 | \ ILeaf -> "ι" 10 | | INode i0 i1 -> "(" ++ show i0 ++ show i1 ++ ")" 11 | } 12 | 13 | def iota_parse = 14 | \ ['1'|s] -> (ILeaf, s) 15 | | ['0'|s] -> 16 | let (i0, s') = iota_parse s -> 17 | let (i1, s'') = iota_parse s' -> 18 | (INode i0 i1, s'') 19 | 20 | def jot_revcomp = 21 | \ [] -> "I" 22 | | ['0'|s] -> "((" ++ jot_revcomp s ++ "S)K)" 23 | | ['1'|s] -> "(S(K" ++ jot_revcomp s ++ "))" 24 | 25 | def jot_comp = reverse |> jot_revcomp 26 | 27 | def main = 28 | _ <- putStrLn $ "Parsed Iota: " ++ show $ map (iota_parse |> fst) ["0011011", "0101011"]; 29 | putStrLn $ "Jot to SK: " ++ show $ map jot_comp ["11100", "11111000"] 30 | -------------------------------------------------------------------------------- /examples/lcg.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | 3 | def bsd_round = \n -> rem (12345 + n*1103515245) (2^31) 4 | 5 | data pub Random a = Random (Int -> a) 6 | 7 | inst Monad Random { 8 | def return = \x -> Random (const x) 9 | def bind = 10 | \ Random sf, f -> Random (\s -> 11 | let Random sf' = f (sf s) 12 | -> sf' (bsd_round s)) 13 | } 14 | def pub randInt : Random Int = Random id 15 | 16 | def pub runRandom : forall a. Int -> Random a -> a = 17 | \ seed, Random f -> f seed 18 | 19 | def main = runRandom 0 $ 20 | x <- randInt; 21 | y <- randInt; 22 | z <- randInt; 23 | w <- randInt; 24 | return (x, y, z, w) 25 | -------------------------------------------------------------------------------- /examples/listmonad.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | 3 | def main = 4 | x <- 0..=5; 5 | y <- 0..=4; 6 | _ <- Monad.guard (5 >= x+y); 7 | return (x, y) 8 | -------------------------------------------------------------------------------- /examples/lsystem.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | use List 3 | use Maybe 4 | 5 | def step = \rules, state -> 6 | c <- state; 7 | fromMaybe [c] (lookup c rules) 8 | 9 | def stepn = \n, rules, axiom -> 10 | foldl (\s, _ -> step rules s) axiom (0..n) 11 | 12 | def main = 13 | putStrLn (stepn 5 [('0', "01"), ('1', "10")] "0") 14 | >> putStrLn (stepn 2 [('F', "FF"), ('X', "F+[[X]-X]-F[-FX]+X")] "X") 15 | -------------------------------------------------------------------------------- /examples/matrix.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | 3 | mod TypeNat { 4 | # TODO: questo deve diventare una libreria, è abbastanza utile 5 | 6 | data pub S a = S a 7 | data pub Z = Z 8 | # NOTE: Wrapper per i TypeNat, è un semplice holder di phantom type 9 | data pub NatWrap a = NatWrap 10 | 11 | # Numeri natuali condificati con Peano in type-level, al momento si può controllare soltanto l'equivalenza grazie all'unificazione, ma con le dipendenze funzionali si potrebbero anche codificare delle operazioni di base 12 | # Un tipo è un naturale di type-level se implementa questa relazione, cioè se si può convertire (tramite un wrapper) a un naturale value-level 13 | rel pub TyNat a = tynatInt : NatWrap a -> Int 14 | 15 | # Istanze della relazione per 0 e S(n) 16 | inst TyNat Z { def tynatInt = const 0 } 17 | inst forall a. {TyNat a} => TyNat (S a) { 18 | def tynatInt = const (1 + tynatInt (NatWrap : NatWrap a)) 19 | } 20 | } 21 | 22 | mod Matrix { 23 | use List 24 | use Maybe 25 | use TypeNat 26 | 27 | # Una matrice è un wrapper di una lista di liste, con due phantom type che rappresentano il numero di righe e colonne 28 | data pub Matrix r c a = Matrix [[a]] 29 | 30 | # Stampa la matrice 31 | inst forall r c a.{TyNat r, TyNat c, Show [a]} => Show (Matrix r c a) { 32 | def show = \Matrix l -> "Matrix " ++ show (tynatInt $ NatWrap : NatWrap r) ++ "x" ++ show (tynatInt $ NatWrap : NatWrap c) ++ ':'::show l 33 | } 34 | 35 | # Matrice identità, nota che le dimensioni della matrice vengono rilevate automaticamente 36 | def pub identity : forall n a.{TyNat n, Num a} => Matrix n n a 37 | = let n = tynatInt (NatWrap : NatWrap n) -> 38 | let iter = 0..n -> 39 | Matrix $ map (\y -> map (\x -> if x == y then fromInt 1 else fromInt 0) iter) iter 40 | 41 | # Matrice zero, nota che le dimensioni della matrice vengono rilevate automaticamente 42 | def pub zero : forall r c a.{TyNat r, TyNat c, Num a} => Matrix r c a 43 | = let r = tynatInt (NatWrap : NatWrap r) -> 44 | let c = tynatInt (NatWrap : NatWrap c) -> 45 | let iterr = 0..r -> 46 | let iterc = 0..c -> 47 | Matrix $ map (const $ map (const $ fromInt 0) iterc) iterr 48 | 49 | # Converti una list di liste in matrice controllandone le dimensioni, un crash è possibile solo qui 50 | def pub mxfromList : forall r c a. {TyNat r, TyNat c, Num a} => [[a]] -> Matrix r c a 51 | = \l -> 52 | let r = tynatInt (NatWrap : NatWrap r) -> 53 | let c = tynatInt (NatWrap : NatWrap c) -> 54 | Matrix $ if length l != r 55 | then error "List has wrong number of rows" 56 | else map (\l' -> if length l' != c then error "List row has wrong number of columns" else l') l 57 | 58 | # Trasposizione 59 | def pub mxtranspose : forall r c a. Matrix r c a -> Matrix c r a 60 | = \Matrix l -> Matrix (transpose l) 61 | 62 | # Somma 63 | def pub mxadd : forall r c a.{TyNat r, TyNat c, Num a} => Matrix r c a -> Matrix r c a -> Matrix r c a 64 | = \Matrix l, Matrix l' -> Matrix $ map2 (map2 (+)) l l' 65 | 66 | # Prodotto interno generalizzato 67 | def pub mxinner : forall m n p a b c. ([a] -> [b] -> c) -> Matrix m n a -> Matrix n p b -> Matrix m p c 68 | = \f, Matrix l, Matrix l' -> 69 | let tl' = transpose l' -> 70 | Matrix $ map (\rowa -> map (f rowa) tl') l 71 | 72 | # Prodotto tra matrici classico 73 | def pub mxmul : forall m n p a. {Num a} => Matrix m n a -> Matrix n p a -> Matrix m p a 74 | = mxinner (\a, b -> foldl (+) (fromInt 0) (map2 (*) a b)) 75 | 76 | def swapFront = 77 | \ _, _, [] -> None 78 | | n, curr, [r | rows] -> 79 | if all (fromInt 0 ==) (take n r) && fromInt 0 != nth n r 80 | then Some (r, reverse curr ++ rows) 81 | else swapFront n (r :: curr) rows 82 | 83 | # Annulla l'n-esimo elemento di ogni riga grazie a una somma proporzionale a r 84 | def canceln = \n, r, rows -> 85 | map (\row -> 86 | let ratio = nth n row / nth n r -> 87 | map2 (-) row (map (ratio *) r) 88 | ) rows 89 | 90 | # Annulla gli elementi superiori arrivando a una diagonale (NOTE: l'input è invertito) 91 | def cancelTop = 92 | \ _, curr, [] -> curr 93 | | n, curr, [r | rows] -> 94 | cancelTop (n - 1) (map (/ nth n r) r :: curr) (canceln n r rows) 95 | 96 | # Diagonalizza 97 | def diagonalize = 98 | \ n, curr, [] -> return $ cancelTop (n - 1) [] curr 99 | | n, curr, rows -> 100 | (r, rows') <- swapFront n [] rows; 101 | diagonalize (n + 1) (r :: curr) (canceln n r rows') 102 | 103 | # Trova l'inverso con l'eliminazione Gauss-Jordan 104 | def pub inverseList 105 | = \list -> 106 | let n = length list -> 107 | let ident = 108 | let iter = 0..n -> 109 | map (\y -> map (\x -> if x == y then fromInt 1 else fromInt 0) iter) iter -> 110 | let augmented = map2 (++) list ident -> 111 | fmap (map (drop n)) $ diagonalize 0 [] augmented 112 | 113 | # Wrapper per le matrici type-safe 114 | def pub mxinverse : forall m a. {Eq a, Num a} => Matrix m m a -> Maybe (Matrix m m a) 115 | = \Matrix l -> fmap Matrix (inverseList l) 116 | } use Matrix 117 | 118 | use TypeNat 119 | 120 | # Risolve i Least Squares: (X^T X)^-1 X^T Y 121 | def lsq_solve = \x, y -> 122 | fmap (flip mxmul (mxmul (mxtranspose x) y)) (mxinverse $ mxmul (mxtranspose x) x) 123 | 124 | def main = 125 | let x = mxfromList [[0.0, 1.0], [1.0, 1.0], [2.0, 1.0], [3.0, 1.0]] : Matrix (S(S(S(S Z)))) (S(S Z)) Flt -> 126 | let y = mxfromList [[1.0], [2.0], [4.0], [-1.0]] : Matrix (S(S(S(S Z)))) (S Z) Flt -> 127 | lsq_solve x y 128 | -------------------------------------------------------------------------------- /examples/mergesort.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | use List 3 | 4 | mod pub Sort { 5 | def split = \l -> splitAt (length l / 2) l 6 | 7 | def merge = \cmp, ll, lr -> 8 | put ll, lr 9 | | l, [] -> l 10 | | [], l -> l 11 | | [xa | xsa], [xb | xsb] -> ( 12 | put cmp xa xb 13 | | True -> xa :: merge cmp xsa (xb::xsb) 14 | | False -> xb :: merge cmp (xa::xsa) xsb 15 | ) 16 | 17 | def pub sortBy = \cmp, list -> 18 | put list 19 | | [] -> [] 20 | | [x] -> list 21 | | _ -> let (left, right) = split list -> 22 | merge cmp (sortBy cmp left) (sortBy cmp right) 23 | 24 | def pub sort = sortBy (<=) 25 | } 26 | 27 | def ordleft = \(a,_),(b,_)->a <= b 28 | def ordright = \(_,a),(_,b)->a <= b 29 | 30 | def main = 31 | Sort.sortBy ordleft $ 32 | Sort.sortBy ordright $ 33 | x <- -5..0; 34 | y <- reverse $ 0..10; 35 | return (x, y) 36 | -------------------------------------------------------------------------------- /examples/multimod.spk: -------------------------------------------------------------------------------- 1 | mod M { 2 | mod A { 3 | def pub a = 10 4 | def pub b = 1 5 | } 6 | mod B { 7 | def pub a = 11 8 | def pub c = 2 9 | } 10 | use pub A 11 | use B 12 | } 13 | 14 | def main = M.a 15 | -------------------------------------------------------------------------------- /examples/multipat.spk: -------------------------------------------------------------------------------- 1 | use Core 2 | 3 | def main = putStrLn $ 4 | put 213, True 5 | | 213, False -> "a" 6 | | 2, True -> "b" 7 | | _, False -> "c" 8 | | 3, _ -> "d" 9 | | a, b -> show (a, b) 10 | -------------------------------------------------------------------------------- /examples/n_queens.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | use List use Monad 3 | 4 | def is_safe_aux = \i, ps, n -> put ps 5 | | [] -> True 6 | | [p | ps'] -> 7 | if (p != n) && (abs (n - p) != i) 8 | then is_safe_aux (i + 1) ps' n 9 | else False 10 | and is_safe = is_safe_aux 1 11 | 12 | def partial = \n, i -> put i 13 | | 0 -> [[]] 14 | | _ -> 15 | q <- partial n (i - 1); 16 | p <- 0..n; 17 | _ <- guard (is_safe q p); 18 | return (p :: q) 19 | 20 | def n_queens = \n -> partial n n 21 | 22 | def print_sol = 23 | let print_row = \n, p -> putStrLn $ concat $ map (\x -> if x == p then "Q " else ". ") (0..n) -> 24 | \s -> mapM_ (print_row (length s)) s 25 | 26 | def main = mapM_ (print_sol |> (>> putChr '\n')) $ n_queens 6 27 | -------------------------------------------------------------------------------- /examples/newmod.spk: -------------------------------------------------------------------------------- 1 | mod pub MyMod { 2 | use Core 3 | def internal_1 = 1 4 | def internal_2 = 2 5 | def internal_add = (+) 6 | def pub pubresult = internal_add internal_1 internal_2 7 | } 8 | 9 | mod UseAs{use pub Core} 10 | 11 | def main = UseAs.(*) MyMod.pubresult 5 12 | -------------------------------------------------------------------------------- /examples/ninetyninebottles.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | 3 | def bottles = 4 | \ 0 -> "no more bottles" 5 | | 1 -> "1 bottle" 6 | | n -> show n ++ " bottles" 7 | 8 | def verse = \n -> 9 | if n == 0 10 | then "No more bottles of beer on the wall, no more bottles of beer.\nGo to the store and buy some more, 99 bottles of beer on the wall." 11 | else bottles n 12 | ++ " of beer on the wall, " ++ bottles n ++ " of beer.\nTake one down, pass it around, " 13 | ++ bottles (n - 1) ++ " of beer on the wall.\n" 14 | 15 | def main = Monad.mapM_ (putStrLn <| verse) (reverse $ 0..100) 16 | -------------------------------------------------------------------------------- /examples/overloaded_fac.spk: -------------------------------------------------------------------------------- 1 | use Core 2 | 3 | def fac = \n -> 4 | if n == fromInt 0 5 | then fromInt 1 6 | else n * fac (n - fromInt 1) 7 | 8 | def main = fac (fromInt 5) : Int 9 | -------------------------------------------------------------------------------- /examples/printbool.spk: -------------------------------------------------------------------------------- 1 | use Core 2 | 3 | def main = print True 4 | -------------------------------------------------------------------------------- /examples/putchr.spk: -------------------------------------------------------------------------------- 1 | use Core 2 | 3 | def main = putChr 'c' >> putChr 's' >> putChr '\n' 4 | -------------------------------------------------------------------------------- /examples/quine.spk: -------------------------------------------------------------------------------- 1 | use Std def main=(\s->putStr s>>print s)"use Std def main=(\\s->putStr s>>print s)" 2 | -------------------------------------------------------------------------------- /examples/show.spk: -------------------------------------------------------------------------------- 1 | use Core 2 | 3 | def main = 4 | let s = "Ciao\\\nMondo!" -> 5 | print ((True, 10), (s, ())) 6 | >> putStrLn s 7 | >> print [[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]] 8 | 9 | -------------------------------------------------------------------------------- /examples/statet.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | use Monad 3 | use State 4 | 5 | def main = flip runStateT 0 $ 6 | puts 10 >> 7 | s <- gets; 8 | (lift $ print s) >> 9 | puts (s + 1) 10 | -------------------------------------------------------------------------------- /examples/strpat.spk: -------------------------------------------------------------------------------- 1 | def strtonum = 2 | \ "uno" -> 1 3 | | "due" -> 2 4 | | "tre" -> 3 5 | | _ -> -1 6 | 7 | def main = Core.map strtonum ["uno", "due", "tre", "nonvalido", "un", "ue"] 8 | -------------------------------------------------------------------------------- /examples/superrels.spk: -------------------------------------------------------------------------------- 1 | use Core 2 | 3 | rel Rel0 a = el0:a 4 | rel {Rel0 a} => Rel1 a = el1:a 5 | 6 | inst Rel0 Flt {def el0=0.0} 7 | inst Rel1 Int {def el1=0} 8 | 9 | # Rel0 serve per Rel1, ma viene definito dopo. Questo potrebbe essere un bug. Succede perché i controlli di superrel vengono effettuati durante i controlli delle definizioni, non il typecheck delle istanze 10 | inst Rel0 Int {def el0=0} 11 | 12 | def somef = fmap id <| return 13 | def eq : forall a. {Ord a} => a -> a -> Bool 14 | = (==) 15 | 16 | def main = fmap id (print ()) 17 | -------------------------------------------------------------------------------- /examples/test_division.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | 3 | def dividend_divisor_pairs = [(8, 3), (8, -3), (-8, 3), (-8, -3), (1, 2), (1, -2), (-1, 2), (-1, -2)] 4 | 5 | def main = map (\(a, b) -> (a / b, rem a b)) dividend_divisor_pairs 6 | -------------------------------------------------------------------------------- /examples/testbool.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | 3 | def test = \f -> 4 | b <- [False, True]; 5 | a <- [False, True]; 6 | [f a b] 7 | 8 | def main = 9 | putStr "and " >> print (test (&&)) >> 10 | putStr "or " >> print (test (||)) >> 11 | putStr "not " >> print (map not [False, True]) 12 | -------------------------------------------------------------------------------- /examples/textbox.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | use List 3 | 4 | mod Corners { 5 | def pub v = '│' 6 | def pub h = '─' 7 | def pub ur = '┐' 8 | def pub ul = '┌' 9 | def pub dl = '└' 10 | def pub dr = '┘' 11 | } 12 | 13 | def complete_line = \len, str -> str ++ replicate (len - length str) ' ' 14 | def max_len = map length |> foldl max 0 15 | 16 | def complete = \lines -> 17 | let maxlen = max_len lines -> 18 | map (complete_line maxlen) lines 19 | 20 | def make_box_lines = \lines -> 21 | let lines' = complete lines -> 22 | let len = length $ head lines' -> 23 | let lines'' = map ((++ [' ', Corners.v]) |> ([Corners.v, ' '] ++)) lines' -> 24 | let edge = replicate (len + 2) Corners.h -> 25 | let upper_edge = Corners.ul :: edge ++ [Corners.ur] -> 26 | let lower_edge = Corners.dl :: edge ++ [Corners.dr] -> 27 | upper_edge :: lines'' ++ [lower_edge] 28 | 29 | def make_box = Text.lines |> make_box_lines |> Text.unlines 30 | 31 | def main = putStr $ make_box $ "Hi\nline1\nline_2" 32 | -------------------------------------------------------------------------------- /examples/typeof.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | 3 | mod Typeof { 4 | mod pub Defs { 5 | data pub TOTok a = TOTok 6 | rel pub Typeof a = typeofT : TOTok a -> String 7 | } use Defs 8 | 9 | def pub typeof : forall a. {Typeof a} => a -> String 10 | = const (typeofT (TOTok : TOTok a)) 11 | 12 | inst Typeof Int { def typeofT = const "Int" } 13 | inst Typeof Flt { def typeofT = const "Flt" } 14 | inst Typeof () { def typeofT = const "()" } 15 | inst Typeof Bool { def typeofT = const "Bool" } 16 | inst Typeof Chr { def typeofT = const "Chr" } 17 | inst forall a b. {Typeof a, Typeof b} => Typeof (a, b) { 18 | def typeofT = const $ "(" ++ typeofT (TOTok:TOTok a) ++ ", " ++ typeofT (TOTok:TOTok b) ++ ")" 19 | } 20 | inst forall a. {Typeof a} => Typeof [a] { 21 | def typeofT = const $ "[" ++ typeofT (TOTok:TOTok a) ++ "]" 22 | } 23 | inst Typeof String { def typeofT = const "String" } 24 | 25 | } 26 | 27 | def main 28 | = putStrLn (Typeof.typeof 1) 29 | >> putStrLn (Typeof.typeof 1.1) 30 | >> putStrLn (Typeof.typeof ()) 31 | >> putStrLn (Typeof.typeof True) 32 | >> putStrLn (Typeof.typeof 'a') 33 | >> putStrLn (Typeof.typeof (1, 1.1)) 34 | >> putStrLn (Typeof.typeof [0]) 35 | >> putStrLn (Typeof.typeof "ciao") 36 | >> putStrLn (Typeof.typeof $ List.tail [(1,())]) 37 | -------------------------------------------------------------------------------- /examples/weird_syntax.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | 3 | def minus_six : Int = (-6) 4 | def six_sub : Int -> Int = (6 -) 5 | def sub_six : Int -> Int = (- 6) 6 | 7 | def range : [Int] = (-6..10) 8 | def weird_pat : Int -> Int -> Int = 9 | let (!-!) = (-) -> (!-!) 10 | def minus_pat = let (-5) = -5 -> () 11 | 12 | def diff_range : [Int] = 13 | map sub_six range 14 | 15 | def main = print minus_six >> print diff_range 16 | -------------------------------------------------------------------------------- /examples/zip.spk: -------------------------------------------------------------------------------- 1 | use Std 2 | use List 3 | 4 | def main = let list = [0, 123, 13, 5, 523] -> 5 | map2 (+) (0..length list) list 6 | -------------------------------------------------------------------------------- /runtests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | backend="scm" 3 | interpreter="gambitc -i" 4 | 5 | out_file="out.$backend" 6 | timings_file=$(mktemp) 7 | 8 | for file in examples/*.spk 9 | do 10 | echo "########### Running test: $file" 11 | cabal run spinnaker -- --file $file --verbose --backend=$backend | awk '/Unoptimized program size:/,0' 12 | echo "----" 13 | { time $(echo $interpreter $out_file); } 2>> $timings_file 14 | echo "###########" 15 | echo "" 16 | done 17 | echo "Tests complete. Total time spent in execution: " 18 | awk -F'[ms]' '/real/ {print $2}' $timings_file | awk 'gsub(/,/,".") {sum += $0} END {print sum}' 19 | rm $timings_file 20 | rm $out_file 21 | -------------------------------------------------------------------------------- /runtime/js/spinnaker.js: -------------------------------------------------------------------------------- 1 | class RealWorld_ { constructor(){} } 2 | class Tup0 { constructor(){} } 3 | class Tup2 { 4 | constructor(x0, x1) { 5 | this[0] = x0; 6 | this[1] = x1; 7 | } 8 | } 9 | class Tup3 { 10 | constructor(x0, x1, x2) { 11 | this[0] = x0; 12 | this[1] = x1; 13 | this[2] = x2; 14 | } 15 | } 16 | class Tup4 { 17 | constructor(x0, x1, x2, x3) { 18 | this[0] = x0; 19 | this[1] = x1; 20 | this[2] = x2; 21 | this[3] = x3; 22 | } 23 | } 24 | 25 | function spinnaker_addInt(a, b) { 26 | return a + b; 27 | } 28 | function spinnaker_subInt(a, b) { 29 | return a - b; 30 | } 31 | function spinnaker_mulInt(a, b) { 32 | return a * b; 33 | } 34 | function spinnaker_divInt(a, b) { 35 | return Math.trunc(a / b); //TODO euclidean division 36 | } 37 | function spinnaker_remInt(a, b) { 38 | return a % b; 39 | } 40 | function spinnaker_equInt (a, b) { 41 | return a === b; 42 | } 43 | function spinnaker_neqInt (a, b) { 44 | return a !== b; 45 | } 46 | function spinnaker_leqInt (a, b) { 47 | return a <= b; 48 | } 49 | function spinnaker_greInt (a, b) { 50 | return a > b; 51 | } 52 | 53 | function spinnaker_addFlt(a, b) { 54 | return a + b; 55 | } 56 | function spinnaker_subFlt(a, b) { 57 | return a - b; 58 | } 59 | function spinnaker_mulFlt(a, b) { 60 | return a * b; 61 | } 62 | function spinnaker_divFlt(a, b) { 63 | return a / b; 64 | } 65 | function spinnaker_equFlt (a, b) { 66 | return a === b; 67 | } 68 | function spinnaker_neqFlt (a, b) { 69 | return a !== b; 70 | } 71 | function spinnaker_leqFlt (a, b) { 72 | return a <= b; 73 | } 74 | function spinnaker_greFlt (a, b) { 75 | return a > b; 76 | } 77 | 78 | function spinnaker_andBool (a, b) { 79 | return a && b; 80 | } 81 | function spinnaker_orBool (a, b) { 82 | return a || b; 83 | } 84 | function spinnaker_notBool (a) { 85 | return !a; 86 | } 87 | 88 | function spinnaker_floorFlt(a) { 89 | return Math.floor(a); 90 | } 91 | function spinnaker_convItoF(a) { 92 | return a; 93 | } 94 | 95 | function spinnaker_convItoC(a) { 96 | return String.fromCharCode(a); 97 | } 98 | function spinnaker_convCtoI(a) { 99 | return a.charCodeAt(); 100 | } 101 | 102 | let chrbuffer = "" 103 | function spinnaker_putChr(c,rw) { 104 | chrbuffer = chrbuffer + c; 105 | if (c === '\n') { 106 | process.stdout.write(chrbuffer); 107 | chrbuffer = ""; 108 | } 109 | return rw; 110 | } 111 | 112 | let fs = require("fs"); 113 | function spinnaker_getChr(rw) { 114 | process.stdout.write(chrbuffer); 115 | chrbuffer = ""; 116 | let buffer = Buffer.alloc(4); 117 | fs.readSync(0,buffer,0,1,null); 118 | let fst = buffer[0]; 119 | if (fst < 0xe0) { 120 | if(fst > 0x7F) { 121 | fs.readSync(0,buffer,1,1,null); 122 | } 123 | } else if (fst < 0xf0) { 124 | fs.readSync(0,buffer,1,2,null); 125 | } else { 126 | fs.readSync(0,buffer,1,3,null); 127 | } 128 | let string = buffer.toString('utf8'); 129 | return new Tup2(string.substr(0,string.indexOf('\0')), rw); 130 | } 131 | 132 | //TODO spinnaker_putChr serio, spinnaker_getChr serio, spinnaker_isEOF 133 | function spinnaker_exit(i, rw) { 134 | process.exit(i); 135 | return rw; 136 | } 137 | -------------------------------------------------------------------------------- /runtime/scm/spinnaker.scm: -------------------------------------------------------------------------------- 1 | (define-macro (spinnaker_comp_chrLit x) (integer->char x)) 2 | 3 | (define-syntax spinnaker_addInt (syntax-rules () ((_ a b) 4 | (+ a b)))) 5 | (define-syntax spinnaker_subInt (syntax-rules () ((_ a b) 6 | (- a b)))) 7 | (define-syntax spinnaker_mulInt (syntax-rules () ((_ a b) 8 | (* a b)))) 9 | (define-syntax spinnaker_divInt (syntax-rules () ((_ a b) 10 | (quotient a b)))) 11 | (define-syntax spinnaker_remInt (syntax-rules () ((_ a b) 12 | (remainder a b)))) 13 | (define-syntax spinnaker_equInt (syntax-rules () ((_ a b) 14 | (= a b)))) 15 | (define-syntax spinnaker_neqInt (syntax-rules () ((_ a b) 16 | (not (= a b))))) 17 | (define-syntax spinnaker_leqInt (syntax-rules () ((_ a b) 18 | (<= a b)))) 19 | (define-syntax spinnaker_greInt (syntax-rules () ((_ a b) 20 | (> a b)))) 21 | 22 | (define-syntax spinnaker_addFlt (syntax-rules () ((_ a b) 23 | (+ a b)))) 24 | (define-syntax spinnaker_subFlt (syntax-rules () ((_ a b) 25 | (- a b)))) 26 | (define-syntax spinnaker_mulFlt (syntax-rules () ((_ a b) 27 | (* a b)))) 28 | (define-syntax spinnaker_divFlt (syntax-rules () ((_ a b) 29 | (/ a b)))) 30 | (define-syntax spinnaker_equFlt (syntax-rules () ((_ a b) 31 | (= a b)))) 32 | (define-syntax spinnaker_neqFlt (syntax-rules () ((_ a b) 33 | (not (= a b))))) 34 | (define-syntax spinnaker_leqFlt (syntax-rules () ((_ a b) 35 | (<= a b)))) 36 | (define-syntax spinnaker_greFlt (syntax-rules () ((_ a b) 37 | (> a b)))) 38 | 39 | (define-syntax spinnaker_andBool (syntax-rules () ((_ a b) 40 | (and a b)))) 41 | (define-syntax spinnaker_orBool (syntax-rules () ((_ a b) 42 | (or a b)))) 43 | (define-syntax spinnaker_notBool (syntax-rules () ((_ a) 44 | (not a)))) 45 | 46 | (define-syntax spinnaker_floorFlt (syntax-rules () ((_ a) 47 | (inexact->exact (floor a))))) 48 | (define-syntax spinnaker_convItoF (syntax-rules () ((_ a) 49 | (exact->inexact a)))) 50 | 51 | (define-syntax spinnaker_convItoC (syntax-rules () ((_ a) 52 | (integer->char a)))) 53 | (define-syntax spinnaker_convCtoI (syntax-rules () ((_ a) 54 | (char->integer a)))) 55 | 56 | (define (spinnaker_putChr c rw) 57 | (write-char c) 58 | rw) 59 | (define (spinnaker_getChr rw) 60 | (list "(,)" (read-char) rw)) 61 | 62 | (define (spinnaker_exit i rw) 63 | (exit i) 64 | rw) 65 | -------------------------------------------------------------------------------- /spinnaker.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 3.0 2 | name: spinnaker 3 | version: 0.1.0.0 4 | 5 | -- A short (one-line) description of the package. 6 | synopsis: The Spinnaker Programming Language 7 | 8 | -- A longer description of the package. 9 | description: TODO 10 | 11 | -- A URL where users can report bugs. 12 | -- bug-reports: 13 | 14 | -- The license under which the package is released. 15 | -- license: 16 | author: Caius Iulius Caesar 17 | maintainer: caiusiuliuscaesar84@gmail.com 18 | 19 | -- A copyright notice. 20 | -- copyright: 21 | category: Language 22 | build-type: Simple 23 | -- extra-sources: CHANGELOG.md 24 | 25 | data-files: runtime/ stdlib/ 26 | 27 | common warnings 28 | ghc-options: -Wall -Wno-incomplete-patterns -Wno-incomplete-uni-patterns -Wno-type-defaults -Wno-unused-do-bind -Wno-unused-matches 29 | 30 | executable spinnaker 31 | import: warnings 32 | 33 | main-is: Main.hs 34 | 35 | -- Modules included in this executable, other than Main. 36 | other-modules: 37 | Paths_spinnaker 38 | CompDefs 39 | ArgParser 40 | ResultT 41 | PrettyPrinter 42 | 43 | Parser.MPCL 44 | SyntaxDefs 45 | Parser.Parser 46 | Parser.Demod 47 | 48 | HLDefs 49 | HL.HLOps 50 | Typer.TypingDefs 51 | Typer.MGUs 52 | Typer.KindTyper 53 | Typer.VariantComplete 54 | Typer.TypeTyper 55 | Typer.Typer 56 | 57 | HL.Monomorphizer 58 | HL.HLOptimize 59 | HL.Defunctionalize 60 | 61 | MLDefs 62 | ML.MLOps 63 | ML.HLtoML 64 | ML.MLOptimize 65 | 66 | --Backends.VM.MLtoVM 67 | --Backends.VM.VM 68 | Backends.MLtoJS 69 | Backends.MLtoSCM 70 | -- LANGUAGE extensions used by modules in this package. 71 | -- other-extensions: 72 | build-depends: base ^>=4.18.0.0, mtl, containers 73 | hs-source-dirs: src 74 | default-language: Haskell2010 75 | -------------------------------------------------------------------------------- /src/ArgParser.hs: -------------------------------------------------------------------------------- 1 | module ArgParser where 2 | import Control.Monad(join) 3 | import Data.List(intercalate, transpose, partition) 4 | import Data.Maybe(fromJust, isJust, catMaybes) 5 | import System.Environment 6 | 7 | data Arg = 8 | Arg { 9 | argID :: String, 10 | argShort :: Maybe Char, 11 | argLong :: Maybe String, 12 | argIsOpt :: Bool, 13 | argData :: Maybe ArgData, 14 | argDesc :: String 15 | } 16 | 17 | data ArgData 18 | = ArgDataStr (Maybe String) 19 | | ArgDataOpt [String] (Maybe String) 20 | -- | ArgDataInt (Maybe Int) 21 | -- | ArgDataFlt (Maybe Float) 22 | 23 | showDefault :: (a -> String) -> Maybe a -> String 24 | showDefault _ Nothing = "" 25 | showDefault f (Just d) = " (default=" ++ f d ++ ")" 26 | 27 | showArgData :: ArgData -> String 28 | showArgData (ArgDataStr s) = "" ++ showDefault id s 29 | showArgData (ArgDataOpt opts s) = intercalate "|" opts ++ showDefault id s 30 | --showArgData (ArgDataInt s) = "" ++ showDefault show s 31 | --showArgData (ArgDataFlt s) = "" ++ showDefault show s 32 | 33 | getHelpColumns :: Arg -> [String] 34 | getHelpColumns arg = [maybe "" (('-':) . (:[])) (argShort arg), maybe "" ("--"++) (argLong arg), if argIsOpt arg then "[opt]" else "", maybe "" showArgData (argData arg), argDesc arg] 35 | 36 | showHelp :: [Arg] -> String 37 | showHelp args = 38 | let aass = map getHelpColumns args 39 | maxlens = map (maximum . map length) $ transpose aass 40 | pad len s = s ++ replicate (len - length s) ' ' 41 | aasspadded = map (zipWith pad maxlens) aass 42 | in unlines $ map (intercalate " ") aasspadded 43 | 44 | type Parse = [(String, Maybe String)] 45 | parseArgs :: [Arg] -> [String] -> Either String Parse 46 | parseArgs = inner [] 47 | where inner :: Parse -> [Arg] -> [String] -> Either String Parse 48 | inner parse defs [] = do 49 | defaults <- checkRemaining defs 50 | return $ catMaybes defaults ++ parse 51 | inner parse defs (arg:args) = do 52 | let argname = takeWhile ('='/=) arg 53 | eqargstring = dropWhile ('='/=) arg 54 | margstring = if null eqargstring then Nothing else Just (tail eqargstring) 55 | (def, defs') <- getDef argname defs 56 | case (argData def, margstring) of 57 | (Nothing, Nothing) -> inner ((argID def, Nothing):parse) defs' args 58 | (Just argdata, Just argstring) -> checkData argdata argstring >> inner ((argID def, Just argstring):parse) defs' args 59 | (Nothing, Just _) -> Left $ "unexpected data after the argument: " ++ argID def 60 | (Just argdata, Nothing) -> case args of 61 | [] -> Left $ "expected some data after the argument: " ++ argID def 62 | a:args' -> checkData argdata a >> inner ((argID def, Just a):parse) defs' args' 63 | getDef arg defs = 64 | case partition (\def -> elem arg $ catMaybes [('-':).(:[]) <$> argShort def, ("--"++) <$> argLong def]) defs of 65 | ([], _) -> Left $ "unrecognized argument: " ++ arg 66 | ([def], defs') -> return (def, defs') 67 | checkData (ArgDataStr _) _ = return () 68 | checkData (ArgDataOpt opts _) d = if elem d opts then return () else Left $ d ++ " is not a valid option (" ++ intercalate "|" opts ++ ")" 69 | checkRemaining = mapM (\arg -> 70 | case argData arg of 71 | Just (ArgDataStr (Just def)) -> return $ Just (argID arg, Just def) 72 | Just (ArgDataOpt _ (Just def)) -> return $ Just (argID arg, Just def) 73 | _ -> if argIsOpt arg then return Nothing else Left $ "expected non-optional argument: " ++ argID arg 74 | ) 75 | 76 | parseArgsIO :: [Arg] -> IO Parse 77 | parseArgsIO defs = do 78 | args <- getArgs 79 | case parseArgs defs args of 80 | Left e -> error e 81 | Right p -> return p 82 | 83 | gotArg :: String -> Parse -> Bool 84 | gotArg = (isJust .) . lookup 85 | getArg :: String -> Parse -> Maybe String 86 | getArg = (join .) . lookup 87 | forceGetArg :: String -> Parse -> String 88 | forceGetArg = (fromJust .) . getArg 89 | -------------------------------------------------------------------------------- /src/Backends/MLtoJS.hs: -------------------------------------------------------------------------------- 1 | module Backends.MLtoJS where 2 | import GHC.Unicode(isPrint, isSpace) 3 | import Control.Monad.State 4 | import Data.Maybe(fromJust, fromMaybe) 5 | import HLDefs 6 | import MLDefs 7 | import ML.MLOps 8 | import Typer.TypingDefs(isTupLabl) 9 | 10 | type IdentMap = [(String, String)] 11 | type CodeGen a = State (Int, IdentMap, IdentMap, IdentMap, [String]) a 12 | 13 | emit :: String -> CodeGen () 14 | emit code' = do 15 | (uid, var, comb, lab, code) <- get 16 | put (uid, var, comb, lab, code':code) 17 | 18 | getVariant :: String -> CodeGen String 19 | getVariant v = let (istup, n) = isTupLabl v in 20 | if istup then return $ "Tup"++show n 21 | else do 22 | (_, var, _, _, _) <- get 23 | return $ fromMaybe v (lookup v var) -- se non si trova una variante significa che è una classe esterna, perciò il nome resta invariato 24 | 25 | getCombinator :: String -> CodeGen String 26 | getCombinator c = do 27 | (_, _, comb, _, _) <- get 28 | return $ fromMaybe c (lookup c comb) -- se non si trova un combinatore significa che è una funzione esterna, perciò il nome resta invariato 29 | 30 | getLabel :: String -> CodeGen String 31 | getLabel l = do 32 | (_, _, _, lab, _) <- get 33 | return $ fromJust $ lookup l lab 34 | 35 | newLabel :: CodeGen String 36 | newLabel = do 37 | (uid, var, comb, lab, code) <- get 38 | put (uid+1, var, comb, lab, code) 39 | return ("label"++show uid) 40 | 41 | newMapVariant :: String -> CodeGen String 42 | newMapVariant v = do 43 | (uid, var, comb, lab, code) <- get 44 | put (uid+1, (v, "Variant"++show uid):var, comb, lab, code) 45 | return ("Variant"++show uid) 46 | 47 | newMapCombinator :: String -> CodeGen String 48 | newMapCombinator c = do 49 | (uid, var, comb, lab, code) <- get 50 | put (uid+1, var, (c, "combinator"++show uid):comb, lab, code) 51 | return ("combinator"++show uid) 52 | 53 | newMapLabel :: String -> CodeGen String 54 | newMapLabel l = do 55 | (uid, var, comb, lab, code) <- get 56 | put (uid+1, var, comb, (l,"label"++show uid):lab, code) 57 | return ("label"++show uid) 58 | 59 | toCommaList :: [String] -> String 60 | toCommaList [] = "" 61 | toCommaList [x] = x 62 | toCommaList (x:xs) = x ++ ", " ++ toCommaList xs 63 | 64 | tojsLit :: Literal -> String 65 | tojsLit (LitInteger i) = show i 66 | tojsLit (LitFloating f) = show f 67 | tojsLit (LitCharacter c) 68 | | isSpace c || elem c "\\\"\'" = show c 69 | | isPrint c = ['\"', c, '\"'] 70 | | otherwise = show c 71 | 72 | emitTest :: String -> MLPattern -> CodeGen () 73 | emitTest l (MLPLiteral lit) = emit $ "if(" ++ l ++ " === " ++ tojsLit lit ++ "){\n" 74 | emitTest l (MLPVariant "True") = emit $ "if(" ++ l ++ "){\n" 75 | emitTest l (MLPVariant "False") = emit $ "if(!" ++ l ++ "){\n" 76 | emitTest l (MLPVariant v) = do 77 | v' <- getVariant v 78 | emit $ "if(" ++ l ++ " instanceof " ++ v' ++ "){\n" 79 | 80 | tojsBlock :: (String -> String) -> MLExpr -> CodeGen () 81 | tojsBlock final (_, _, MLLet l e0 e1) = do 82 | l' <- newMapLabel l 83 | e0' <- tojsExpr e0 84 | emit $ "let " ++ l' ++ " = " ++ e0' ++ ";\n" 85 | tojsBlock final e1 86 | tojsBlock final (_, _, MLTest tv pes def) = do 87 | tv' <- tojsExpr tv 88 | l' <- newLabel 89 | emit $ "let " ++ l' ++ " = " ++ tv' ++ ";\n" 90 | mapM_ (\(p, e) -> do 91 | emitTest l' p 92 | tojsBlock final e 93 | emit "} else ") pes 94 | emit "{\n" 95 | tojsBlock final def 96 | emit "}\n" 97 | tojsBlock final (_, _, MLError c s) = emit $ "throw new Error(" ++ show(show c ++ s) ++ ");\n" 98 | tojsBlock final other = do 99 | expr <- tojsExpr other 100 | emit $ final expr 101 | 102 | tojsExpr :: MLExpr -> CodeGen String 103 | tojsExpr (_, _, MLLiteral lit) = return $ tojsLit lit 104 | tojsExpr (_, _, MLLabel l) = getLabel l 105 | tojsExpr (_, _, MLConstructor "True" []) = return "true" 106 | tojsExpr (_, _, MLConstructor "False" []) = return "false" 107 | tojsExpr (_, _, MLProj e _ n) = do 108 | e' <- tojsExpr e 109 | return $ "(" ++ e' ++ ")[" ++ show n ++ "]" 110 | tojsExpr (_, _, MLConstructor v es) = do 111 | v' <- getVariant v 112 | es' <- mapM tojsExpr es 113 | return $ "new " ++ v' ++ "(" ++ toCommaList es' ++ ")" 114 | tojsExpr (_, _, MLCombinator c es) = do 115 | c' <- getCombinator c 116 | es' <- mapM tojsExpr es 117 | return $ c' ++ "(" ++ toCommaList es' ++ ")" 118 | tojsExpr (_, _, MLJoin j es) = do 119 | j' <- getLabel j 120 | es' <- mapM tojsExpr es 121 | return $ j' ++ "(" ++ toCommaList es' ++ ")" 122 | tojsExpr (_, _, MLLet l e0 e1) = do 123 | l' <- newMapLabel l 124 | e0' <- tojsExpr e0 125 | emit $ "let " ++ l' ++ " = " ++ e0' ++ ";\n" 126 | tojsExpr e1 127 | tojsExpr (_, _, MLLetJoin j lvs e0 e1) = do 128 | j' <- newMapLabel j 129 | as <- mapM (newMapLabel . fst) lvs 130 | emit $ "let " ++ j' ++ " = function(" ++ toCommaList as ++ "){\n" 131 | tojsBlock (\e' -> "return " ++ e' ++ ";\n") e0 132 | emit "};\n" 133 | tojsExpr e1 134 | tojsExpr other = do 135 | l <- newLabel 136 | emit $ "let " ++ l ++ ";\n" 137 | tojsBlock (\e -> l ++ " = " ++ e ++ ";\n") other 138 | return l 139 | 140 | tojsVariant :: [String] -> String -> Int -> CodeGen () 141 | tojsVariant vused vname numargs = 142 | if not (elem vname vused) then return () 143 | else do 144 | vname' <- newMapVariant vname 145 | let args = map (("x"++) . show) [0..numargs-1] 146 | emit $ "class " ++ vname' ++ "{\nconstructor(" ++ toCommaList args ++ "){\n" 147 | mapM_ (\(n, arg) -> emit $ "this["++show n++"] = " ++ arg ++ ";\n" 148 | ) $ zip [0..] args 149 | emit "}\n}\n\n" 150 | 151 | tojsDataSummaries :: [String] -> [DataSummary] -> CodeGen () 152 | tojsDataSummaries vused summaries = 153 | let stripped = do 154 | (_, variants) <- summaries 155 | map (\(vname, args) -> (vname, length args)) variants 156 | in mapM_ (uncurry $ tojsVariant vused) stripped 157 | 158 | tojsCombinators :: [MLDef] -> CodeGen () 159 | tojsCombinators combs = do 160 | mapM_ (\(c, _, _) -> newMapCombinator c) combs 161 | mapM_ (\(c, asts, e) -> do 162 | c' <- getCombinator c 163 | as <- mapM (newMapLabel . fst) asts 164 | emit $ "function " ++ c' ++ "(" ++ toCommaList as ++ "){\n" 165 | tojsBlock (\e' -> "return " ++ e' ++ ";\n") e 166 | emit "}\n\n" 167 | ) combs 168 | 169 | tojsProgram :: [DataSummary] -> MLProgram -> String 170 | tojsProgram datasummaries (ep, defs) = concat $ reverse $ (\(_,(_,_,_,_,code))->code) $ flip runState (0, [], [], [], []) $ do 171 | let vused = variantsUsedProg (ep, defs) 172 | tojsDataSummaries vused datasummaries 173 | tojsCombinators defs 174 | tojsBlock (++ ";\n") ep 175 | -------------------------------------------------------------------------------- /src/Backends/MLtoSCM.hs: -------------------------------------------------------------------------------- 1 | module Backends.MLtoSCM where 2 | import Control.Monad.State 3 | import Data.Maybe(fromJust, fromMaybe) 4 | import Data.Char(ord) 5 | import HLDefs 6 | import MLDefs 7 | import ML.MLOps 8 | 9 | type IdentMap = [(String, String)] 10 | type CodeGen a = State (Int, IdentMap, IdentMap, IdentMap, [String]) a 11 | 12 | emit :: String -> CodeGen () 13 | emit code' = do 14 | (uid, var, comb, lab, code) <- get 15 | put (uid, var, comb, lab, code':code) 16 | 17 | getVariant :: String -> CodeGen String 18 | getVariant v = do 19 | (_, var, _, _, _) <- get 20 | return $ fromMaybe (show v) (lookup v var) -- se non si trova una variante significa che è una classe esterna, perciò il nome resta invariato 21 | 22 | getCombinator :: String -> CodeGen String 23 | getCombinator c = do 24 | (_, _, comb, _, _) <- get 25 | return $ fromMaybe c (lookup c comb) -- se non si trova un combinatore significa che è una funzione esterna, perciò il nome resta invariato 26 | 27 | getLabel :: String -> CodeGen String 28 | getLabel l = do 29 | (_, _, _, lab, _) <- get 30 | return $ fromJust $ lookup l lab 31 | 32 | newLabel :: CodeGen String 33 | newLabel = do 34 | (uid, var, comb, lab, code) <- get 35 | put (uid+1, var, comb, lab, code) 36 | return ("label"++show uid) 37 | 38 | newMapVariant :: String -> CodeGen String 39 | newMapVariant v = do 40 | (uid, var, comb, lab, code) <- get 41 | put (uid+1, (v, show $ show uid):var, comb, lab, code) 42 | return (show $ show uid) 43 | 44 | newMapCombinator :: String -> CodeGen String 45 | newMapCombinator c = do 46 | (uid, var, comb, lab, code) <- get 47 | put (uid+1, var, (c, "combinator"++show uid):comb, lab, code) 48 | return ("combinator"++show uid) 49 | 50 | newMapLabel :: String -> CodeGen String 51 | newMapLabel l = do 52 | (uid, var, comb, lab, code) <- get 53 | put (uid+1, var, comb, (l,"label"++show uid):lab, code) 54 | return ("label"++show uid) 55 | 56 | toscmLit :: Literal -> String 57 | toscmLit (LitInteger i) = show i 58 | toscmLit (LitFloating f) = show f 59 | toscmLit (LitCharacter c) = "(spinnaker_comp_chrLit " ++ show (ord c) ++ ")" 60 | 61 | variantAccess :: Int -> String -> String 62 | variantAccess n l = "(cadr" ++ cdrs n l ++ ")" 63 | where 64 | cdrs 0 myl = ' ':myl 65 | cdrs myn myl = "(cdr" ++ cdrs (myn - 1) myl ++ ")" 66 | 67 | genTest :: String -> MLPattern -> CodeGen String 68 | genTest l (MLPLiteral lit@(LitCharacter _)) = return $ "((char=? " ++ l ++ " " ++ toscmLit lit ++ ")" 69 | genTest l (MLPLiteral lit) = return $ "((= " ++ l ++ " " ++ toscmLit lit ++ ")" 70 | genTest l (MLPVariant "True") = return $ "(" ++ l ++ " " 71 | genTest l (MLPVariant "False") = return $ "((not " ++ l ++ ") " 72 | genTest l (MLPVariant v) = do 73 | v' <- getVariant v 74 | return $ "((string=? (car " ++ l ++ ") " ++ v' ++ ") " 75 | 76 | toscmExpr :: MLExpr -> CodeGen String 77 | toscmExpr (_, _, MLTest tv pes def) = do 78 | tv' <- toscmExpr tv 79 | l' <- newLabel 80 | conds <- mapM (\(p, e) -> do 81 | test <- genTest l' p 82 | e' <- toscmExpr e 83 | return $ test ++ e' ++ ")\n") pes 84 | def' <- toscmExpr def 85 | return $ "(let ((" ++ l' ++ " " ++ tv' ++ "))" ++ "(cond " ++ concat conds ++ "(#t " ++ def' ++ "))" ++ ")" 86 | toscmExpr (_, _, MLError c s) = return $ "(error " ++ show(show c ++ s) ++ ")" 87 | toscmExpr (_, _, MLProj e _ n) = do 88 | e' <- toscmExpr e 89 | return (variantAccess n e') 90 | toscmExpr (_, _, MLLiteral lit) = return $ toscmLit lit 91 | toscmExpr (_, _, MLLabel l) = getLabel l 92 | toscmExpr (_, _, MLConstructor "True" []) = return "#t" 93 | toscmExpr (_, _, MLConstructor "False" []) = return "#f" 94 | toscmExpr (_, _, MLConstructor v es) = do 95 | v' <- getVariant v 96 | es' <- mapM toscmExpr es 97 | return $ "(list " ++ unwords (v' : es') ++ ")" --TODO interleave/intersperse 98 | toscmExpr (_, _, MLCombinator c es) = do 99 | c' <- getCombinator c 100 | es' <- mapM toscmExpr es 101 | return $ "(" ++ unwords (c' : es') ++ ")" 102 | toscmExpr (_, _, MLJoin j es) = do 103 | j' <- getLabel j 104 | es' <- mapM toscmExpr es 105 | return $ "(" ++ unwords (j' : es') ++ ")" 106 | toscmExpr (_, _, MLLet l e0 e1) = do 107 | l' <- newMapLabel l 108 | e0' <- toscmExpr e0 109 | e1' <- toscmExpr e1 110 | return $ "(let ((" ++ l' ++ " " ++ e0' ++ "))\n" ++ e1' ++ ")" 111 | toscmExpr (_, _, MLLetJoin j lts e0 e1) = do 112 | j' <- newMapLabel j 113 | e1' <- toscmExpr e1 114 | as <- mapM (newMapLabel . fst) lts 115 | e0' <- toscmExpr e0 116 | return $ "(let ((" ++ j' ++ " (lambda (" ++ unwords as ++ ") " ++ e0' ++ ")))\n" ++ e1' ++ ")" 117 | 118 | 119 | toscmVariant :: [String] -> String -> Int -> CodeGen () 120 | toscmVariant vused vname numargs = 121 | if elem vname vused then const () <$> newMapVariant vname 122 | else return () 123 | 124 | toscmDataSummaries :: [String] -> [DataSummary] -> CodeGen () 125 | toscmDataSummaries vused summaries = 126 | let stripped = do 127 | (_, variants) <- summaries 128 | map (\(vname, args) -> (vname, length args)) variants 129 | in mapM_ (uncurry $ toscmVariant vused) stripped 130 | 131 | toscmCombinators :: [MLDef] -> CodeGen () 132 | toscmCombinators combs = do 133 | mapM_ (\(c, _, _) -> newMapCombinator c) combs 134 | mapM_ (\(c, asts, e) -> do 135 | c' <- getCombinator c 136 | as <- mapM (newMapLabel . fst) asts 137 | let define = "(define (" ++ unwords (c':as) ++ ")\n" 138 | body <- toscmExpr e 139 | emit $ define ++ body ++ ")\n\n" 140 | ) combs 141 | 142 | toscmProgram :: [DataSummary] -> MLProgram -> String 143 | toscmProgram datasummaries (ep, defs) = concat $ reverse $ (\(_,(_,_,_,_,code))->code) $ flip runState (0, [], [], [], []) $ do 144 | let vused = variantsUsedProg (ep, defs) 145 | toscmDataSummaries vused datasummaries 146 | toscmCombinators defs 147 | ep' <- toscmExpr ep 148 | emit ep' 149 | -------------------------------------------------------------------------------- /src/CompDefs.hs: -------------------------------------------------------------------------------- 1 | module CompDefs where 2 | import Control.Monad.Reader 3 | import System.CPUTime 4 | import ArgParser 5 | 6 | data CompState = CompState { 7 | dataDir :: String, 8 | argOptions :: Parse 9 | } 10 | 11 | type CompMon = ReaderT CompState IO 12 | 13 | runCompMon :: CompState -> CompMon a -> IO a 14 | runCompMon = flip runReaderT 15 | 16 | getDataDir :: CompMon String 17 | getDataDir = asks dataDir 18 | 19 | getArgOptions :: CompMon Parse 20 | getArgOptions = asks argOptions 21 | 22 | compLog :: String -> CompMon () 23 | compLog l = do 24 | opts <- getArgOptions 25 | if gotArg "verbose" opts 26 | then lift $ putStrLn l 27 | else return () 28 | 29 | time :: CompMon t -> CompMon (t, Double) 30 | time a = do 31 | start <- lift getCPUTime 32 | v <- a 33 | end <- lift getCPUTime 34 | let diff = fromIntegral (end - start) / (10^9) 35 | return (v, diff) 36 | -------------------------------------------------------------------------------- /src/HL/Defunctionalize.hs: -------------------------------------------------------------------------------- 1 | module HL.Defunctionalize (defunProgram) where 2 | import Control.Monad.State 3 | import Data.List(nub) 4 | 5 | import CompDefs 6 | import HLDefs 7 | import HL.HLOps 8 | import Typer.TypingDefs 9 | import Parser.MPCL(dummyStdCoord) 10 | 11 | type ApplysEnv = [(DataType, (String, [(String, [(String, DataType)], String)]))] 12 | type DefunEnv = (Int, [Combinator], ApplysEnv) 13 | type DefunState t = StateT DefunEnv CompMon t 14 | 15 | getUid :: DefunState String 16 | getUid = do 17 | (u, cs, env) <- get 18 | put (u+1, cs, env) 19 | return ('#':show u) 20 | 21 | getApply :: DataType -> DefunState String 22 | getApply t = do 23 | (u, cs, env) <- get 24 | case lookup t env of 25 | Just (l, _) -> return l 26 | Nothing -> do 27 | s <- getUid 28 | let combl = "_apply" ++ s 29 | (u', _, _) <- get 30 | put (u', cs, (t, (combl, [])):env) 31 | return combl 32 | addApply :: DataType -> [DataType] -> String -> DefunState String 33 | addApply t freets cmb = do 34 | (u, cs, env) <- get 35 | ((combl, brs), aps) <- 36 | case lookup t env of 37 | Just dat -> return (dat, filter ((t /=) . fst) env) 38 | Nothing -> do 39 | s <- getUid 40 | let combl = "_apply" ++ s 41 | return ((combl, []), env) 42 | sv <- getUid 43 | frees <- mapM (\ft -> fmap (\s -> ("_v" ++ s, ft)) getUid) freets 44 | let varl = "Closure_" ++ sv 45 | brs' = (varl, frees, cmb) : brs 46 | (u', _, _) <- get 47 | put (u', cs, (t, (combl, brs')) : aps) 48 | return varl 49 | 50 | addComb :: Combinator -> DefunState () 51 | addComb cmb = do 52 | (u, cs, env) <- get 53 | put (u, cmb:cs, env) 54 | newComb :: [(String, DataType)] -> HLExpr -> DefunState String 55 | newComb as e = do 56 | s <- getUid 57 | let combl = "_comb" ++ s 58 | (u, cs, env) <- get 59 | put (u, (combl, False, as, e):cs, env) 60 | return combl 61 | 62 | freeLabls :: HLExpr -> [(String, DataType)] 63 | freeLabls (_, _, ExprLiteral _) = [] 64 | freeLabls (_, _, ExprApp f a) = nub $ freeLabls f ++ freeLabls a --TODO: le coppie labl, datatype sono sempre uguali? 65 | freeLabls (_, t, ExprLabel l) = [(l, t)] 66 | freeLabls (_, _, ExprConstructor l es) = nub $ es >>= freeLabls 67 | freeLabls (_, _, ExprCombinator l es) = nub $ es >>= freeLabls 68 | freeLabls (_, _, ExprLambda l e) = filter ((l /=) . fst) $ freeLabls e 69 | freeLabls (_, _, ExprPut vs pses) = 70 | let lvss = map freeLabls vs 71 | lpses = map (\(ps, e) -> 72 | let le = freeLabls e 73 | in filter (\l -> not $ any (appearsPat (fst l)) ps) le 74 | ) pses 75 | in nub $ concat $ lvss ++ lpses 76 | freeLabls (_, _, ExprHint _ e) = freeLabls e 77 | 78 | defunExpr :: HLExpr -> DefunState HLExpr 79 | defunExpr e@(_, _, ExprLiteral _) = return e 80 | defunExpr e@(_, _, ExprLabel _) = return e 81 | defunExpr (c, t, ExprApp f@(_, tf, _) a) = do 82 | f' <- defunExpr f 83 | a' <- defunExpr a 84 | apl <- getApply tf 85 | return (c, t, ExprCombinator apl [f', a']) 86 | defunExpr (c, t, ExprConstructor l es) = do 87 | es' <- mapM defunExpr es 88 | return (c, t, ExprConstructor l es') 89 | defunExpr (c, t, ExprCombinator l es) = do 90 | es' <- mapM defunExpr es 91 | return (c, t, ExprCombinator l es') 92 | defunExpr le@(c, t@(DataTypeApp (DataTypeApp _ at) _), ExprLambda l e) = do 93 | let frees = freeLabls le 94 | e' <- defunExpr e 95 | myc <- newComb (frees ++ [(l, at)]) e' 96 | myv <- addApply t (map snd frees) myc 97 | let freerefs = map (\(myl, myt) -> (c, myt, ExprLabel myl)) frees 98 | return (c, t, ExprConstructor myv freerefs) 99 | defunExpr (c, t, ExprPut vs pses) = do 100 | vs' <- mapM defunExpr vs 101 | pses' <- mapM (\(ps, e) -> fmap (\e' -> (ps, e')) (defunExpr e)) pses 102 | return (c, t, ExprPut vs' pses') 103 | defunExpr (_, _, ExprHint _ e) = defunExpr e 104 | 105 | applysToComb :: DefunState () 106 | applysToComb = do 107 | (_, _, aps) <- get 108 | acmbs <- mapM applyToComb aps 109 | (n, cmbs, _) <- get 110 | put (n, acmbs ++ cmbs, []) 111 | where applyToComb (t@(DataTypeApp (DataTypeApp _ at) rt), (combl, vars)) = do 112 | s <- getUid 113 | let funl = "_fun" ++ s 114 | argl = "_arg" ++ s 115 | vartobranch (vname, ls, cmb) = 116 | ([(dummyStdCoord, t, Nothing, PatVariant vname (map (\(l,lt) -> (dummyStdCoord, lt, Just l, PatWildcard)) ls))], 117 | (dummyStdCoord, rt, ExprCombinator cmb (map (\(l, lt) -> (dummyStdCoord, lt, ExprLabel l)) (ls ++ [(argl, at)])))) 118 | return (combl, True, [(funl, t), (argl, at)], (dummyStdCoord, rt, ExprPut [(dummyStdCoord, t, ExprLabel funl)] (map vartobranch vars))) 119 | 120 | 121 | applysToDataSummary :: ApplysEnv -> [DataSummary] 122 | applysToDataSummary = map summarizeApply 123 | where summarizeApply (dt, (_, brs)) = (dt, map summarizeBranch brs) 124 | summarizeBranch (varl, datalabs, _) = (varl, map snd datalabs) 125 | 126 | defunProgram :: MonoProgram -> Int -> CompMon ([DataSummary], MonoProgram, Int) 127 | defunProgram (ep, defs) n = do 128 | ((datasummary, ep'), (n', combs, _)) <- runStateT defunmon (n, [], []) 129 | return (datasummary, (ep', combs), n') 130 | where defunmon = do 131 | ep' <- defunExpr ep 132 | mapM_ (\(l, il, as, e) -> do 133 | e' <- defunExpr e 134 | addComb (l, il, as, e') 135 | ) defs 136 | (_, _, aps) <- get 137 | let datasummary = applysToDataSummary aps 138 | applysToComb 139 | return (datasummary, ep') 140 | -------------------------------------------------------------------------------- /src/HL/HLOps.hs: -------------------------------------------------------------------------------- 1 | module HL.HLOps where 2 | import Data.Maybe (isJust, maybeToList, fromMaybe) 3 | import Typer.TypingDefs(DataType) 4 | import HLDefs 5 | 6 | 7 | patVarsInner PatWildcard = [] 8 | patVarsInner (PatLiteral _) = [] 9 | patVarsInner (PatVariant c ps) = ps >>= patVars 10 | 11 | patVars :: HLPattern -> [(String, DataType)] 12 | patVars (_, _, Nothing, ip) = patVarsInner ip 13 | patVars (_, t, Just l, ip) = (l, t) : patVarsInner ip 14 | 15 | appearsPat :: String -> HLPattern -> Bool 16 | appearsPat l pat = isJust $ lookup l (patVars pat) 17 | 18 | appears :: String -> HLExpr -> Int 19 | appears _ (_, _, ExprLiteral _) = 0 20 | appears l (_, _, ExprApp f a) = appears l f + appears l a 21 | appears l (_, _, ExprLabel l') = if l == l' then 1 else 0 22 | appears l (_, _, ExprConstructor c es) = sum (map (appears l) es) 23 | appears l (_, _, ExprCombinator c es) = (if c == l then 1 else 0) + sum (map (appears l) es) 24 | appears l (_, _, ExprLambda l' e) = if l == l' then 0 else appears l e 25 | appears l (_, _, ExprPut vs pses) = sum (map (appears l) vs) + 26 | sum (map (\(ps, e)->if any (appearsPat l) ps then 0 else appears l e) pses) 27 | 28 | appearsDefs :: String -> [Combinator] -> Int 29 | appearsDefs l defs = sum $ map (\(_,_,as,e) -> if elem l (map fst as) then 0 else appears l e) defs 30 | 31 | exprSize :: HLExpr -> Int 32 | exprSize (_, _, ExprLiteral _) = 1 33 | exprSize (_, _, ExprApp f a) = exprSize f + exprSize a --TODO: +1? 34 | exprSize (_, _, ExprLabel _) = 1 35 | exprSize (_, _, ExprConstructor _ es) = 1 + sum (map exprSize es) 36 | exprSize (_, _, ExprCombinator _ es) = 1 + sum (map exprSize es) 37 | exprSize (_, _, ExprLambda p e) = 1 + exprSize e --TODO patSize p? 38 | exprSize (_, _, ExprPut vs pses) = length pses + sum (map exprSize vs) + sum (map (exprSize . snd) pses) --TODO patsize pses? 39 | exprSize (_, _, ExprHint _ e) = exprSize e 40 | 41 | programSize :: MonoProgram -> Int 42 | programSize (ep, defs) = exprSize ep + sum (map (exprSize . (\(_,_,_,a)->a)) defs) 43 | 44 | inline :: [(String, HLExpr)] -> HLExpr -> HLExpr 45 | inline binds e@(_, _, ExprLiteral _) = e 46 | inline binds e@(c, _, ExprLabel l') = fromMaybe e (lookup l' binds) 47 | inline binds (c, t, ExprApp f a) = (c, t, ExprApp (inline binds f) (inline binds a)) 48 | inline binds (c, t, ExprConstructor cn es) = (c, t, ExprConstructor cn (map (inline binds) es)) 49 | inline binds (c, t, ExprCombinator cn es) = (c, t, ExprCombinator cn (map (inline binds) es)) 50 | inline binds (c, t, ExprLambda l' le) = 51 | let newbinds = filter ((l' /=) . fst) binds 52 | in (c, t, ExprLambda l' (inline newbinds le)) 53 | inline binds (c, t, ExprPut vs pses) = (c, t, ExprPut (map (inline binds) vs) 54 | (map (\(p, e)-> 55 | --TODO: questo è un fix per evitare shadowing, significa che bisogna ristrutturare l'optimizer per creare nuove variabili uniche 56 | let newbinds = filter (\(sl, _) -> not $ any (appearsPat sl) p) binds in 57 | (p, inline newbinds e)) pses)) 58 | 59 | -------------------------------------------------------------------------------- /src/HL/HLOptimize.hs: -------------------------------------------------------------------------------- 1 | module HL.HLOptimize where 2 | import Data.Char (ord, chr) 3 | import Data.List (sortBy) 4 | 5 | import Parser.MPCL(StdCoord) 6 | import HLDefs 7 | import HL.HLOps 8 | import Typer.TypingDefs 9 | 10 | inlineHeuristic :: HLExpr -> Int -> Bool 11 | inlineHeuristic e timesappears = 12 | let size = exprSize e 13 | addedSize = size*(timesappears - 1) 14 | in size < 8 || addedSize < size 15 | 16 | isSimple :: HLExpr -> Bool 17 | isSimple (_, _, ExprLiteral _) = True 18 | isSimple (_, _, ExprLabel _) = True 19 | isSimple (_, _, ExprConstructor _ es) = all isSimple es 20 | isSimple (_, _, ExprLambda _ _) = True 21 | isSimple (_, _, ExprHint _ e) = isSimple e 22 | isSimple _ = False 23 | 24 | inlineComb :: Combinator -> HLExpr -> HLExpr 25 | inlineComb cmb e@(_, _, ExprLiteral _) = e 26 | inlineComb cmb e@(_, _, ExprLabel l') = e 27 | inlineComb cmb (c, t, ExprApp f a) = (c, t, ExprApp (inlineComb cmb f) (inlineComb cmb a)) 28 | inlineComb cmb (c, t, ExprConstructor cn es) = (c, t, ExprConstructor cn (map (inlineComb cmb) es)) 29 | inlineComb cmb@(cn, _, as, e) (c, t, ExprCombinator mcn es) 30 | | cn == mcn = 31 | let es' = map (inlineComb cmb) es 32 | binds = zip (map fst as) es' 33 | newe = inline binds e 34 | in newe 35 | | otherwise = (c, t, ExprCombinator mcn (map (inlineComb cmb) es)) 36 | inlineComb cmb e@(c, t, ExprLambda l' le) = (c, t, ExprLambda l' (inlineComb cmb le)) 37 | inlineComb cmb (c, t, ExprPut vs pses) = (c, t, ExprPut (map (inlineComb cmb) vs) 38 | (map (\(p, e)-> (p, inlineComb cmb e)) pses)) 39 | inlineComb cmb (_, _, ExprHint _ e) = inlineComb cmb e 40 | 41 | inlineDefs :: Combinator -> [Combinator] -> [Combinator] 42 | inlineDefs cmb [] = [] 43 | inlineDefs cmb ((ld, il, as, e):defs') = (ld, il, as, inlineComb cmb e):inlineDefs cmb defs' 44 | 45 | sortInlines :: MonoProgram -> MonoProgram 46 | sortInlines (ep, defs) = 47 | let weighted = map (\c@(_,il,_,e)->((il,exprSize e), c)) defs 48 | in (ep, map snd $ sortBy (\(w,_) (w', _) -> compare w' w) weighted) 49 | 50 | -- (ep, sortBy (\(l,il,as,e) (l',il',as',e')-> 51 | -- compare (il', (appears l ep + appearsDefs l defs)) (il, (appears l' ep + appearsDefs l' defs)) 52 | -- ) defs) 53 | 54 | -- TODO: non considera l'esplosione della dimensione a causa di argomenti utilizzati più volte 55 | inlineProgram :: MonoProgram -> MonoProgram 56 | inlineProgram prog = loop [] (sortInlines prog) 57 | where 58 | loop procd (ep, []) = (ep, procd) 59 | loop procd (ep, cmb@(l, il, as, e):defs) 60 | | (appears l ep + appearsDefs l (procd ++ defs)) == 0 61 | = loop procd (ep, defs) 62 | | appears l e == 0 && inlineHeuristic e (appears l ep + appearsDefs l (procd++defs)) 63 | = loop (inlineDefs cmb procd) (inlineComb cmb ep, inlineDefs cmb defs) 64 | | otherwise = loop (cmb:procd) (ep, defs) 65 | 66 | data SieveRes 67 | = Always [(String, HLExpr)] 68 | | Maybe 69 | | Never 70 | 71 | concatRess :: [SieveRes] -> SieveRes 72 | concatRess [] = Always [] 73 | concatRess (Never:_) = Never 74 | concatRess (Maybe:_) = Maybe 75 | concatRess (Always bs:rs) = case concatRess rs of 76 | Never -> Never 77 | Maybe -> Maybe 78 | Always bs' -> Always (bs ++ bs') 79 | 80 | 81 | sievePatternInner :: HLPatternData -> HLExpr -> SieveRes 82 | sievePatternInner PatWildcard _ = Always [] 83 | sievePatternInner (PatLiteral plit) (_, _, ExprLiteral elit) = 84 | if plit == elit then Always [] else Never 85 | sievePatternInner (PatVariant pvar ps) (_, _, ExprConstructor evar es) = 86 | if pvar == evar 87 | then sievePatternList ps es 88 | else Never 89 | sievePatternInner p e = Maybe 90 | 91 | sievePattern :: HLPattern -> HLExpr -> SieveRes 92 | sievePattern (_, _, Nothing, patdata) e = sievePatternInner patdata e 93 | sievePattern (_, _, Just l, patdata) e = 94 | let inner = sievePatternInner patdata e 95 | in concatRess [Always[(l, e)],inner] 96 | 97 | sievePatternList :: [HLPattern] -> [HLExpr] -> SieveRes 98 | sievePatternList ps es = concatRess (zipWith sievePattern ps es) 99 | 100 | sievePatterns :: [HLExpr] -> [([HLPattern], HLExpr)] -> [([HLPattern], HLExpr)] 101 | sievePatterns v = reverse . loop [] 102 | where loop pses' [] = pses' 103 | loop pses' ((p, e):pses) = 104 | case sievePatternList p v of 105 | Always _ -> (p, e):pses' 106 | Maybe -> loop ((p, e):pses') pses 107 | Never -> loop pses' pses 108 | 109 | optimizeBI :: StdCoord -> DataType -> String -> [HLExpr] -> HLExpr 110 | optimizeBI c t "spinnaker_addInt" [(_, _, ExprLiteral (LitInteger i0)),(_, _, ExprLiteral (LitInteger i1))] = (c, t, ExprLiteral (LitInteger (i0+i1))) 111 | optimizeBI c t "spinnaker_subInt" [(_, _, ExprLiteral (LitInteger i0)),(_, _, ExprLiteral (LitInteger i1))] = (c, t, ExprLiteral (LitInteger (i0-i1))) 112 | optimizeBI c t "spinnaker_mulInt" [(_, _, ExprLiteral (LitInteger i0)),(_, _, ExprLiteral (LitInteger i1))] = (c, t, ExprLiteral (LitInteger (i0*i1))) 113 | optimizeBI c t "spinnaker_divInt" [(_, _, ExprLiteral (LitInteger i0)),(_, _, ExprLiteral (LitInteger i1))] = (c, t, ExprLiteral (LitInteger (quot i0 i1))) 114 | optimizeBI c t "spinnaker_remInt" [(_, _, ExprLiteral (LitInteger i0)),(_, _, ExprLiteral (LitInteger i1))] = (c, t, ExprLiteral (LitInteger (rem i0 i1))) 115 | optimizeBI c t "spinnaker_equInt" [(_, _, ExprLiteral (LitInteger i0)),(_, _, ExprLiteral (LitInteger i1))] = (c, t, ExprConstructor (if i0 == i1 then "True" else "False") []) 116 | optimizeBI c t "spinnaker_neqInt" [(_, _, ExprLiteral (LitInteger i0)),(_, _, ExprLiteral (LitInteger i1))] = (c, t, ExprConstructor (if i0 /= i1 then "True" else "False") []) 117 | optimizeBI c t "spinnaker_leqInt" [(_, _, ExprLiteral (LitInteger i0)),(_, _, ExprLiteral (LitInteger i1))] = (c, t, ExprConstructor (if i0 <= i1 then "True" else "False") []) 118 | optimizeBI c t "spinnaker_greInt" [(_, _, ExprLiteral (LitInteger i0)),(_, _, ExprLiteral (LitInteger i1))] = (c, t, ExprConstructor (if i0 > i1 then "True" else "False") []) 119 | 120 | optimizeBI c t "spinnaker_addFlt" [(_, _, ExprLiteral (LitFloating f0)),(_, _, ExprLiteral (LitFloating f1))] = (c, t, ExprLiteral (LitFloating (f0+f1))) 121 | optimizeBI c t "spinnaker_subFlt" [(_, _, ExprLiteral (LitFloating f0)),(_, _, ExprLiteral (LitFloating f1))] = (c, t, ExprLiteral (LitFloating (f0-f1))) 122 | optimizeBI c t "spinnaker_mulFlt" [(_, _, ExprLiteral (LitFloating f0)),(_, _, ExprLiteral (LitFloating f1))] = (c, t, ExprLiteral (LitFloating (f0*f1))) 123 | optimizeBI c t "spinnaker_divFlt" [(_, _, ExprLiteral (LitFloating f0)),(_, _, ExprLiteral (LitFloating f1))] = (c, t, ExprLiteral (LitFloating (f0/f1))) 124 | optimizeBI c t "spinnaker_equFlt" [(_, _, ExprLiteral (LitFloating f0)),(_, _, ExprLiteral (LitFloating f1))] = (c, t, ExprConstructor (if f0 == f1 then "True" else "False") [] ) 125 | optimizeBI c t "spinnaker_neqFlt" [(_, _, ExprLiteral (LitFloating f0)),(_, _, ExprLiteral (LitFloating f1))] = (c, t, ExprConstructor (if f0 /= f1 then "True" else "False") [] ) 126 | optimizeBI c t "spinnaker_leqFlt" [(_, _, ExprLiteral (LitFloating f0)),(_, _, ExprLiteral (LitFloating f1))] = (c, t, ExprConstructor (if f0 <= f1 then "True" else "False") [] ) 127 | optimizeBI c t "spinnaker_greFlt" [(_, _, ExprLiteral (LitFloating f0)),(_, _, ExprLiteral (LitFloating f1))] = (c, t, ExprConstructor (if f0 > f1 then "True" else "False") [] ) 128 | optimizeBI c t "spinnaker_convItoF" [(_, _, ExprLiteral (LitInteger i))] = (c, t, ExprLiteral (LitFloating (fromIntegral i))) 129 | optimizeBI c t "spinnaker_floorFlt" [(_, _, ExprLiteral (LitFloating f))] = (c, t, ExprLiteral (LitInteger (floor f))) 130 | 131 | optimizeBI c t "spinnaker_convItoC" [(_, _, ExprLiteral (LitInteger i))] = (c, t, ExprLiteral (LitCharacter (chr i))) 132 | optimizeBI c t "spinnaker_convCtoI" [(_, _, ExprLiteral (LitCharacter ch))] = (c, t, ExprLiteral (LitInteger (ord ch))) 133 | 134 | optimizeBI c t "spinnaker_andBool" [(_, _, ExprConstructor b0 []),(_, _, ExprConstructor b1 [])] = 135 | let b = case (b0, b1) of 136 | ("True", "True") -> "True" 137 | ("True", "False") -> "False" 138 | ("False", "True") -> "False" 139 | ("False", "False") -> "False" 140 | in (c, t, ExprConstructor b []) 141 | optimizeBI c t "spinnaker_orBool" [(_, _, ExprConstructor b0 []),(_, _, ExprConstructor b1 [])] = 142 | let b = case (b0, b1) of 143 | ("True", "True") -> "True" 144 | ("True", "False") -> "True" 145 | ("False", "True") -> "True" 146 | ("False", "False") -> "False" 147 | in (c, t, ExprConstructor b []) 148 | optimizeBI c t "spinnaker_notBool" [(_, _, ExprConstructor b0 [])] = 149 | let b = case b0 of 150 | "True" -> "False" 151 | "False" -> "True" 152 | in (c, t, ExprConstructor b []) 153 | 154 | optimizeBI c t l es = (c, t, ExprCombinator l es) 155 | 156 | optimizeExpr :: HLExpr -> HLExpr 157 | optimizeExpr e@(_, _, ExprLiteral _) = e 158 | optimizeExpr (c, t, ExprApp f a@(_, at, _)) = 159 | let f' = optimizeExpr f 160 | a' = optimizeExpr a 161 | in case f' of 162 | (_, _, ExprLambda l inner) -> optimizeExpr (c, t, ExprPut [a'] [([(c, at, Just l, PatWildcard)], inner)]) 163 | _ -> (c, t, ExprApp f' a') 164 | optimizeExpr e@(_, _, ExprLabel _) = e 165 | optimizeExpr (c, t, ExprConstructor l es) = (c, t, ExprConstructor l (map optimizeExpr es)) 166 | optimizeExpr (c, t, ExprCombinator l es) = optimizeBI c t l (map optimizeExpr es) 167 | optimizeExpr (c, t, ExprPut vals pses) = --TODO: putofput 168 | let vals' = map optimizeExpr vals 169 | pses' = sievePatterns vals' pses 170 | pses'' = map (\(p,e)->(p,optimizeExpr e)) pses' 171 | in case pses'' of 172 | [(p, e)] -> case sievePatternList p vals' of 173 | Always bs -> 174 | if all (\(ml,me)->let numappears = appears ml e 175 | -- TODO: la condizione con isSimple potrebbe impedire alcune ottimizzazioni 176 | in (isSimple me || numappears <= 1) && inlineHeuristic me numappears) bs 177 | then optimizeExpr $ 178 | foldl (\me (l,e')->inline [(l, e')] me) e bs 179 | else (c, t, ExprPut (map snd bs) [(map (\(ml, (mc, mt, _))-> (mc, mt, Just ml, PatWildcard)) bs, e)]) 180 | _ -> (c, t, ExprPut vals' pses'') 181 | _ -> (c, t, ExprPut vals' pses'') 182 | --error $ "OPTIMIZED val " ++ show val' ++ "\npses " ++ show pses ++ "\npses'" ++ show pses' 183 | optimizeExpr (c, t, ExprLambda pat e) = (c, t, ExprLambda pat (optimizeExpr e)) 184 | optimizeExpr (_, _, ExprHint _ e) = optimizeExpr e 185 | 186 | liftComb :: Combinator -> (Combinator, Combinator) --lambda e lifted 187 | liftComb (l, il, as, e) = 188 | let (clts, e'@(innerc, innert, _)) = liftLambda e 189 | newlab = l ++ "lifted" 190 | newas = map (\(myl, myat) -> (myl ++ "lifted", myat)) as 191 | newclts = map (\(myc, (myl, myat)) -> (myc, (myl ++ "lifted", myat))) clts 192 | newinner = (innerc, innert, ExprCombinator newlab (map (\(al, at) -> (innerc, at, ExprLabel al)) $ newas ++ map snd newclts)) 193 | newlam = foldr (\(myc, (myl, myat)) mye@(_, myrt, _) -> (myc, buildFunType myat myrt, ExprLambda myl mye)) newinner newclts 194 | in ((l, True, newas, newlam), (newlab, il, as ++ map snd clts, e')) 195 | where --liftLambda :: HLExpr -> ([(StdCoord, (String, DataType))], HLExpr) 196 | liftLambda (c, DataTypeApp (DataTypeApp _ a) _, ExprLambda myl le) = 197 | let (clts, le') = liftLambda le in ((c, (myl, a)):clts, le') 198 | liftLambda (_, _, ExprHint _ mye) = liftLambda mye 199 | liftLambda mye = ([], mye) 200 | liftCombs :: MonoProgram -> MonoProgram 201 | liftCombs (ep, defs) = 202 | let (lams, lifts) = unzip $ map liftComb defs 203 | in (foldr inlineComb ep lams, foldr inlineDefs lifts lams) 204 | 205 | optimizeDefExprs :: MonoProgram -> MonoProgram 206 | optimizeDefExprs (ep, defs) = (optimizeExpr ep, 207 | map (\(l,il,as,e)->(l,il,as,optimizeExpr e)) defs) 208 | 209 | optimizeProgram :: [MonoProgram -> MonoProgram] -> MonoProgram -> MonoProgram 210 | optimizeProgram passes p = foldl (flip ($)) p passes 211 | -------------------------------------------------------------------------------- /src/HL/Monomorphizer.hs: -------------------------------------------------------------------------------- 1 | module HL.Monomorphizer (monomorphizeProgram) where 2 | import qualified Data.Map as Map 3 | import qualified Data.Set as Set 4 | import Data.List(find, partition) 5 | import Data.Maybe(isJust, catMaybes) 6 | import Control.Monad(join) 7 | import Control.Monad.State 8 | 9 | import CompDefs 10 | import HLDefs 11 | import Parser.MPCL(dummyStdCoord) 12 | import Typer.TypingDefs 13 | import Typer.MGUs 14 | import Typer.TypeTyper(substApplyExpr) 15 | 16 | type Instances = [(DataType, String)] 17 | type Generators = [(DataType, HLExpr)] 18 | 19 | type MonoEnv = (Int, [Combinator], Map.Map String (Instances, Generators)) 20 | type MonoState t = StateT MonoEnv CompMon t 21 | 22 | monoLog :: String -> MonoState () 23 | monoLog = lift . compLog 24 | 25 | addDef :: String -> HLExpr -> MonoState () 26 | addDef l e = do 27 | (u, defs, env) <- get 28 | put (u, (l, False, [], e) : defs, env) 29 | getInsts :: String -> MonoState Instances 30 | getInsts l = do 31 | (_, _, e) <- get 32 | let Just (is, _) = Map.lookup l e 33 | return is 34 | addInst :: String -> DataType -> String -> MonoState () 35 | addInst l t l' = do 36 | (u, defs, e) <- get 37 | let Just (is, gs) = Map.lookup l e 38 | e' = Map.insert l ((t, l'):is, gs) e 39 | put (u, defs, e') 40 | getGenerators :: String -> MonoState Generators 41 | getGenerators l = do 42 | (_, _, e) <- get 43 | let Just (_, gs) = Map.lookup l e 44 | return gs 45 | newMonoSuffix :: MonoState String 46 | newMonoSuffix = do 47 | (u, defs, e) <- get 48 | put (u+1, defs, e) 49 | return ('#':show u) 50 | isGlobal :: String -> MonoState Bool 51 | isGlobal l = do 52 | (_, _, e) <- get 53 | return $ isJust $ Map.lookup l e 54 | 55 | findGenerator :: String -> DataType -> Generators -> Maybe HLExpr 56 | findGenerator l t = getMostSpecific 57 | where reduceMostSpecifics :: [(DataType, HLExpr)]->[(DataType, HLExpr)]->[(DataType, HLExpr)] 58 | reduceMostSpecifics sps [] = sps 59 | reduceMostSpecifics sps ((te, e):tses') 60 | | any (\(te', _) -> isJust (match dummyStdCoord te te')) (sps ++ tses') 61 | = reduceMostSpecifics sps tses' 62 | | otherwise = reduceMostSpecifics ((te, e):sps) tses' 63 | getMostSpecific tses = 64 | let matching = catMaybes [match dummyStdCoord te t >> Just (te, e) | (te, e) <- tses] 65 | specifics = reduceMostSpecifics [] matching 66 | in case specifics of 67 | [] -> error $ "No matching generators of: " ++ show l ++ " with type: " ++ show t ++ "\nAvailable gens: " ++ show tses 68 | [(te, e)] -> do 69 | s <- match dummyStdCoord te t 70 | Just (substApplyExpr s e) 71 | xs -> error $ "Cannot find most specific instance of: " ++ show l ++ " with type: " ++ show t ++ "\n Possible instances are: " ++ show (map fst xs) 72 | 73 | generateInstance :: String -> DataType -> MonoState String 74 | generateInstance l t = do 75 | gs <- getGenerators l 76 | let Just e = findGenerator l t gs 77 | s <- newMonoSuffix 78 | addInst l t (l++s) 79 | e' <- monomorphize s e 80 | addDef (l++s) e' 81 | return (l++s) 82 | 83 | findInstance :: String -> DataType -> MonoState String 84 | findInstance l t = if (not . null) (freetyvars t) then error "WHAT: there are free type variables in instance search" else do 85 | monoLog $ "Looking for instance of: " ++ show l ++ " with type: " ++ show t 86 | is <- getInsts l 87 | case find ((==) t . fst) is of 88 | Just (_, l') -> do 89 | monoLog $ "Instance found: " ++ show l' 90 | return l' 91 | Nothing -> do 92 | monoLog "Instance not found, generating..." 93 | generateInstance l t 94 | 95 | monomorphizePatInner :: String -> HLPatternData -> MonoState HLPatternData 96 | monomorphizePatInner _ PatWildcard = return PatWildcard 97 | monomorphizePatInner _ p@(PatLiteral _) = return p 98 | monomorphizePatInner s (PatVariant c ps) = do 99 | ps' <- mapM (monomorphizePat s) ps 100 | return (PatVariant c ps') 101 | --TODO: In futuro trova i data giusti 102 | 103 | monomorphizePat :: String -> HLPattern -> MonoState HLPattern 104 | monomorphizePat s (c, t, ml, i) = do 105 | i' <- monomorphizePatInner s i 106 | return (c, t, fmap (++s) ml, i') 107 | 108 | monomorphizeInner :: DataType -> String -> HLExprData -> MonoState HLExprData 109 | monomorphizeInner _ _ e@(ExprLiteral _) = return e 110 | monomorphizeInner _ s (ExprApp f a) = do 111 | f' <- monomorphize s f 112 | a' <- monomorphize s a 113 | return (ExprApp f' a') 114 | monomorphizeInner t s (ExprLabel l) = do 115 | isglob <- isGlobal l 116 | if isglob then do 117 | l' <- findInstance l t 118 | return (ExprCombinator l' []) 119 | else return (ExprLabel (l++s)) 120 | monomorphizeInner _ s (ExprConstructor c es) = do 121 | es' <- mapM (monomorphize s) es 122 | return (ExprConstructor c es') 123 | monomorphizeInner _ s (ExprCombinator l es) = do 124 | es' <- mapM (monomorphize s) es 125 | return (ExprCombinator l es') 126 | monomorphizeInner _ s (ExprLambda l e) = do 127 | e' <- monomorphize s e 128 | return (ExprLambda (l++s) e') 129 | monomorphizeInner _ s (ExprPut vs pses) = do 130 | vs' <- mapM (monomorphize s) vs 131 | pses' <- mapM (\(ps, e) -> do {ps' <- mapM (monomorphizePat s) ps; e' <- monomorphize s e; return (ps', e')}) pses 132 | return (ExprPut vs' pses') 133 | monomorphizeInner t s (ExprHint _ e) = do 134 | e' <- monomorphize s e 135 | return (ExprHint t e') 136 | 137 | monomorphize :: String -> HLExpr -> MonoState HLExpr 138 | monomorphize s e@(c, t, _) = do 139 | let monoSubst = Map.fromList $ map (\q -> (q, buildTupType [])) $ Set.toList (freetyvars t) 140 | (_, t', ed) = substApplyExpr monoSubst e 141 | ed' <- monomorphizeInner t' s ed 142 | return (c, t', ed') 143 | 144 | myListMerge :: Eq k => [(k,v)]->[(k,[v])] 145 | myListMerge [] = [] 146 | myListMerge ((k,v):kvs) = 147 | let (isk, isntk) = partition ((k==) . fst) kvs 148 | in (k, v:map snd isk):myListMerge isntk 149 | 150 | monomorphizeProgram :: (HLExpr, BlockProgram) -> CompMon MonoProgram 151 | monomorphizeProgram (entryPoint, BlockProgram datagroups extdefs reldefs valgroups instdefs) = 152 | let 153 | valbinds = join valgroups 154 | valVtables = map (\(ValDef _ l _ _ e@(_, t, _))->(l, [(t, e)])) valbinds 155 | instsList = myListMerge $ map (\(InstDef _ (Qual _ (Pred l _)) cles) -> (l, cles)) instdefs 156 | instsListTEs = map (\(il, cless) -> (il, map (\(c, l, e@(_, t, _))->(l, (t, e))) (concat cless))) instsList 157 | instsVtables :: [(String, [(DataType, HLExpr)])] 158 | instsVtables = concatMap (myListMerge . snd) instsListTEs 159 | 160 | env = Map.fromList $ map (\(l, tses) -> (l, ([], tses))) (valVtables ++ instsVtables) 161 | in do 162 | (entryPoint', (_, defs, _)) <- runStateT (monomorphize "" entryPoint) (0, [], env) 163 | return (entryPoint', defs) 164 | --TODO: così main può avere soltanto il tipo: (), il che non viene controllato nel typer 165 | -------------------------------------------------------------------------------- /src/HLDefs.hs: -------------------------------------------------------------------------------- 1 | module HLDefs where 2 | import Parser.MPCL(StdCoord) 3 | import Typer.TypingDefs 4 | 5 | data Literal 6 | = LitInteger Int 7 | | LitFloating Float 8 | | LitCharacter Char 9 | deriving (Show, Eq) 10 | 11 | data HLPatternData 12 | = PatWildcard 13 | | PatLiteral Literal --Il literal rappresentato 14 | | PatVariant String [HLPattern] --Nome della variante, lista di argomenti di questo 15 | deriving Show 16 | type HLPattern = (StdCoord, DataType, Maybe String, HLPatternData) -- coordinate, eventuale assegnazione del valore (tipo haskell labl@pat) e pattern vero e proprio 17 | 18 | data HLExprData 19 | = ExprLiteral Literal --Valore letterale 20 | | ExprApp HLExpr HLExpr --Funzione, argomento 21 | | ExprLabel String --Riferimento a label 22 | | ExprConstructor String [HLExpr] -- Riferimento a una variante e argomenti "applicati" 23 | | ExprCombinator String [HLExpr] -- Riferimento al combinatore e argomenti 24 | | ExprLambda String HLExpr --Argomento e valore interno 25 | | ExprPut [HLExpr] [([HLPattern], HLExpr)] --Valore da controllare, lista di pattern e i branch corrispondenti 26 | | ExprHint DataType HLExpr --type hint di un'espressione 27 | deriving Show 28 | type HLExpr = (StdCoord, DataType, HLExprData) 29 | 30 | data HLValDef 31 | = ValDef StdCoord String (Maybe (Qual DataType)) [Pred] HLExpr -- Cordinate della definizione, nome del valore, type hint, espressione 32 | deriving Show 33 | 34 | data HLExtDef = ExtDef StdCoord String String [DataType] DataType 35 | deriving Show 36 | 37 | data HLDataVariant 38 | = DataVariant StdCoord String [(StdCoord, DataType)] 39 | deriving Show 40 | 41 | data HLDataDef 42 | = DataDef StdCoord String [(String, TyQuant)] [HLDataVariant] 43 | deriving Show 44 | 45 | data HLRelDef 46 | = RelDef StdCoord String [TyQuant] [Pred] [(StdCoord, String, Qual DataType)] 47 | deriving Show 48 | 49 | data HLInstDef 50 | = InstDef StdCoord (Qual Pred) [(StdCoord, String, HLExpr)] 51 | deriving Show 52 | 53 | data BlockProgram = BlockProgram [[HLDataDef]] [HLRelDef] [HLExtDef] [[HLValDef]] [HLInstDef] 54 | 55 | type DataSummary = (DataType, [(String, [DataType])]) 56 | blockProgramToDataSummary :: BlockProgram -> [DataSummary] 57 | blockProgramToDataSummary (BlockProgram ddefgroups _ _ _ _) = do 58 | ddefs <- ddefgroups 59 | flip map ddefs $ \(DataDef _ l qs variants) -> 60 | let datakind = dataQsToKind qs 61 | datatype = foldl DataTypeApp (DataTypeName l datakind) $ map (DataQuant . snd) qs 62 | variantinfo = map (\(DataVariant _ vl csds) -> (vl, map snd csds)) variants 63 | in (datatype, variantinfo) 64 | 65 | type Inline = Bool 66 | type Combinator = (String, Inline, [(String, DataType)], HLExpr) 67 | type MonoProgram = (HLExpr, [Combinator]) 68 | -------------------------------------------------------------------------------- /src/ML/HLtoML.hs: -------------------------------------------------------------------------------- 1 | module ML.HLtoML(hltoml) where 2 | import Parser.MPCL(StdCoord) 3 | import CompDefs 4 | import Typer.TypingDefs 5 | import HLDefs 6 | import HL.HLOps 7 | import MLDefs 8 | import ML.MLOps 9 | import Control.Monad.State 10 | 11 | type Branches = [([(String, HLPattern)], MLExpr)] 12 | pushvalassigns :: Branches -> Branches 13 | pushvalassigns = map (uncurry $ pushvals []) 14 | where pushvals ps [] e = (ps, e) 15 | pushvals ps ((l,(c, pt, ml, pd)):lps) e = 16 | let ps' = case pd of 17 | PatWildcard -> ps 18 | _ -> (l, (c, pt, Nothing, pd)) : ps 19 | e' = case ml of 20 | Nothing -> e 21 | Just patlab -> mlsubst patlab (c, pt, MLLabel l) e 22 | in pushvals ps' lps e' 23 | 24 | makeJoins :: Branches -> MLState (Branches, [(String, [(String, DataType)], MLExpr)]) 25 | makeJoins = fmap unzip . mapM makeJoinsSingle 26 | where makeJoinsSingle (lps, e@(c, t, _)) = let ltys = filter ((/= 0) . flip mlappears e . fst) $ lpatsVars lps in do 27 | joinLab <- newlab 28 | return ((lps, (c, t, MLJoin joinLab $ map (\(argl, argt)->(c, argt, MLLabel argl)) ltys)), (joinLab, ltys, e)) 29 | lpatsVars lps = lps >>= (patVars . snd) 30 | 31 | chooseTestHeuristic :: Branches -> (StdCoord, DataType, String) 32 | chooseTestHeuristic ((lps,_):lpsses) = 33 | let occurences = map (\(l, p) -> ((l,p), length $ filter (any ((l ==) . fst) . fst) lpsses)) lps 34 | (lab, (c, pt, _, _)) = fst $ maximumOn snd occurences 35 | in (c, pt, lab) 36 | where maximumOn f = foldr1 (\a b -> if f a < f b then b else a) 37 | 38 | patCompatibility :: (MLPattern, [(String, DataType)]) -> HLPattern -> Maybe [(String, HLPattern)] 39 | patCompatibility (MLPLiteral l, []) (_,_,_, PatLiteral l') = if l == l' then Just [] else Nothing 40 | patCompatibility (MLPVariant v, ls) (_,_,_, PatVariant v' ps) = if v == v' then Just $ zipWith (\myl myp -> (fst myl, myp)) ls ps else Nothing 41 | 42 | pattomlpat :: HLPattern -> MLState (MLPattern, [(String, DataType)]) 43 | pattomlpat (c, pt, _, PatLiteral lit) = return (MLPLiteral lit, []) 44 | pattomlpat (c, pt, _, PatVariant var subps) = do 45 | ls <- mapM (\(_,mypt,_,_)-> (\pl -> (pl, mypt)) <$> newlab) subps 46 | return (MLPVariant var, ls) 47 | 48 | appendTest :: HLPattern -> ([(String, HLPattern)], MLExpr) -> [((MLPattern, [(String, DataType)]), Branches)] -> MLState [((MLPattern, [(String, DataType)]), Branches)] 49 | appendTest hlpat (lps, e) [] = do 50 | pat <- pattomlpat hlpat 51 | let Just subtests = patCompatibility pat hlpat 52 | in return [(pat, [(subtests ++ lps, e)])] 53 | appendTest hlpat branch@(lps, e) ((p, bs) : pbs) = 54 | case patCompatibility p hlpat of 55 | Nothing -> ((p, bs) :) <$> appendTest hlpat branch pbs 56 | Just subtests -> return $ (p, bs ++ [(subtests ++ lps, e)]) : pbs 57 | 58 | splitTests :: String -> Branches -> MLState ([((MLPattern, [(String, DataType)]), Branches)], Branches) 59 | splitTests lab = inner [] [] 60 | where inner pbs notest [] = return (pbs, notest) 61 | inner pbs notest (lpse@(lps, e):lpsses) = 62 | case lookup lab lps of 63 | Nothing -> inner pbs (notest++[lpse]) lpsses 64 | Just hlpat -> do 65 | let lps' = filter ((lab /=) . fst) lps 66 | pbs' <- appendTest hlpat (lps', e) pbs 67 | inner pbs' notest lpsses 68 | 69 | treatput :: StdCoord -> DataType -> Branches -> MLState MLExpr 70 | treatput c t [] = return (c, t, MLError c "Pattern matching failure") 71 | treatput c t lpsses = do 72 | let lpsses' = pushvalassigns lpsses 73 | if null $ fst $ head lpsses' then return $ snd $ head lpsses' 74 | else do 75 | let (testc, testty, testlab) = chooseTestHeuristic lpsses' 76 | lift $ compLog $ show testc ++ " TESTING LABEL:" ++ show testlab 77 | (test, notest) <- splitTests testlab lpsses' 78 | (notest', joins) <- makeJoins notest 79 | mltest <- mapM (\((pat, projs), branches) -> do 80 | res <- treatput c t (branches ++ notest') 81 | let res_projs = case pat of 82 | MLPLiteral _ -> res 83 | MLPVariant vname -> foldr (\(projn, (projl, projt)) e -> (c, t, MLLet projl (c, projt, MLProj (c, testty, MLLabel testlab) vname projn) e) ) res 84 | $ filter (\(_, (projl, _)) -> mlappears projl res /= 0) 85 | $ zip [0..] projs 86 | return (pat, res_projs) 87 | ) test 88 | mlnotest <- treatput testc t notest' 89 | return $ foldr (\(joinl, joinargs, joine) e -> (c, t, MLLetJoin joinl joinargs joine e)) 90 | (c, t, MLTest (testc, testty, MLLabel testlab) mltest mlnotest) 91 | joins 92 | 93 | exprtomlexpr :: HLExpr -> MLState MLExpr 94 | exprtomlexpr (c, t, ExprLiteral l) = return (c, t,MLLiteral l) 95 | exprtomlexpr (c, t, ExprLabel l) = return (c, t, MLLabel l) 96 | exprtomlexpr (c, t, ExprConstructor v es) = do 97 | mles <- mapM exprtomlexpr es 98 | return (c, t, MLConstructor v mles) 99 | exprtomlexpr (c, t, ExprCombinator cmb es) = do 100 | mles <- mapM exprtomlexpr es 101 | return (c, t, MLCombinator cmb mles) 102 | exprtomlexpr (c, t, ExprPut es psses) = do 103 | mles <- mapM exprtomlexpr es 104 | ls <- mapM (const newlab) [1..length es] 105 | mergedlps_etoml <- mapM (\(ps, e) -> do 106 | mle <- exprtomlexpr e --TODO: metti label qui per evitare duplicazione, oppure crea combinatori nello step precedente 107 | return (zip ls ps, mle)) psses 108 | mlmatch <- treatput c t mergedlps_etoml 109 | return $ foldr (\(l, e) e' -> (c, t, MLLet l e e')) mlmatch $ zip ls mles 110 | 111 | hltoml :: MonoProgram -> Int -> CompMon (MLProgram, Int) 112 | hltoml (expr, combs) uid = flip runStateT uid $ do 113 | mlexpr <- exprtomlexpr expr 114 | defs <- mapM combtodef combs 115 | return (mlexpr, defs) 116 | where combtodef (labl, il, args, myexpr) = do 117 | mlexpr <- exprtomlexpr myexpr 118 | return (labl, args, mlexpr) 119 | -------------------------------------------------------------------------------- /src/ML/MLOps.hs: -------------------------------------------------------------------------------- 1 | module ML.MLOps where 2 | import Data.List(union) 3 | import Control.Monad.State 4 | import CompDefs 5 | import MLDefs 6 | 7 | type MLState t = StateT Int CompMon t 8 | runMLState :: MLState t -> Int -> CompMon (t, Int) 9 | runMLState = runStateT 10 | 11 | newlab :: MLState String 12 | newlab = do 13 | uid <- get 14 | put (uid + 1) 15 | return ("_ml#" ++ show uid) 16 | 17 | mlexprSize :: MLExpr -> Int 18 | mlexprSize (_, _, MLLiteral _) = 1 19 | mlexprSize (_, _, MLLabel _) = 1 20 | mlexprSize (_, _, MLConstructor _ es) = 1 + sum (map mlexprSize es) 21 | mlexprSize (_, _, MLCombinator _ es) = 1 + sum (map mlexprSize es) 22 | mlexprSize (_, _, MLJoin _ es) = 1 + sum (map mlexprSize es) 23 | mlexprSize (_, _, MLTest tv pes def) = 1 + mlexprSize tv + sum (map (mlexprSize . snd) pes) + mlexprSize def 24 | mlexprSize (_, _, MLProj e _ _) = 1 + mlexprSize e 25 | mlexprSize (_, _, MLLet _ e0 e1) = 1 + mlexprSize e0 + mlexprSize e1 26 | mlexprSize (_, _, MLLetJoin _ _ e0 e1) = mlexprSize e0 + mlexprSize e1 27 | mlexprSize (_, _, MLError _ _) = 1 28 | 29 | mlappears :: String -> MLExpr -> Int 30 | mlappears l (_, _, MLLiteral _) = 0 31 | mlappears l (_, _, MLLabel l') = if l == l' then 1 else 0 32 | mlappears l (_, _, MLConstructor _ es) = sum (map (mlappears l) es) 33 | mlappears l (_, _, MLCombinator _ es) = sum (map (mlappears l) es) 34 | mlappears l (_, _, MLJoin j es) = (if l == j then 1 else 0) + sum (map (mlappears l) es) 35 | mlappears l (_, _, MLTest tv pes def) = mlappears l tv + sum (map (mlappears l . snd) pes) + mlappears l def 36 | mlappears l (_, _, MLProj e _ _) = mlappears l e 37 | mlappears l (_, _, MLLet l' e0 e1) = mlappears l e0 + (if l == l' then 0 else mlappears l e1) 38 | mlappears l (_, _, MLLetJoin j lts e0 e1) = (if elem l (map fst lts) then 0 else mlappears l e0) + (if l == j then 0 else mlappears l e1) 39 | mlappears l (_, _, MLError _ _) = 0 40 | 41 | mlprogramSize :: MLProgram -> Int 42 | mlprogramSize (ep, defs) = mlexprSize ep + sum (map (mlexprSize . (\(_,_,a)->a)) defs) 43 | 44 | mlsubst :: String -> MLExpr -> MLExpr -> MLExpr 45 | mlsubst l e' e@(_, _, MLLiteral _) = e 46 | mlsubst l e' e@(_, _, MLLabel myl) = if myl == l then e' else e 47 | mlsubst l e' (c, t, MLConstructor v es) = (c, t, MLConstructor v $ map (mlsubst l e') es) 48 | mlsubst l e' (c, t, MLCombinator cmb es) = (c, t, MLCombinator cmb $ map (mlsubst l e') es) 49 | mlsubst l e' (c, t, MLJoin j es) = (c, t, MLJoin j $ map (mlsubst l e') es) 50 | mlsubst l e' (c, t, MLTest tv pes def) = (c, t, MLTest (mlsubst l e' tv) (map (\(myp, mye) -> (myp, mlsubst l e' mye)) pes) (mlsubst l e' def)) 51 | mlsubst l e' (c, t, MLProj e var n) = (c, t, MLProj (mlsubst l e' e) var n) 52 | mlsubst l e' (c, t, MLLet ll e0 e1) = (c, t, MLLet ll (mlsubst l e' e0) (mlsubst l e' e1)) --TODO: La sostituzione può avvenire solo se ll != l 53 | mlsubst l e' (c, t, MLLetJoin j lts e0 e1) = (c, t, MLLetJoin j lts (mlsubst l e' e0) (mlsubst l e' e1))--TODO: La sostituzione può avvenire solo se l nonelem lts 54 | mlsubst _ _ e@(_, _, MLError _ _) = e 55 | 56 | joinsubst :: (String, [String], MLExpr) -> MLExpr -> MLExpr 57 | joinsubst _ e@(_, _, MLLiteral _) = e 58 | joinsubst _ e@(_, _, MLLabel _) = e 59 | joinsubst j (c, t, MLConstructor v es) = (c, t, MLConstructor v $ map (joinsubst j) es) 60 | joinsubst j (c, t, MLCombinator cmb es) = (c, t, MLCombinator cmb $ map (joinsubst j) es) 61 | joinsubst (l, as, je) e@(_, _, MLJoin myl es) = if myl == l then foldl (\e' (al, ae) -> mlsubst al ae e') je (zip as es) else e 62 | joinsubst j (c, t, MLTest tv pes def) = (c, t, MLTest (joinsubst j tv) (map (\(myp, mye) -> (myp, joinsubst j mye)) pes) (joinsubst j def)) 63 | joinsubst j (c, t, MLProj e var n) = (c, t, MLProj (joinsubst j e) var n) 64 | joinsubst j (c, t, MLLet ll e0 e1) = (c, t, MLLet ll (joinsubst j e0) (joinsubst j e1)) --TODO: La sostituzione può avvenire solo se ll != l 65 | joinsubst j (c, t, MLLetJoin jl lts e0 e1) = (c, t, MLLetJoin jl lts (joinsubst j e0) (joinsubst j e1))--TODO: La sostituzione può avvenire solo se j != jl 66 | joinsubst _ e@(_, _, MLError _ _) = e 67 | 68 | unions :: Eq a => [[a]] -> [a] 69 | unions = foldr union [] 70 | --TODO: specializza a tipi? 71 | variantsUsed :: MLExpr -> [String] 72 | variantsUsed (_, _, MLLiteral _) = [] 73 | variantsUsed (_, _, MLLabel _) = [] 74 | variantsUsed (_, _, MLConstructor v es) = unions $ [v]:map variantsUsed es 75 | variantsUsed (_, _, MLCombinator _ es) = unions $ map variantsUsed es 76 | variantsUsed (_, _, MLJoin _ es) = unions $ map variantsUsed es 77 | variantsUsed (_, _, MLTest tv pes def) = unions $ variantsUsed tv : variantsUsed def : map (\(p, e) -> union (patvar p) (variantsUsed e)) pes 78 | where patvar p = case p of 79 | MLPVariant pl -> [pl] 80 | _ -> [] 81 | variantsUsed (_, _, MLProj _ var _) = [var] 82 | variantsUsed (_, _, MLLet _ e0 e1) = union (variantsUsed e0) (variantsUsed e1) 83 | variantsUsed (_, _, MLLetJoin _ _ e0 e1) = union (variantsUsed e0) (variantsUsed e1) 84 | variantsUsed (_, _, MLError _ _) = [] 85 | 86 | variantsUsedProg :: MLProgram -> [String] 87 | variantsUsedProg (ep, defs) = 88 | unions $ variantsUsed ep : map variantsUsedDef defs 89 | where variantsUsedDef (_, _, e) = variantsUsed e 90 | -------------------------------------------------------------------------------- /src/ML/MLOptimize.hs: -------------------------------------------------------------------------------- 1 | module ML.MLOptimize where 2 | import MLDefs 3 | import ML.MLOps 4 | 5 | optimizeMLExpr :: MLExpr -> MLExpr 6 | optimizeMLExpr e@(_, _, MLLiteral _) = e 7 | optimizeMLExpr e@(_, _, MLLabel _) = e 8 | optimizeMLExpr e@(_, _, MLError _ _) = e 9 | optimizeMLExpr (c, t, MLProj e var n) = (c, t, MLProj (optimizeMLExpr e) var n) 10 | optimizeMLExpr (c, t, MLConstructor v es) = (c, t, MLConstructor v $ map optimizeMLExpr es) 11 | optimizeMLExpr (c, t, MLCombinator v es) = (c, t, MLCombinator v $ map optimizeMLExpr es) 12 | optimizeMLExpr (c, t, MLJoin j es) = (c, t, MLJoin j $ map optimizeMLExpr es) 13 | optimizeMLExpr (c, t, MLTest tv pes def) = (c, t, MLTest (optimizeMLExpr tv) (map (\(p, e) -> (p, optimizeMLExpr e)) pes) (optimizeMLExpr def)) 14 | optimizeMLExpr (c, t, MLLet l e0@(_, _, MLLabel _) e1) = mlsubst l e0 $ optimizeMLExpr e1 15 | optimizeMLExpr (c, t, MLLet l e0 e1) = 16 | let e0' = optimizeMLExpr e0 17 | e1' = optimizeMLExpr e1 18 | in case mlappears l e1' of 19 | 0 -> e1' 20 | 1 -> mlsubst l e0' e1' 21 | _ -> (c, t, MLLet l e0' e1') 22 | --TODO: sostituzione di un'espressione usata una sola volta 23 | optimizeMLExpr (c, t, MLLetJoin j lts e0 e1) = 24 | let e0' = optimizeMLExpr e0 25 | e1' = optimizeMLExpr e1 26 | in case mlappears j e1' of 27 | 0 -> e1' 28 | 1 -> joinsubst (j, map fst lts, e0') e1' --TODO: può capitare che e0' sia ottimizzato considerando che gli argomenti sono label, mentre quando viene inlinizzato possono non esserlo, basterebbe introdurre dei let in joinsubst e ottimizzare su quelli 29 | _ -> (c, t, MLLetJoin j lts e0' e1') 30 | --TODO: inline se è chiamato solo una volta 31 | 32 | optimizeMLProg :: MLProgram -> MLProgram 33 | optimizeMLProg (ep, defs) = 34 | (optimizeMLExpr ep, map optimizeDef defs) 35 | where optimizeDef (lab, args, expr) = (lab, args, optimizeMLExpr expr) 36 | -------------------------------------------------------------------------------- /src/MLDefs.hs: -------------------------------------------------------------------------------- 1 | module MLDefs where 2 | import Parser.MPCL(StdCoord) 3 | import Typer.TypingDefs 4 | import HLDefs (Literal) 5 | 6 | data MLPattern 7 | = MLPLiteral Literal 8 | | MLPVariant String 9 | deriving Show 10 | 11 | data MLExprData 12 | = MLLiteral Literal 13 | | MLLabel String 14 | | MLConstructor String [MLExpr] 15 | | MLCombinator String [MLExpr] 16 | | MLJoin String [MLExpr] 17 | | MLTest MLExpr [(MLPattern, MLExpr)] MLExpr 18 | | MLProj MLExpr String Int 19 | | MLLet String MLExpr MLExpr 20 | | MLLetJoin String [(String, DataType)] MLExpr MLExpr 21 | | MLError StdCoord String --TODO: la coordinata si può prendere dall'esterno, sostituisci la stringa con una reference al tipo di errore, oppure specializza solo al pattern matching, o ancora utilizza un'espressione esterna 22 | deriving Show 23 | 24 | type MLExpr = (StdCoord, DataType, MLExprData) 25 | type MLDef = (String, [(String, DataType)], MLExpr) 26 | type MLProgram = (MLExpr, [MLDef]) 27 | -------------------------------------------------------------------------------- /src/Main.hs: -------------------------------------------------------------------------------- 1 | import System.IO 2 | import System.Environment 3 | import System.CPUTime 4 | import Data.Tree 5 | import Control.Monad.State 6 | 7 | import qualified Paths_spinnaker as Paths 8 | 9 | import CompDefs 10 | import ArgParser 11 | import HLDefs 12 | import HL.HLOps 13 | import PrettyPrinter 14 | import Parser.Demod 15 | import Typer.TypingDefs 16 | import Typer.Typer 17 | import HL.Monomorphizer 18 | import HL.HLOptimize 19 | import HL.Defunctionalize 20 | import ML.MLOps 21 | import ML.MLOptimize 22 | import ML.HLtoML 23 | --import Backends.VM.MLtoVM 24 | --import qualified Backends.VM.VM as VM 25 | import Backends.MLtoJS 26 | import Backends.MLtoSCM 27 | 28 | timeTyper :: TyperState t -> TyperState (t, Double) 29 | timeTyper a = do 30 | start <- lift $ lift $ lift getCPUTime 31 | v <- a 32 | end <- lift $ lift $ lift getCPUTime 33 | let diff = fromIntegral (end - start) / (10^9) 34 | return (v, diff) 35 | 36 | frontendCompile :: String -> CompMon (Either String (TypingEnv, HLExpr, BlockProgram, (Double, Double)), Int) 37 | frontendCompile fname = fmap (\(eitherprog, (uid, _, _)) -> (eitherprog, uid)) $ runTyperState (0,0,0) $ do 38 | rootpath <- lift $ lift getDataDir 39 | typerLog $ "Current data dir: " ++ rootpath 40 | ((_denv, entry, block), t_demod) <- timeTyper $ demodProgram (rootpath ++ "/") "stdlib/core.spk" "stdlib/std.spk" fname 41 | typerLog $ "DemodProgram:\n" ++ drawTree (toTreeBlockProgram block) 42 | ((env, tyblock), t_typer) <- timeTyper $ typeBlockProgram block 43 | typerLog $ "Typed Program:\n" ++ drawTree (toTreeBlockProgram tyblock) 44 | return (env, entry, tyblock, (t_demod, t_typer)) 45 | 46 | monoOptiPasses, defunOptiPasses :: [MonoProgram -> MonoProgram] 47 | monoOptiPasses = [optimizeDefExprs, liftCombs, inlineProgram, optimizeDefExprs] 48 | --[liftCombs, optimizeDefExprs, inlineProgram, optimizeDefExprs] 49 | defunOptiPasses = [optimizeDefExprs, inlineProgram, optimizeDefExprs] 50 | 51 | compile :: CompMon () 52 | compile = do 53 | source <- fmap (forceGetArg "source_file") getArgOptions 54 | ((eitherprog, uid),t_frontend) <- time $ frontendCompile source 55 | let (ep, block, ts) = case eitherprog of 56 | Left e -> error $ "Frontend compilation error: " ++ e 57 | Right (_, myep, myblock, myts) -> (myep, myblock, myts) 58 | let typeddatasummary = blockProgramToDataSummary block --TODO sposta questa operazione in qualche altro file 59 | (prog, t_mono) <- time $ monomorphizeProgram (ep, block) 60 | (mono, t_opti) <- time $ return $ optimizeProgram monoOptiPasses prog 61 | compLog $ "Mono " ++ showMonoProg mono 62 | ((defundatasummary, defraw, uid'), t_defun) <- time $ defunProgram mono uid 63 | let datasummary = typeddatasummary ++ defundatasummary 64 | compLog $ "Final data summary: " ++ show datasummary 65 | (defopti, t_opti2) <- time $ return $ optimizeProgram defunOptiPasses defraw 66 | compLog $ "Defun " ++ showMonoProg defopti 67 | 68 | ((mlprog, uid''), t_toml) <- time $ hltoml defopti uid' 69 | let mlopti = optimizeMLProg mlprog 70 | compLog $ "MLProg " ++ showMLProg mlopti 71 | 72 | compLog $ "Unoptimized program size: " ++ show (programSize prog) ++ ", optimized program size: " ++ show (programSize mono) ++ ", defun program size: " ++ show (programSize defopti) ++ ", ML program size: " ++ show (mlprogramSize mlopti) 73 | compLog $ "Timings: frontend:" ++ show t_frontend ++ show ts ++ "ms mono:" ++ show t_mono ++ "ms opti:" ++ show t_opti ++ "ms defun:" ++ show t_defun ++ "ms opti2:" ++ show t_opti2 ++ "ms toml:" ++ show t_toml ++ "ms" 74 | 75 | backend <- fmap (forceGetArg "backend") getArgOptions 76 | case backend of 77 | "js" -> do 78 | let jsprog = tojsProgram datasummary mlopti 79 | rootpath <- getDataDir 80 | runtimehandle <- lift $ openFile (rootpath ++ "/runtime/js/spinnaker.js") ReadMode 81 | runtimecode <- lift $ hGetContents runtimehandle 82 | lift $ writeFile "out.js" $ runtimecode ++ jsprog 83 | "scm" -> do 84 | let scmprog = toscmProgram datasummary mlopti 85 | rootpath <- getDataDir 86 | runtimehandle <- lift $ openFile (rootpath ++ "/runtime/scm/spinnaker.scm") ReadMode 87 | runtimecode <- lift $ hGetContents runtimehandle 88 | lift $ writeFile "out.scm" $ runtimecode ++ scmprog 89 | -- "vm" -> do 90 | -- let vmprog = progToVm mlopti 91 | -- compLog $ "VM Bytecode: " ++ show vmprog 92 | -- lift $ hFlush stdout 93 | -- (_, t_eval) <- time $ lift $ VM.evalProg vmprog 94 | -- compLog $ "Program eval time:" ++ show t_eval ++ "ms" 95 | 96 | argdefs :: [Arg] 97 | argdefs = 98 | [ Arg {argID="help", argShort=Just 'h', argLong=Just "help", argIsOpt=True, argData=Nothing, argDesc="Display this message"} 99 | , Arg {argID="verbose", argShort=Just 'v', argLong=Just "verbose", argIsOpt=True, argData=Nothing, argDesc="Verbose compiler output"} 100 | , Arg {argID="source_file", argShort=Just 'f', argLong=Just "file", argIsOpt=False, argData=Just $ ArgDataStr Nothing, argDesc="Specify source code file"} 101 | , Arg {argID="backend", argShort=Nothing, argLong=Just "backend", argIsOpt=True, argData=Just $ ArgDataOpt ["js", "scm"] (Just "js"), argDesc="Specify the compiler backend"} 102 | ] 103 | 104 | main :: IO () 105 | main = getArgs >>= \args -> 106 | case parseArgs argdefs args of 107 | Left _ -> putStr $ "The Spinnaker Compiler\n"++showHelp argdefs 108 | Right argparse -> do 109 | datadir <- Paths.getDataDir 110 | if gotArg "help" argparse then putStr $ "The Spinnaker Compiler\n"++showHelp argdefs else return () 111 | runCompMon CompState{dataDir=datadir, argOptions=argparse} compile 112 | -------------------------------------------------------------------------------- /src/Parser/MPCL.hs: -------------------------------------------------------------------------------- 1 | --Micro Parser Combinator Library -- Just Enough To Be Dangerous! 2 | module Parser.MPCL where 3 | 4 | import GHC.Base 5 | import GHC.Unicode 6 | 7 | type ErrMessage = String 8 | data ParseResult coord t 9 | = POk t (coord, String) 10 | | PFail coord ErrMessage 11 | | PFatal coord ErrMessage 12 | deriving Show 13 | 14 | data Parser coord a 15 | = PValue a 16 | | PParse ((coord, String)->ParseResult coord a) 17 | 18 | instance Applicative (Parser coord) where 19 | pure = PValue 20 | (<*>) = ap 21 | 22 | instance Monad (Parser coord) where 23 | (PValue a) >>= f = f a 24 | (PParse pf) >>= f = PParse(\cs -> 25 | case pf cs of 26 | POk e cs1 -> parse (f e) cs1 27 | PFail c m -> PFail c m 28 | PFatal c m -> PFatal c m 29 | ) 30 | 31 | instance Functor (Parser coord) where 32 | fmap f p = 33 | (p >>= (pure . f)) 34 | 35 | -- Funzione primaria di parsing 36 | parse :: Parser c a -> (c, String) -> ParseResult c a 37 | parse (PValue v) cs = POk v cs 38 | parse (PParse f) cs = f cs 39 | 40 | -- Scelta con preferenza a sinistra: 41 | -- (p <|| k) = p se p è valido, altrimenti k 42 | -- TODO magari conviene far vedere tutti i messaggi di errore quando falliscono 43 | infixr 5 <|| 44 | (<||) :: Parser c a -> Parser c a -> Parser c a 45 | (PValue v) <|| _ = PValue v 46 | (PParse f) <|| p = PParse(\cs -> 47 | case f cs of 48 | PFail _ _ -> parse p cs 49 | resf -> resf 50 | ) 51 | 52 | --Restituisce un parser che non consuma l'input 53 | look :: Parser c a -> Parser c a 54 | look p = PParse(\cs -> 55 | case parse p cs of 56 | POk el _ -> POk el cs 57 | resp -> resp 58 | ) 59 | 60 | --Restituisce un parser che fallisce catastroficamente se non è soddisfatto 61 | require :: Parser c a -> Parser c a 62 | require p = PParse(\cs -> 63 | case parse p cs of 64 | PFail c e -> PFatal c e 65 | resp -> resp 66 | ) 67 | --Restituisce un parser che converte i fallimenti catastrofici in fallimenti normali 68 | recover :: Parser c a -> Parser c a 69 | recover p = PParse(\cs -> 70 | case parse p cs of 71 | PFatal c e -> PFail c e 72 | resp -> resp 73 | ) 74 | 75 | discard :: Parser c a -> Parser c () 76 | discard = fmap (const ()) 77 | {- 78 | --Aggiunge la stringa specificata all'inizio del messaggio di errore 79 | detailError s p = PParse(\cs -> 80 | let resp = parse p cs in case resp of 81 | POk _ _ -> resp 82 | PFail c preve -> PFail c (s++'\n':preve)-- parse (PPFail (fst cs, s++'\n':preve)) cs 83 | PFatal c preve -> PFatal c (s++'\n':preve)-- parse (PPFail (fst cs, s++'\n':preve)) cs 84 | ) 85 | -} 86 | 87 | --Sostituisce il messaggio di errore con la stringa specificata 88 | --TODO: Forse bisogna conservare le coordinate originarie? 89 | --TODO: Forse sovrascrivi anche l'errore fatale 90 | describeError :: String -> Parser c a -> Parser c a 91 | describeError s p = PParse(\cs -> 92 | case parse p cs of 93 | PFail c preve -> PFail (fst cs) s 94 | resp -> resp 95 | ) 96 | 97 | --Messaggio di errore in caso di fallimento "soft" 98 | pfail :: String -> Parser c a 99 | pfail e = PParse(\(c, s) -> PFail c e) 100 | --Messaggio di errore fatale 101 | pfatal :: String -> Parser c a 102 | pfatal e = PParse(\(c, s) -> PFatal c e) 103 | 104 | --Ha successo se e solo se si è arrivati alla fine del file 105 | reachedEof :: Parser c () 106 | reachedEof = PParse(\(c, s) -> 107 | case s of 108 | [] -> POk () (c, s) 109 | xs -> PFail c ("EOF not reached, remaining string: " ++ xs) 110 | ) 111 | 112 | --Elabora uno o più elementi del parser specificato 113 | munch1 :: Parser c a -> Parser c [a] 114 | munch1 p = do { 115 | e <- p; 116 | es <- munch p; 117 | return (e:es) 118 | } 119 | 120 | --Elabora zero o più elementi del parser specificato 121 | munch :: Parser c a -> Parser c [a] 122 | munch p = munch1 p <|| return [] 123 | 124 | --Elabora uno o più p separati da sep 125 | sepBy1 :: Parser c p -> Parser c sep -> Parser c [p] 126 | sepBy1 p sep = do { 127 | e <- p; 128 | es <- munch (sep >> require p); -- se c'è il separatore è necessario l'elemento 129 | return (e:es) 130 | } 131 | 132 | --Elabora zero o più p separati da sep 133 | sepBy :: Parser c p -> Parser c sep -> Parser c [p] 134 | sepBy p sep = sepBy1 p sep <|| return [] 135 | 136 | --Elabora due o più p separati da sep 137 | sepBy2 :: Parser c p -> Parser c sep -> Parser c [p] 138 | sepBy2 p sep = do { 139 | e <- p; 140 | es <- munch1 (sep >> require p); 141 | return (e:es) 142 | } 143 | 144 | --Se e ha successo il parser fallisce, altrimenti fai p 145 | difference :: Show e => Parser c p -> Parser c e -> Parser c p 146 | difference p e = PParse(\cs -> 147 | case parse e cs of 148 | POk res (c, _) -> PFail c ("Unexpected parse: " ++ show res) 149 | _ -> parse p cs 150 | ) 151 | 152 | --Se p è ha successo lo restituisce, altrimenti val 153 | option :: a -> Parser c a -> Parser c a 154 | option val p = p <|| return val 155 | 156 | --Se p è ha successo restituisce Just p, altrimenti Nothing 157 | optional :: Parser c a -> Parser c (Maybe a) 158 | optional p = fmap Just p <|| return Nothing 159 | 160 | --come ReadP.satisfy, ma newc è una funzione da una coordinata alla successiva, questo dà la possibilità di cambiare sistema di riferimento 161 | satisfyInternal :: (c -> Char -> c) -> (Char -> Bool) -> String -> Parser c (c, Char) 162 | satisfyInternal newc cond emsg = PParse(\(coord, str) -> 163 | case str of 164 | [] -> PFail coord emsg 165 | (c:cs) -> if cond c then POk (coord, c) (newc coord c, cs) 166 | else PFail coord emsg 167 | ) 168 | 169 | --Definizioni utili 170 | 171 | --Sistema di coordinate predefinito, viene stampato con la forma: [file:line:col] 172 | data StdCoord = Coord String Int Int 173 | type StdParser = Parser StdCoord 174 | 175 | instance Show StdCoord where 176 | show (Coord file line col) = 177 | "[" ++ file ++ ":" ++ show line ++ ":" ++ show col ++ "]" 178 | dummyStdCoord :: StdCoord 179 | dummyStdCoord = Coord "" 0 0 180 | 181 | stdcoordNewc :: StdCoord -> Char -> StdCoord 182 | stdcoordNewc (Coord file line col) char = --TODO: Come si trattano i caratteri a più celle? 183 | case char of 184 | '\n' -> Coord file (line+1) 1 185 | _ -> Coord file line (col+1) 186 | 187 | --satisfy con StdCoord 188 | stdSatisfy :: (Char -> Bool) -> String -> StdParser (StdCoord, Char) 189 | stdSatisfy = satisfyInternal stdcoordNewc 190 | 191 | --raccoglie il carattere specificato 192 | thisChar :: Char -> StdParser (StdCoord, Char) 193 | thisChar char = stdSatisfy (char==) ("Expected character: \'" ++ char : '\'' : []) 194 | --raccoglie uno dei caratteri specificati 195 | anyChar :: [Char] -> StdParser (StdCoord, Char) 196 | anyChar chars = stdSatisfy (\c -> any (c==) chars) ("Expected one of these chars: \"" ++ chars ++ "\"") 197 | 198 | --alcuni parser utili 199 | asciiAlphaLower, asciiAlphaUpper, digit, asciiAlphaNumeric, whiteSpace :: StdParser (StdCoord, Char) 200 | asciiAlphaLower = anyChar "abcdefghijklmnopqrstuvwxyz" 201 | asciiAlphaUpper = anyChar "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 202 | digit = anyChar "01234567890" 203 | asciiAlphaNumeric = asciiAlphaLower <|| asciiAlphaUpper <|| digit 204 | whiteSpace = stdSatisfy isSpace "Expected a whitespace" 205 | -------------------------------------------------------------------------------- /src/PrettyPrinter.hs: -------------------------------------------------------------------------------- 1 | module PrettyPrinter where 2 | import Data.Tree 3 | import SyntaxDefs 4 | import HLDefs 5 | import MLDefs 6 | 7 | --Roba per HL 8 | toTreeHLPattern p = Node (show p) [] 9 | 10 | toTreeHLExpr (c, dt, ExprLiteral l) = Node (show c ++ " DT:" ++ show dt ++ " Literal: " ++ show l) [] 11 | toTreeHLExpr (c, dt, ExprApp f a) = Node (show c ++ " DT:" ++ show dt ++ " Function call") [toTreeHLExpr f, toTreeHLExpr a] 12 | toTreeHLExpr (c, dt, ExprLabel l) = Node (show c ++ " DT:" ++ show dt ++ " Label: " ++ show l) [] 13 | toTreeHLExpr (c, dt, ExprConstructor l es) = Node (show c ++ " DT:" ++ show dt ++ " Constructor: " ++ show l) (map toTreeHLExpr es) 14 | toTreeHLExpr (c, dt, ExprCombinator l es) = Node (show c ++ " DT:" ++ show dt ++ " Combinator: " ++ show l) (map toTreeHLExpr es) 15 | toTreeHLExpr (c, dt, ExprLambda l expr) = Node (show c ++ " DT:" ++ show dt ++ " Lambda") [Node ("arg " ++ show l) [], Node "expr" [toTreeHLExpr expr]] 16 | toTreeHLExpr (c, dt, ExprPut vals branches) = Node (show c ++ " DT:" ++ show dt ++ " Put") [Node "vals" (map toTreeHLExpr vals), Node "branches" (map (\(p, e) -> Node "branch" [Node "pat" [toTreeHLPattern p], Node "expr" [toTreeHLExpr e]]) branches)] 17 | toTreeHLExpr (c, dt, ExprHint hint e) = Node (show c ++ " DT:" ++ show dt ++ " Hinting with type: " ++ show hint) [toTreeHLExpr e] 18 | 19 | toTreeHLValDef (ValDef c s t ps e) = Node (show c ++ " Defining val: " ++ show s ++ " typehint: " ++ show t ++ " qualifiers: " ++ show ps) [toTreeHLExpr e] 20 | 21 | toTreeHLExtDef (ExtDef c l lext tas tr) = Node (show c ++ " External combinator: " ++ show l ++ " which imports: " ++ show lext) [Node "with arg " $ map (\ta->Node (show ta)[]) tas, Node "and return type" [Node (show tr) []]] 22 | toTreeHLDataVariant (DataVariant c labl args) = Node (show c ++ " DataVariant: " ++ show labl) (map (\t->Node ("Arg: " ++ show t) []) args) 23 | toTreeHLDataDef (DataDef c labl quants variants) = Node (show c ++ " Defining data: " ++ show labl ++ " with quantifiers: " ++ show quants) 24 | (map toTreeHLDataVariant variants) 25 | 26 | toTreeHLRelDef (RelDef c label quants preds decls) = Node (show c ++ " Defining rel: " ++ show preds ++ " => " ++ show label ++ show quants ++ " declares: ") (map (\(c, l, t)->Node (show c ++ " Decl: " ++ l ++ " of type: " ++ show t) []) decls) 27 | toTreeHLInstDef (InstDef c qualpred defs) = Node (show c ++ " Defining inst: " ++ show qualpred) (map (\(c, l, e)->Node (show c ++ " Def: " ++ show l) [toTreeHLExpr e]) defs) 28 | 29 | toTreeBlockProgram (BlockProgram datagroups reldefs extdefs valgroups instdefs) = Node "BlockProgram" [ 30 | Node "Datas" (map (Node "Group of datas" . map toTreeHLDataDef) datagroups), 31 | Node "Rels" (map toTreeHLRelDef reldefs), 32 | Node "Exts" (map toTreeHLExtDef extdefs), 33 | Node "Vals" (map (Node "Group of vals" . map toTreeHLValDef) valgroups), 34 | Node "Insts" (map toTreeHLInstDef instdefs) 35 | ] 36 | 37 | toTreeMonoDef (l, il, as, e) = Node (show l ++ " inline: " ++ show il) [Node "args" (map (\(al,at)-> Node (show al ++ ":" ++ show at) []) as), toTreeHLExpr e] 38 | toTreeMonoDefs defs = Node "MonoDefs" (map toTreeMonoDef defs) 39 | showMonoProg (ep, defs) = "EP: " ++ drawTree (toTreeHLExpr ep) ++ "\nDefs: " ++ drawTree (toTreeMonoDefs defs) 40 | 41 | --Roba per ML 42 | toTreeMLPattern p = Node (show p) [] 43 | 44 | toTreeMLBranch (pat, expr) = Node "branch" [toTreeMLPattern pat, toTreeMLExpr expr] 45 | 46 | toTreeMLExpr (c, dt, MLLiteral l) = Node (show c ++ " DT:" ++ show dt ++ " Literal: " ++ show l) [] 47 | toTreeMLExpr (c, dt, MLLabel l) = Node (show c ++ " DT:" ++ show dt ++ " Label: " ++ show l) [] 48 | toTreeMLExpr (c, dt, MLConstructor l es) = Node (show c ++ " DT:" ++ show dt ++ " Constructor: " ++ show l) (map toTreeMLExpr es) 49 | toTreeMLExpr (c, dt, MLCombinator l es) = Node (show c ++ " DT:" ++ show dt ++ " Combinator: " ++ show l) (map toTreeMLExpr es) 50 | toTreeMLExpr (c, dt, MLJoin l es) = Node (show c ++ " DT:" ++ show dt ++ " Join: " ++ show l) (map toTreeMLExpr es) 51 | toTreeMLExpr (c, dt, MLTest tv pes def) = Node (show c ++ " DT:" ++ show dt ++ " TEST") (Node "testval" [toTreeMLExpr tv] : map toTreeMLBranch pes ++ [Node "def" [toTreeMLExpr def]]) 52 | toTreeMLExpr (c, dt, MLProj e var n) = Node (show c ++ " DT: " ++ show dt ++ " PROJ FIELD " ++ show n) [toTreeMLExpr e] 53 | toTreeMLExpr (c, dt, MLLet l e0 e1) = Node (show c ++ " DT:" ++ show dt ++ " LET:" ++ show l) [Node "val" [toTreeMLExpr e0], Node "expr" [toTreeMLExpr e1]] 54 | toTreeMLExpr (c, dt, MLLetJoin l args e0 e1) = Node (show c ++ " DT:" ++ show dt ++ " LETJOIN:" ++ show l) [Node "args" (map (\(al, at) -> Node (show al ++ ":" ++ show at) []) args), Node "val" [toTreeMLExpr e0], Node "expr" [toTreeMLExpr e1]] 55 | toTreeMLExpr (c, dt, MLError _ s) = Node (show c ++ " DT:" ++ show dt ++ " ERROR:" ++ show s) [] 56 | 57 | toTreeMLDef (l, as, e) = Node (show l) [Node "args" (map (\(al,at)-> Node (show al ++ ":" ++ show at) []) as), toTreeMLExpr e] 58 | toTreeMLDefs defs = Node "MLDefs" (map toTreeMLDef defs) 59 | showMLProg (ep, defs) = "EP: " ++ drawTree (toTreeMLExpr ep) ++ "\nDefs: " ++ drawTree (toTreeMLDefs defs) 60 | --Roba per Syn 61 | toTreeSynPattern p = Node (show p) [] 62 | 63 | toTreeSynBranch (pats, e) = Node "branch" [Node "pats" (map toTreeSynPattern pats), Node "expr" [toTreeSynExpr e]] 64 | 65 | toTreeSynExpr (c, SynExprLiteral l) = Node (show c ++ " Literal: " ++ show l) [] 66 | toTreeSynExpr (c, SynExprApp f a) = Node (show c ++ " Function call") [toTreeSynExpr f, toTreeSynExpr a] 67 | toTreeSynExpr (c, SynExprLabel l) = Node (show c ++ " Label: " ++ show l) [] 68 | toTreeSynExpr (c, SynExprConstructor l) = Node (show c ++ " Constructor: " ++ show l) [] 69 | toTreeSynExpr (c, SynExprTuple es) = Node (show c ++ " Tuple") (map (maybe (Node "SECTION" []) toTreeSynExpr) es) 70 | toTreeSynExpr (c, SynExprLambda branches) = Node (show c ++ " Lambda") [Node "branches" (map toTreeSynBranch branches)] 71 | toTreeSynExpr (c, SynExprSndSection op expr) = Node (show c ++ " Second section of operator: " ++ show op) [toTreeSynExpr expr] 72 | toTreeSynExpr (c, SynExprPut vals branches) = Node (show c ++ " Put") [Node "vals" (map toTreeSynExpr vals), Node "branches" (map toTreeSynBranch branches)] 73 | toTreeSynExpr (c, SynExprString s) = Node (show c ++ "String literal: " ++ show s) [] 74 | toTreeSynExpr (c, SynExprListNil) = Node (show c ++ " Empty List") [] 75 | toTreeSynExpr (c, SynExprListConss es final) = Node (show c ++ " List") (map toTreeSynExpr es ++ [Node "With final elems" [toTreeSynExpr final]]) 76 | toTreeSynExpr (c, SynExprIfThenElse cond iftrue iffalse) = Node (show c ++ " IfThenElse") [Node "Condition" [toTreeSynExpr cond], Node "If True" [toTreeSynExpr iftrue], Node "If False" [toTreeSynExpr iffalse]] 77 | toTreeSynExpr (c, SynExprInlineUse path e) = Node (show c ++ "Inline use: " ++ show path) [toTreeSynExpr e] 78 | toTreeSynExpr (c, SynExprBind pat me fe) = Node (show c ++ "Monadic bind to pattern: " ++ show pat) [toTreeSynExpr me, toTreeSynExpr fe] 79 | toTreeSynExpr (c, SynExprHint hint e) = Node (show c ++ "Hinting with type") [toTreeSynTypeExpr hint, toTreeSynExpr e] 80 | 81 | toTreeSynValDef (SynValDef c v s te e) = Node (show c ++ " Defining " ++ show v ++ " val: " ++ show s ++ " typehint: " ++ show te) [toTreeSynExpr e] 82 | 83 | toTreeSynTypeExpr :: SyntaxTypeExpr -> Tree String 84 | toTreeSynTypeExpr te = Node (show te) [] 85 | 86 | toTreeSynDataVariant (SynDataVariant c labl args) = Node (show c ++ " DataVariant: " ++ show labl) (map toTreeSynTypeExpr args) 87 | 88 | toTreeSynDataDef (SynDataDef c v labl quants variants) = Node (show c ++ " Defining " ++ show v ++ " data: " ++ show labl ++ " with quantifiers: " ++ show quants) 89 | (map toTreeSynDataVariant variants) 90 | 91 | toTreeSynRelValDecl (c, l, te) = Node (show c ++ " Declare val: " ++ show l ++ " of type: " ++ show te) [] 92 | toTreeSynModDef (ModMod c v l m) = Node (show c ++ " Defining " ++ show v ++ " module: " ++ show l) [toTreeSynMod m] 93 | toTreeSynModDef (ModFromFile c v l f) = Node (show c ++ " Importing " ++ show v ++ " module: " ++ show l ++ " from file " ++ show f) [] 94 | toTreeSynModDef (ModUse c v p) = Node (show c ++ " Using " ++ show v ++ " module: " ++ show p) [] 95 | toTreeSynModDef (ModTypeSyn c v l qs e) = Node (show c ++ " " ++ show v ++ " type synonym: " ++ show l ++ " tyargs: " ++ show qs) [toTreeSynTypeExpr e] 96 | toTreeSynModDef (ModValGroup vvdefs) = Node "Group of vals" (map toTreeSynValDef vvdefs) 97 | toTreeSynModDef (ModDataGroup group) = Node "Group of datas" (map toTreeSynDataDef group) 98 | toTreeSynModDef (ModRel c v preds l qs defs) = Node (show c ++ " " ++ show v ++ " rel definition: " ++ show l ++ " tyargs: " ++ show qs ++ " with preds: {" ++ show preds ++ "}") (map toTreeSynRelValDecl defs) 99 | toTreeSynModDef (ModInst c qs preds head instdefs) = Node (show c ++ " Instance definition of: " ++ show head ++ " quantified with forall." ++ show qs ++ "{" ++ show preds ++ "}" ++ " with inst_val_defs") (map (\(c', l, e)->Node ("Defining: " ++ show l) [toTreeSynExpr e]) instdefs) 100 | toTreeSynModDef (ModExt c v l lext tas tr) = Node (show c ++ " Declaring " ++ show v ++ " combinator: " ++ show l ++ " which imports: " ++ show lext) [Node "with argument type" (map toTreeSynTypeExpr tas), Node "and return type" [toTreeSynTypeExpr tr]] 101 | toTreeSynMod (Module defs) = Node "Module" (map toTreeSynModDef defs) 102 | -------------------------------------------------------------------------------- /src/ResultT.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-} 2 | module ResultT where 3 | import Control.Monad.Trans 4 | import Control.Monad.State 5 | 6 | newtype ResultT m a = ResultT (m (Either String a)) 7 | 8 | runResultT :: ResultT m a -> m (Either String a) 9 | runResultT (ResultT m) = m 10 | 11 | instance Functor m => Functor (ResultT m) where 12 | fmap f (ResultT m) = ResultT (fmap (fmap f) m) 13 | instance Applicative m => Applicative (ResultT m) where 14 | pure a = ResultT (pure (Right a)) 15 | (<*>) (ResultT mf) (ResultT ma) = ResultT (fmap (<*>) mf <*> ma) 16 | instance Monad m => Monad (ResultT m) where 17 | (>>=) (ResultT m) mf = ResultT ( 18 | do eitherval <- m 19 | case eitherval of 20 | Left s -> return $ Left s 21 | Right a -> let ResultT m' = mf a in m' 22 | ) 23 | instance MonadTrans ResultT where 24 | lift m = ResultT (Right <$> m) 25 | instance MonadState s m => MonadState s (ResultT m) where 26 | get = lift get 27 | put = lift . put 28 | state = lift . state 29 | instance Monad m => MonadFail (ResultT m) where 30 | fail s = ResultT (return (Left s)) 31 | -------------------------------------------------------------------------------- /src/SyntaxDefs.hs: -------------------------------------------------------------------------------- 1 | module SyntaxDefs where 2 | import HLDefs(Literal(..)) 3 | import Parser.MPCL(StdCoord) 4 | 5 | data Path = Path [String] String 6 | deriving Show 7 | 8 | data SyntaxPatternData 9 | = SynPatWildcard 10 | | SynPatLiteral Literal --Il literal rappresentato 11 | | SynPatTuple [SyntaxPattern] --Lista di elementi della n-tupla 12 | | SynPatVariant Path [SyntaxPattern] --Nome della variante, lista di argomenti di questo 13 | | SynPatListNil -- lista vuota 14 | | SynPatListConss [SyntaxPattern] SyntaxPattern -- primi elementi della lista, continuazione 15 | deriving Show 16 | type SyntaxPattern = (StdCoord, Maybe String, SyntaxPatternData) -- coordinate, eventuale assegnazione del valore (tipo haskell labl@pat) e pattern vero e proprio 17 | 18 | data SyntaxExprData 19 | = SynExprLiteral Literal --Valore letterale 20 | | SynExprApp SyntaxExpr SyntaxExpr --Funzione, argomento 21 | | SynExprLabel Path --Riferimento a label 22 | | SynExprConstructor Path -- Riferimento a una variante 23 | | SynExprSndSection Path SyntaxExpr -- Sezioni di tipo (OP META) 24 | | SynExprTuple [Maybe SyntaxExpr] --Elementi della n-tupla 25 | | SynExprLambda [SyntaxBranch] --Argomenti(anche "smontati" e con match) e valore interno 26 | | SynExprPut [SyntaxExpr] [SyntaxBranch] --Valore da controllare, lista di pattern e i branch corrispondenti 27 | | SynExprString String -- Costante stringa 28 | | SynExprListNil -- lista vuota 29 | | SynExprListConss [SyntaxExpr] SyntaxExpr -- primi elementi della lista, continuazione 30 | | SynExprIfThenElse SyntaxExpr SyntaxExpr SyntaxExpr -- condizione, branch per true, branch per false 31 | | SynExprInlineUse Path SyntaxExpr -- modulo da portare nel contesto, espressione 32 | | SynExprBind SyntaxPattern SyntaxExpr SyntaxExpr --assegnazione, monade, funzione di trasformazione 33 | | SynExprHint SyntaxTypeExpr SyntaxExpr --type hint di un'espressione 34 | deriving Show 35 | type SyntaxExpr = (StdCoord, SyntaxExprData) 36 | 37 | type SyntaxBranch = ([SyntaxPattern], SyntaxExpr) 38 | 39 | data SyntaxTypeExprData 40 | = SynTypeExprQuantifier String -- Nome del quantifier 41 | | SynTypeExprNTuple Int -- Numero di elementi della tupla 42 | | SynTypeExprList -- Costruttore del tipo delle liste 43 | | SynTypeExprName Path -- Nome del tipo 44 | | SynTypeExprApp SyntaxTypeExpr [SyntaxTypeExpr] --Tipo funzione, tipi argomento 45 | deriving Show 46 | type SyntaxTypeExpr = (StdCoord, SyntaxTypeExprData) 47 | type SyntaxTyPred = (StdCoord, Path, [SyntaxTypeExpr]) 48 | type SyntaxTySchemeExpr = (StdCoord, [String], [SyntaxTyPred], SyntaxTypeExpr) 49 | 50 | data SyntaxValDef 51 | = SynValDef StdCoord Visibility String (Maybe SyntaxTySchemeExpr) SyntaxExpr -- Cordinate della definizione, nome del valore, espressione 52 | deriving Show 53 | 54 | data SyntaxDataVariant 55 | = SynDataVariant StdCoord String [SyntaxTypeExpr] --Coordinate della definizione, nome della variante, lista di argomenti sia come tipo concreto (da assegnare in fase di tipizzazione), sia come espressione di tipi 56 | deriving Show 57 | 58 | data SyntaxDataDef 59 | = SynDataDef StdCoord Visibility String [String] [SyntaxDataVariant] --Coordinate della definizione, nome del tipo, lista di tipi argomento e quantificatori corrispondenti (da assegnare in fase di tipizzazione), varianti del tipo 60 | deriving Show 61 | 62 | data Visibility = Public | Private 63 | deriving Show 64 | 65 | type SyntaxModRelValDecl = (StdCoord, String, SyntaxTySchemeExpr) 66 | data SyntaxModDef 67 | = ModMod StdCoord Visibility String SyntaxModule 68 | | ModFromFile StdCoord Visibility String String 69 | | ModUse StdCoord Visibility Path 70 | | ModTypeSyn StdCoord Visibility String [String] SyntaxTypeExpr 71 | | ModValGroup [SyntaxValDef] 72 | | ModDataGroup [SyntaxDataDef] 73 | | ModRel StdCoord Visibility [SyntaxTyPred] String [String] [SyntaxModRelValDecl] --visibilità, superrels, nome, tyvars, corpo 74 | | ModInst StdCoord [String] [SyntaxTyPred] SyntaxTyPred [(StdCoord, String, SyntaxExpr)]-- visibilità, predicato quantificato da constraints con forall, definizioni 75 | | ModExt StdCoord Visibility String String [SyntaxTypeExpr] SyntaxTypeExpr 76 | deriving Show 77 | data SyntaxModule = Module [SyntaxModDef] 78 | deriving Show 79 | -------------------------------------------------------------------------------- /src/Typer/KindTyper.hs: -------------------------------------------------------------------------------- 1 | module Typer.KindTyper where 2 | import qualified Data.Map as Map 3 | import qualified Data.Set as Set 4 | import Data.Maybe(fromJust, isJust) 5 | 6 | import HLDefs 7 | import Typer.TypingDefs 8 | import Typer.MGUs 9 | import Parser.MPCL(StdCoord) 10 | 11 | substApplyKindEnv :: KindSubst -> TypingEnv -> TypingEnv 12 | substApplyKindEnv s (TypingEnv ts ks vs cs rs) = TypingEnv ts (Map.map (kSubstApply s) ks) vs cs rs 13 | --TODO: il pattern \(a,b)->(a, f b) si può sostituire con un fmap f 14 | substApplyVariant :: KindSubst -> HLDataVariant -> HLDataVariant 15 | substApplyVariant s (DataVariant c l ts) = DataVariant c l (map (\(myc,t)->(myc, kSubstApply s t)) ts) 16 | substApplyQuants :: KindSubst -> [(String, TyQuant)] -> [(String, TyQuant)] 17 | substApplyQuants s qs = map (\(l,q)->(l, kSubstApply s q)) qs 18 | substApplyDataDef :: KindSubst -> HLDataDef -> HLDataDef 19 | substApplyDataDef s (DataDef c l qs vs) = DataDef c l (substApplyQuants s qs) (map (substApplyVariant s) vs) 20 | substApplyRelDecls :: KindSubst -> [(StdCoord, String, Qual DataType)] -> [(StdCoord, String, Qual DataType)] 21 | substApplyRelDecls s decls = map (\(c, l, t) -> (c, l, kSubstApply s t)) decls 22 | 23 | -- Funzioni di typing 24 | getTyData :: StdCoord -> TypingEnv -> String -> TyperState Kind 25 | getTyData c (TypingEnv _ ks _ _ _) l 26 | | fst $ isTupLabl l = let len = snd $ isTupLabl l in 27 | return $ foldr (\_->KFun KType) KType [0..len - 1] 28 | | otherwise = case Map.lookup l ks of 29 | Nothing -> fail $ show c ++ " Unbound typename: " ++ l 30 | Just k -> return k 31 | 32 | typeTyExpr :: StdCoord -> TypingEnv -> DataType -> TyperState (KindSubst, Kind, DataType) 33 | typeTyExpr _ env (DataCOORD c dt) = typeTyExpr c env dt 34 | typeTyExpr c _ (DataQuant q) = 35 | return (nullKSubst, kind q, DataQuant q) 36 | {-typeTyExpr env qmap (c, TypeExprTuple exprs) = do 37 | (s, ts) <- typeTyExprsStar env qmap exprs 38 | return $ (s, KType, DataTuple ts)-} 39 | typeTyExpr c env (DataTypeName l k) = do 40 | k' <- getTyData c env l 41 | return (nullKSubst, k', DataTypeName l k') 42 | typeTyExpr c env (DataTypeApp f a) = do 43 | q <- freshKind 44 | (s1, k1, tf) <- typeTyExpr c env f 45 | (s2, k2, ta) <- typeTyExpr c env (kSubstApply s1 a) 46 | s3 <- kindmgu c (kSubstApply s2 k1) (KFun k2 q) 47 | let finals = composeKSubst s3 (composeKSubst s2 s1) 48 | finalk = kSubstApply s3 q in 49 | return (finals, finalk, DataTypeApp (kSubstApply finals tf) (kSubstApply finals ta)) 50 | 51 | typeAndUnifyList :: Kinds e => (StdCoord -> TypingEnv -> e -> TyperState (KindSubst, Kind, e)) -> TypingEnv -> [(StdCoord, e)] -> [Kind] -> TyperState (KindSubst, [(StdCoord, e)]) 52 | typeAndUnifyList tye env [] [] = return (nullKSubst, []) 53 | typeAndUnifyList tye env ((c,e):es) (mk:ks) = do 54 | (s, k, t) <- tye c env e 55 | s' <- kindmgu c k mk 56 | (s'', ts) <- typeAndUnifyList tye env (map (\(c',e')->(c', kSubstApply (composeKSubst s' s) e')) es) ks 57 | let s''' = composeKSubst s'' (composeKSubst s' s) 58 | return (s''', (c, kSubstApply s''' t):ts) 59 | 60 | typeTyExprsStar :: TypingEnv -> [(StdCoord, DataType)] -> TyperState (KindSubst, [(StdCoord, DataType)]) 61 | typeTyExprsStar env ts = typeAndUnifyList typeTyExpr env ts (replicate (length ts) KType) 62 | typeQualTypeStar :: TypingEnv -> [(StdCoord, Qual DataType)] -> TyperState (KindSubst, [(StdCoord, Qual DataType)]) 63 | typeQualTypeStar env ts = typeAndUnifyList typeQualType env ts (replicate (length ts) KType) 64 | 65 | typeDataVariants :: TypingEnv -> [HLDataVariant] -> TyperState (KindSubst, [HLDataVariant]) 66 | typeDataVariants env [] = return (nullKSubst, []) 67 | typeDataVariants env (v:vs) = do 68 | (s, v') <- typeDataVariant v 69 | (s', vs') <- typeDataVariants env vs 70 | return (composeKSubst s' s, substApplyVariant s' v' : vs') 71 | where typeDataVariant (DataVariant c l es) = do 72 | (s, ts) <- typeTyExprsStar env es 73 | return (s, DataVariant c l ts) 74 | 75 | typeDataDef :: TypingEnv -> HLDataDef -> TyperState (KindSubst, HLDataDef) 76 | typeDataDef env (DataDef c l qs vs) = do 77 | (s, vs') <- typeDataVariants env vs 78 | return (s, DataDef c l qs vs') 79 | 80 | typeDataDefsLoop :: TypingEnv -> [HLDataDef] -> TyperState (KindSubst, [HLDataDef]) 81 | typeDataDefsLoop env [] = return (nullKSubst, []) 82 | typeDataDefsLoop env (ddef:ddefs) = do 83 | (s, ddef') <- typeDataDef env ddef 84 | (s', ddefs') <- typeDataDefsLoop (substApplyKindEnv s env) ddefs 85 | return (composeKSubst s' s, ddef':ddefs') 86 | 87 | 88 | addDataDefsEnv :: TypingEnv -> [HLDataDef] -> TypingEnv 89 | addDataDefsEnv env ddefs = 90 | let datadeftotype (DataDef _ l qs vs) = 91 | foldl DataTypeApp (DataTypeName l (dataQsToKind qs)) (map (DataQuant . snd) qs) 92 | varianttovdata t qs (DataVariant _ l ts) = 93 | Map.singleton l (VariantData l (map snd qs) (map snd ts) t) 94 | getvariantdatas ddef@(DataDef _ l qs vs) = 95 | Map.unions $ map (varianttovdata (datadeftotype ddef) qs) vs 96 | in foldl (\(TypingEnv ts ks vs cs rs) ddef@(DataDef c l qs _)-> 97 | TypingEnv ts (Map.insert l (dataQsToKind qs) ks) (Map.union vs (getvariantdatas ddef)) cs rs 98 | ) env ddefs 99 | 100 | unionDataDefEnv :: TypingEnv -> HLDataDef -> TyperState KindSubst 101 | unionDataDefEnv (TypingEnv _ ks _ _ _) (DataDef c l qs _) = kindmgu c (dataQsToKind qs) $ fromJust (Map.lookup l ks) 102 | 103 | kindMonomorphize :: Kinds k => k -> KindSubst 104 | kindMonomorphize = Map.fromList . map (flip (,) KType) . Set.toList . freeKindQuants 105 | 106 | dataMonomorphize :: HLDataDef -> KindSubst 107 | dataMonomorphize (DataDef _ _ qs _) = Map.unions $ map (kindMonomorphize . kind . snd) qs 108 | 109 | typeDataDefGroup :: TypingEnv -> [HLDataDef] -> TyperState (KindSubst, TypingEnv, [HLDataDef]) 110 | typeDataDefGroup env ddefs = let datas_env =addDataDefsEnv env ddefs in do 111 | (s, ddefs') <- typeDataDefsLoop datas_env ddefs 112 | substs <- mapM (unionDataDefEnv (substApplyKindEnv s datas_env)) ddefs' 113 | let s' = foldl (flip composeKSubst) s substs 114 | ddefs'' = map (substApplyDataDef s') ddefs' 115 | s'' = Map.unions $ map dataMonomorphize ddefs'' 116 | ddefs''' = map (substApplyDataDef s'') ddefs'' 117 | s''' = composeKSubst s'' s' 118 | env' = addDataDefsEnv (substApplyKindEnv s''' env) ddefs''' --TODO: è necessario? se non sbaglio l'env è senza variabili 119 | in return (s''', env', ddefs''') 120 | 121 | typeDataDefGroups :: TypingEnv -> [[HLDataDef]] -> TyperState (KindSubst, TypingEnv, [[HLDataDef]]) 122 | typeDataDefGroups env [] = return (nullKSubst, env, []) 123 | typeDataDefGroups env (ddefs:ddefss) = do 124 | (s, env', ddefs') <- typeDataDefGroup env ddefs 125 | (s', env'', ddefss') <- typeDataDefGroups env' ddefss 126 | return (composeKSubst s' s, env'', map (substApplyDataDef s') ddefs':ddefss') --TODO: è necessaro? se non sbaglio l'env è senza variabili 127 | 128 | typeExtDefs :: TypingEnv -> [HLExtDef] -> TyperState [HLExtDef] 129 | typeExtDefs env edefs = mapM (\(ExtDef c l lext tas tr)-> do 130 | (_, tr':tas') <- typeTyExprsStar env (map (\mt->(c,mt)) (tr:tas)) 131 | --TODO controlla monomorfizzazione 132 | return (ExtDef c l lext (map snd tas') (snd tr'))) edefs 133 | 134 | extDefsInEnv :: TypingEnv -> [HLExtDef] -> TypingEnv 135 | extDefsInEnv env@(TypingEnv ts ks vs cs rs) edefs = 136 | let ltpairs = map (\(ExtDef c l lext tas tr) -> (lext, (tas, tr))) edefs 137 | in TypingEnv ts ks vs (Map.union (Map.fromList ltpairs) cs) rs 138 | 139 | typeRelDecls :: TypingEnv -> [(StdCoord, String, Qual DataType)] -> TyperState (KindSubst, [(StdCoord, String, Qual DataType)]) 140 | typeRelDecls env decls = do 141 | (s, csts) <- typeQualTypeStar env (map (\(c,l,t)->(c,t)) decls) 142 | let s' = Map.unions $ map (\(c, t) -> kindMonomorphize $ kind t) csts 143 | return (composeKSubst s' s, zipWith (\(_,l,_) (c, t)->(c,l, kSubstApply s' t)) decls csts) 144 | 145 | 146 | addRel :: String -> [TyQuant] -> [Pred] -> [(StdCoord, String, Qual DataType)] -> TypingEnv -> TypingEnv 147 | addRel l qs preds decls (TypingEnv ts ks vs cs rs) = 148 | let relpred = Pred l (map DataQuant qs) 149 | declpairs = map (\(_,d,Qual ps t)->(d, Qual (relpred:ps) t)) decls 150 | in TypingEnv ts ks vs cs (Map.insert l (RelData qs preds declpairs []) rs) 151 | 152 | typeRelDef :: TypingEnv -> HLRelDef -> TyperState (KindSubst, TypingEnv, HLRelDef) 153 | typeRelDef env (RelDef c l qs preds decls) = do 154 | (s, preds') <- typePreds c env preds 155 | (s', decls') <- typeRelDecls env (substApplyRelDecls s decls) 156 | let s'' = Map.unions $ map (kindMonomorphize . kind . kSubstApply (composeKSubst s' s)) qs 157 | s''' = composeKSubst s'' (composeKSubst s' s) 158 | preds'' = map (substApplyPred s''') preds' 159 | decls'' = substApplyRelDecls s''' decls' 160 | qs' = map (kSubstApply s''') qs 161 | in return (s''', addRel l qs' preds'' decls'' (substApplyKindEnv s''' env), RelDef c l qs' preds'' decls'') --TODO: è necessario? se non sbaglio l'env è senza variabili. 162 | 163 | typeRelDefs :: TypingEnv -> [HLRelDef] -> TyperState (KindSubst, TypingEnv, [HLRelDef]) 164 | typeRelDefs env [] = return (nullKSubst, env, []) 165 | typeRelDefs env (reldef:reldefs) = do 166 | (s, env', reldef') <- typeRelDef env reldef 167 | (s', env'', reldefs') <- typeRelDefs env' reldefs 168 | return (composeKSubst s' s, env'', reldef':reldefs') --TODO: è necessario? se non sbaglio l'env è senza variabili. TODO: se è necessario qui c'è un bug, non viene applicata la sostituzione s' a reldef' 169 | 170 | typePred :: StdCoord -> TypingEnv -> Pred -> TyperState (KindSubst, Pred) 171 | typePred c env@(TypingEnv _ _ _ _ rs) (Pred l ts) = 172 | case Map.lookup l rs of 173 | Just (RelData qs _ _ _) -> do 174 | if length qs /= length ts 175 | then fail $ show c ++ " TypeRel: " ++ show l ++ " applied to wrong number of arguments" 176 | else do 177 | (s, ts') <- typeAndUnifyList typeTyExpr env (zip (replicate (length ts) c) ts) (map kind qs) 178 | return (s, Pred l (map snd ts')) 179 | 180 | typePreds :: StdCoord -> TypingEnv -> [Pred] -> TyperState (KindSubst, [Pred]) 181 | typePreds c env [] = return (nullKSubst, []) 182 | typePreds c env (p:ps) = do 183 | (s, p') <- typePred c env p 184 | (s', ps') <- typePreds c env (map (substApplyPred s) ps) 185 | return (composeKSubst s' s, substApplyPred s' p' : ps') 186 | 187 | typeQualPred :: StdCoord -> TypingEnv -> Qual Pred -> TyperState (KindSubst, Qual Pred) 188 | typeQualPred c env (Qual preds p) = do 189 | (s, p':preds') <- typePreds c env (p:preds) 190 | return (s, Qual preds' p') 191 | 192 | typeQualType :: StdCoord -> TypingEnv -> Qual DataType -> TyperState (KindSubst, Kind, Qual DataType) 193 | typeQualType c env (Qual preds a) = do 194 | (s, preds') <- typePreds c env preds 195 | (s', k, a') <- typeTyExpr c env (kSubstApply s a) 196 | return (composeKSubst s' s, k, Qual (map (substApplyPred s') preds') a') 197 | 198 | addInst :: Qual Pred -> TypingEnv -> TypingEnv 199 | addInst p@(Qual _ (Pred l _)) (TypingEnv ts ks vs cs rs) = TypingEnv ts ks vs cs $ Map.adjust (\(RelData qs preds decls myinsts)->RelData qs preds decls (p:myinsts)) l rs 200 | 201 | --TODO: Sposta in altro file 202 | --TODO: Controlla qui che non ci siano qualificatori liberi nelle definizioni 203 | addRelDecls :: TypingEnv -> TypingEnv 204 | addRelDecls env@(TypingEnv ts ks vs cs rs) = 205 | let general_decl_pairs = concat $ map (\(_, RelData _ _ lqts _)->map (\(l, qt)->(l, generalize env qt)) lqts) $ Map.toList rs 206 | general_decls_map = Map.fromList general_decl_pairs 207 | in TypingEnv (Map.union ts general_decls_map) ks vs cs rs 208 | 209 | typeExprHints :: KindSubst -> TypingEnv -> HLExpr -> TyperState HLExpr 210 | typeExprHints s _ (c, t, ExprLiteral l) = return (c, t, ExprLiteral l) 211 | typeExprHints s env (c, t, ExprApp f a) = do 212 | f' <- typeExprHints s env f 213 | a' <- typeExprHints s env a 214 | return (c, t, ExprApp f' a') 215 | typeExprHints s _ (c, t, ExprLabel l) = return (c, t, ExprLabel l) 216 | typeExprHints s env (c, t, ExprConstructor l es) = do 217 | es' <- mapM (typeExprHints s env) es 218 | return (c, t, ExprConstructor l es') 219 | typeExprHints s env (c, t, ExprCombinator l es) = do 220 | es' <- mapM (typeExprHints s env) es 221 | return (c, t, ExprCombinator l es') 222 | typeExprHints s env (c, t, ExprLambda l e) = do 223 | e' <- typeExprHints s env e 224 | return (c, t, ExprLambda l e') 225 | typeExprHints s env (c, t, ExprPut vs pses) = do 226 | vs' <- mapM (typeExprHints s env) vs 227 | pses' <- mapM (\(p, e) -> do {e' <- typeExprHints s env e; return (p, e')}) pses 228 | return (c, t, ExprPut vs' pses') 229 | typeExprHints s env (c, t, ExprHint hint e) = do 230 | (ks, k, hint') <- typeTyExpr c env (kSubstApply s hint) 231 | if null (freeKindQuants hint') then return () 232 | else error $ show c ++ " KWHAT" ++ show hint' ++ show s 233 | case k of 234 | KType -> return () 235 | _ -> fail $ show c ++ " Kind of type hint should be T, instead found: " ++ show k 236 | e' <- typeExprHints s env e 237 | return (c, t, ExprHint hint' e') 238 | 239 | typeKInstDef :: TypingEnv -> HLInstDef -> TyperState (KindSubst, TypingEnv, HLInstDef) 240 | typeKInstDef env (InstDef c qualhead defs) = do 241 | (s, qualhead'@(Qual _ h@(Pred l _))) <- typeQualPred c env qualhead 242 | let currInsts = map (\(Qual _ mp)->mp) $ insts env l 243 | mapM_ (\i-> 244 | if isJust (matchPred h i) && isJust (matchPred i h) 245 | then fail $ show c ++ " L'istanza " ++ show qualhead' ++ " è identica a un'altra già definita: " ++ show i 246 | else return () 247 | ) currInsts 248 | --TODO: monomorfizzazione del qualhead 249 | defs' <- mapM (\(myc,myl,e)->do 250 | e' <- typeExprHints s env e 251 | return (myc, myl, e') 252 | ) defs 253 | return (s, addInst qualhead' env, InstDef c qualhead' defs') 254 | --TODO: controlli vari su defs (e.g. condizioni Paterson), applica la sostituzione su defs se aggiungo i cast nelle espressioni 255 | 256 | typeKInstDefs :: TypingEnv -> [HLInstDef] -> TyperState (KindSubst, TypingEnv, [HLInstDef]) 257 | typeKInstDefs env [] = return (nullKSubst, env, []) 258 | typeKInstDefs env (instdef:instdefs) = do 259 | (s, env', instdef') <- typeKInstDef env instdef 260 | (s', env'', instdefs') <- typeKInstDefs env' instdefs 261 | return (composeKSubst s' s, env'', instdef':instdefs') --TODO: è necessario? se non sbaglio l'env è senza variabili. TODO: se è necessario qui c'è un bug, non viene applicata la sostituzione s' a instdef' 262 | 263 | -- Typing degli hint 264 | typeValDefHint :: TypingEnv -> HLValDef -> TyperState HLValDef 265 | typeValDefHint env vdef@(ValDef c l Nothing ps e) = do 266 | e' <- typeExprHints Map.empty env e 267 | return $ ValDef c l Nothing ps e' 268 | typeValDefHint env (ValDef c l (Just tyscheme) ps e) = do 269 | (s, _, dt) <- typeQualType c env tyscheme 270 | s' <- kindmgu c (kind dt) KType 271 | let s'' = kindMonomorphize (kSubstApply s' dt) 272 | s''' = composeKSubst s'' (composeKSubst s' s) 273 | typerLog $ show c ++" ValDef " ++ show l ++ " has type hint: " ++ show (kSubstApply s''' dt) 274 | e' <- typeExprHints s''' env e 275 | return $ ValDef c l (Just (kSubstApply s''' dt)) ps e' 276 | 277 | typeValDefHints :: TypingEnv -> [[HLValDef]] -> TyperState [[HLValDef]] 278 | typeValDefHints env vdefss = mapM (mapM $ typeValDefHint env) vdefss 279 | -------------------------------------------------------------------------------- /src/Typer/MGUs.hs: -------------------------------------------------------------------------------- 1 | module Typer.MGUs where 2 | import qualified Data.Map as Map 3 | import qualified Data.Set as Set 4 | import Data.Maybe(fromJust, isJust, isNothing, catMaybes) 5 | 6 | import Typer.TypingDefs 7 | import Parser.MPCL(StdCoord, dummyStdCoord) 8 | -- MGU per i kinds 9 | nullKSubst :: KindSubst 10 | nullKSubst = Map.empty 11 | 12 | composeKSubst :: KindSubst -> KindSubst -> KindSubst 13 | composeKSubst s1 s2 = Map.union (Map.map (kSubstApply s1) s2) s1 14 | 15 | kindQBind :: StdCoord -> KindQuant -> Kind -> TyperState KindSubst 16 | kindQBind c kq t | t == KindQuant kq = return nullKSubst 17 | | Set.member kq (freeKindQuants t) = fail $ show c ++ " Occurs check fails in kind inference: " ++ show (KindQuant kq) ++ " and " ++ show t 18 | | otherwise = return $ Map.singleton kq t 19 | 20 | kindmgu :: StdCoord -> Kind -> Kind -> TyperState KindSubst 21 | kindmgu c (KindQuant kq) t = kindQBind c kq t 22 | kindmgu c t (KindQuant kq) = kindQBind c kq t 23 | kindmgu _ KType KType = return nullKSubst 24 | kindmgu c (KFun a r) (KFun a' r') = do 25 | s1 <- kindmgu c a a' 26 | s2 <- kindmgu c (kSubstApply s1 r) (kSubstApply s1 r') 27 | return $ composeKSubst s1 s2 28 | kindmgu c k1 k2 = fail $ show c ++ " Cannot unify kinds: " ++ show k1 ++ " and " ++ show k2 29 | 30 | -- MGU per i tipi 31 | composeSubst :: Subst -> Subst -> Subst 32 | composeSubst sa sb = Map.union (Map.map (substApply sa) sb) sa 33 | 34 | nullSubst :: Subst 35 | nullSubst = Map.empty 36 | 37 | --TODO: Sposta in altro file, sono funzioni per l'env 38 | --tyBindRemove (TypingEnv typeEnv kindEnv) labl = TypingEnv (Map.delete labl typeEnv) kindEnv 39 | tyBindAdd :: TypingEnv -> String -> TyScheme -> TypingEnv 40 | tyBindAdd (TypingEnv ts ks vs cs rs) labl scheme = TypingEnv (Map.insert labl scheme ts) ks vs cs rs 41 | 42 | generalize :: TypingEnv -> Qual DataType -> TyScheme 43 | generalize env t = 44 | let quants = Set.toList $ Set.difference (freetyvars t) (freetyvars env) 45 | in TyScheme quants t 46 | 47 | getInstantiationSubst :: [TyQuant] -> TyperState Subst 48 | getInstantiationSubst qs = do 49 | nqs <- mapM (\(TyQuant _ k) -> freshType k) qs 50 | return $ Map.fromList (zip qs nqs) 51 | 52 | instantiate :: TyScheme -> TyperState (Qual DataType) 53 | instantiate scm@(TyScheme qs t) = do 54 | subst <- getInstantiationSubst qs 55 | typerLog $ "Instantiating: " ++ show scm ++ " with subst: " ++ show subst 56 | return $ substApply subst t 57 | 58 | --Algoritmo MGU 59 | quantBind :: MonadFail m => StdCoord -> TyQuant -> DataType -> m Subst 60 | quantBind c q t 61 | | (case t of 62 | DataQuant q' -> q' == q 63 | _ -> False) = return nullSubst 64 | | Set.member q (freetyvars t) = fail $ show c ++ " Occurs check fails: " ++ show q ++ " into " ++ show t 65 | | kind q /= kind t = fail $ show c ++ " Kinds do not match in substitution: " ++ show q ++ "into " ++ show t 66 | | otherwise = return (Map.singleton q t) 67 | 68 | mgu :: MonadFail m => StdCoord -> DataType -> DataType -> m Subst 69 | --TODO: regole per resilienza DataCOORD? 70 | mgu c (DataQuant q) t = quantBind c q t 71 | mgu c t (DataQuant q) = quantBind c q t 72 | mgu c t@(DataTypeName s k) t'@(DataTypeName s' k') = 73 | if s == s' && k == k' then return nullSubst else fail $ show c ++ " Could not unify typenames: " ++ show t ++ " and " ++ show t' 74 | mgu c (DataTypeApp f a) (DataTypeApp f' a') = do 75 | s <- mgu c f f' 76 | s' <- mgu c (substApply s a) (substApply s a') 77 | return (composeSubst s' s) 78 | mgu c t t' = 79 | fail $ show c ++ " Could not unify types: " ++ show t ++ " and " ++ show t' 80 | 81 | liftUnionList :: MonadFail m => (StdCoord -> DataType -> DataType -> m Subst) -> StdCoord -> [(DataType, DataType)] -> m Subst 82 | liftUnionList m c tts = 83 | foldl (\m_subst (dta, dtb) -> do{ 84 | s <- m_subst; 85 | s' <- m c (substApply s dta) (substApply s dtb); 86 | return $ composeSubst s' s 87 | }) (return nullSubst) tts 88 | 89 | --TODO: Da testare 90 | match :: MonadFail m => StdCoord -> DataType -> DataType -> m Subst 91 | match c src tgt = do 92 | s <- mgu c src tgt 93 | let 94 | keyss = Set.fromList $ map fst $ Map.toList s 95 | frees = freetyvars tgt 96 | transformsInTgt = Set.intersection keyss frees 97 | in if null transformsInTgt then return s 98 | else fail $ show c ++ " Could not match type: " ++ show src ++ " into: " ++ show tgt 99 | 100 | liftUnionPred :: MonadFail m => (StdCoord -> DataType -> DataType -> m Subst) -> StdCoord -> Pred -> Pred -> m Subst 101 | liftUnionPred m c (Pred l ts) (Pred l' ts') 102 | | l == l' = liftUnionList m c (zip ts ts') 103 | | otherwise = fail $ show c ++ " Rel labels differ: " ++ l ++ " and " ++ l' 104 | 105 | mguPred, matchPred :: Pred -> Pred -> Maybe Subst 106 | mguPred = liftUnionPred mgu dummyStdCoord 107 | matchPred = liftUnionPred match dummyStdCoord 108 | 109 | data ChooseInstRes --TODO: Questa interfaccia non è corretta 110 | = OneMatch [Pred] 111 | | MultipleMatches [InstData] 112 | | NoUnifiers 113 | | PossibleUnifiers [InstData] 114 | deriving Show 115 | 116 | insts :: TypingEnv -> String -> [InstData] 117 | insts (TypingEnv _ _ _ _ rels) l = 118 | let (RelData _ _ _ idatas) = fromJust $ Map.lookup l rels in idatas 119 | 120 | supers :: TypingEnv -> Pred -> [Pred] 121 | supers (TypingEnv _ _ _ _ rels) (Pred l ts) = 122 | let (RelData qs ps _ _) = fromJust $ Map.lookup l rels 123 | s = Map.fromList (zip qs ts) 124 | in map (substApply s) ps 125 | 126 | bySuper :: TypingEnv -> Pred -> [Pred] 127 | bySuper env p = p:concat [bySuper env p' | p' <- supers env p] 128 | 129 | chooseInst :: TypingEnv -> Pred -> ChooseInstRes 130 | chooseInst env p@(Pred l ts) = 131 | let matchInsts = getBestUniInsts matchPred 132 | mguInsts = getBestUniInsts mguPred 133 | in if null mguInsts then NoUnifiers 134 | else case matchInsts of 135 | [] -> PossibleUnifiers mguInsts --No matching instances, failure? 136 | [i@(Qual ps h)] -> --Se è l'unica instance specifica 137 | if elem i mguInsts then case matchPred h p of--Se è tra i più specifici di tutte le possibili instances 138 | --TODO: elem controlla tutto il predicato qualificato, forse dovrebbe ignorare i qualificatori 139 | Just u -> OneMatch (map (substApply u) ps) --Allora prendi i constraint 140 | else PossibleUnifiers mguInsts --altrimenti niente, i constraint potrebbero cambiare con un tipo più specifico 141 | ps -> MultipleMatches ps --Ci sono più instance specifiche 142 | where 143 | reduceToSpecifics :: [Qual Pred] -> [Qual Pred] -> [Qual Pred] 144 | reduceToSpecifics sqs [] = sqs 145 | reduceToSpecifics sqs (q@(Qual _ h):qs) = 146 | let areThereMoreSpecific = any (\(Qual _ h') -> 147 | isJust (matchPred h h') && isNothing (matchPred h' h) 148 | ) (sqs ++ qs) 149 | in if areThereMoreSpecific 150 | then reduceToSpecifics sqs qs 151 | else reduceToSpecifics (q:sqs) qs 152 | tryInstUnion m q@(Qual _ h) = do 153 | u <- m h p 154 | Just q 155 | getBestUniInsts m = 156 | let uniInsts = catMaybes [tryInstUnion m it | it <- insts env l] 157 | in reduceToSpecifics [] uniInsts 158 | 159 | entail :: TypingEnv -> [Pred] -> Pred -> Bool 160 | entail env ps p 161 | = any (elem p . bySuper env) ps 162 | || case chooseInst env p of 163 | OneMatch qs -> all (entail env ps) qs 164 | _ -> False 165 | 166 | simplify :: TypingEnv -> [Pred] -> [Pred] 167 | simplify env = loop [] 168 | where 169 | loop sps [] = sps 170 | loop sps (p:ps) | entail env (sps ++ ps) p = loop sps ps 171 | | otherwise = loop (p:sps) ps 172 | 173 | toHnfs :: MonadFail m => StdCoord -> TypingEnv -> [Pred] -> m [Pred] 174 | toHnfs c env ps = do 175 | pss <- mapM (toHnf c env) ps 176 | return (concat pss) 177 | toHnf :: MonadFail m => StdCoord -> TypingEnv -> Pred -> m [Pred] 178 | toHnf c env p = case chooseInst env p of 179 | OneMatch ps -> toHnfs c env ps 180 | NoUnifiers -> fail $ show c ++ " No compatible instance for: " ++ show p ++ "\n Instances for rel: " ++ show (insts env $ (\(Pred l _)->l) p) 181 | _ -> return [p] -- TODO: In certi casi anche qui serve un fail (quando per esempio è sicuramente impossibile decidere l'instance più specifica)--TODO: forse bisogna usare la regola isHnf 182 | reduce :: MonadFail m => StdCoord -> TypingEnv -> [Pred] -> m [Pred] 183 | reduce c env ps = do 184 | qs <- toHnfs c env ps 185 | return (simplify env qs) 186 | 187 | --TODO: Questa funzione tiene in conto anche delle variabili libere nell'env, dovrebbe essere la cosa giusta ma non ne sono del tutto sicuro 188 | checkAmbiguousQual :: (Types t, Show t) => StdCoord -> TypingEnv -> Qual t -> TyperState () 189 | checkAmbiguousQual c env (Qual ps t) = 190 | let freepsvars = Set.unions $ map freetyvars ps 191 | freedatavars = Set.union (freetyvars t) (freetyvars env) 192 | difference = Set.difference freepsvars freedatavars 193 | in if null difference then return () 194 | else fail $ show c ++ " Qualifier is ambiguous, it qualifies over type variables: " ++ show (Set.toList difference) ++ " in: " ++ show (Qual ps t) 195 | -------------------------------------------------------------------------------- /src/Typer/TypeTyper.hs: -------------------------------------------------------------------------------- 1 | module Typer.TypeTyper where 2 | import qualified Data.Map as Map 3 | 4 | import HLDefs 5 | import Typer.TypingDefs 6 | import Typer.MGUs 7 | import Parser.MPCL (StdCoord) 8 | 9 | getVariantData :: TypingEnv -> String -> TyperState VariantData 10 | getVariantData (TypingEnv _ _ vs _ _) l 11 | | fst $ isTupLabl l = let len = snd $ isTupLabl l in do 12 | qs <- mapM (\_->newTyQuant KType) [1..len] 13 | let ts = map DataQuant qs in return $ VariantData l qs ts (buildTupType ts) 14 | | otherwise = case Map.lookup l vs of 15 | --Nothing -> fail $ show c ++ " Unbound constructor: " ++ l 16 | Just vdata -> return vdata 17 | 18 | -- Funzioni di typing 19 | typeLit :: Literal -> DataType 20 | typeLit (LitInteger _) = intT 21 | typeLit (LitFloating _) = fltT 22 | typeLit (LitCharacter _) = chrT 23 | 24 | -- Funzioni per i pattern, DA RICONTROLLARE E COMPLETARE 25 | typePat :: TypingEnv -> HLPattern -> TyperState (DataType, HLPattern) 26 | typePat _ (c, _, ml, PatWildcard) = do 27 | t <- freshType KType 28 | return (t, (c, t, ml, PatWildcard)) 29 | typePat _ (c, _, ml, PatLiteral lit) = 30 | let t = typeLit lit in 31 | return (t, (c, t, ml, PatLiteral lit)) 32 | typePat env (c, _, ml, PatVariant v ps) = do 33 | (VariantData _ qs vts dt) <- getVariantData env v 34 | if length ps /= length vts then fail $ show c ++ " Constructor is applied to wrong number of arguments" 35 | else do 36 | s <- getInstantiationSubst qs 37 | (pts, ps') <- typePats env ps 38 | s' <- liftUnionList mgu c $ zip (map (substApply s) vts) pts --TODO questo in teoria controlla la validità degli argomenti, va rifatto, forse serve un algoritmo di unificazione "one-way" 39 | typerLog $ show c ++ " Variante:"++v++" di tipo-istanza:"++show (substApply s dt) ++ " unificato in:" ++ show (substApply s' (substApply s dt)) 40 | let newsubst = composeSubst s' s 41 | dt' = substApply newsubst dt 42 | return (dt', (c, dt', ml, PatVariant v (substApplyPats newsubst ps'))) 43 | typePats :: TypingEnv -> [HLPattern] -> TyperState ([DataType], [HLPattern]) 44 | typePats env pats = unzip <$> mapM (typePat env) pats 45 | 46 | --TODO Da testare 47 | patListPatVarsInEnv :: (DataType -> TyScheme) -> TypingEnv -> [HLPattern] -> [DataType] -> TyperState TypingEnv 48 | patListPatVarsInEnv gf env ps ts = foldl (\me (p, t)->do{e<-me; patVarsInEnv gf e p t}) (return env) (zip ps ts) 49 | 50 | innerPatVarsInEnv :: (DataType -> TyScheme) -> StdCoord -> TypingEnv -> HLPatternData -> DataType -> TyperState TypingEnv 51 | innerPatVarsInEnv _ _ env PatWildcard dt = return env 52 | innerPatVarsInEnv _ _ env (PatLiteral _) dt = return env 53 | innerPatVarsInEnv gf c env (PatVariant v ps) dt = do 54 | (VariantData _ qs vts vdt) <- getVariantData env v 55 | s <- mgu c vdt dt --TODO: Forse serve un algoritmo di unificazione "one-way" 56 | patListPatVarsInEnv gf env ps (map (substApply s) vts) 57 | 58 | -- TODO: potrei usare il tipo nel pattern se opportunamente sostituito 59 | patVarsInEnv :: (DataType -> TyScheme) -> TypingEnv -> HLPattern -> DataType -> TyperState TypingEnv 60 | patVarsInEnv gf env (c, _, Nothing, pdata) dt = innerPatVarsInEnv gf c env pdata dt 61 | patVarsInEnv gf env (c, _, Just labl, pdata) dt = 62 | let env' = tyBindAdd env labl (gf dt) 63 | in innerPatVarsInEnv gf c env' pdata dt 64 | 65 | -- Funzioni per le espressioni 66 | typeExprs :: TypingEnv -> [HLExpr] -> TyperState (Subst, [Pred], [DataType], [HLExpr]) 67 | typeExprs env [] = return (nullSubst, [], [], []) 68 | typeExprs env (e:es) = do 69 | (s, ps, t, e') <- typeExpr env e 70 | (s', ps', ts, es') <- typeExprs (substApply s env) es 71 | return (composeSubst s' s, ps ++ ps', t:ts, e':es') 72 | typeConsAbstraction :: StdCoord -> TypingEnv -> [DataType] -> [HLExpr] -> TyperState (Subst, [Pred], [HLExpr]) 73 | typeConsAbstraction c env argts es = --NOTE: Funziona solo se i combinatori sono monomorfici 74 | if length argts /= length es then 75 | fail $ show c ++ " Constructor is applied to wrong number of arguments" --TODO: generalizza messaggio di errore? 76 | else do 77 | (s, ps, ts, es') <- typeExprs env es 78 | s' <- liftUnionList mgu c (zip (map (substApply s) argts) ts) 79 | let s'' = composeSubst s' s 80 | return (s'', map (substApply s'') ps, es') 81 | 82 | typeExprInternal :: TypingEnv -> HLExpr -> TyperState (Subst, [Pred], DataType, HLExpr) 83 | typeExprInternal _ (c, _, ExprLiteral lit) = do 84 | let dt = typeLit lit in return (nullSubst, [], dt, (c, dt, ExprLiteral lit)) 85 | typeExprInternal (TypingEnv env _ _ _ _) (c, _, ExprLabel labl) = 86 | case Map.lookup labl env of 87 | --Nothing -> fail $ show c ++ " Unbound variable: " ++ labl 88 | Just scheme -> do 89 | typerLog $ show c ++ " LABEL:" ++ labl ++ " of scheme:" ++ show scheme 90 | Qual ps t <- instantiate scheme 91 | return (nullSubst, ps, t, (c, t, ExprLabel labl)) 92 | typeExprInternal env (c, _, ExprConstructor l es) = do 93 | (VariantData _ qs argts dt) <- getVariantData env l 94 | is <- getInstantiationSubst qs 95 | let argts' = map (substApply is) argts 96 | dt' = substApply is dt 97 | (s, ps, es') <- typeConsAbstraction c env argts' es 98 | let dt'' = substApply s dt' 99 | return (s, ps, dt'', (c, dt'', ExprConstructor l es')) 100 | typeExprInternal env@(TypingEnv _ _ _ cs _) (c, _, ExprCombinator l es) = do 101 | case Map.lookup l cs of 102 | Just (tas, tr) -> do 103 | (s, ps, es') <- typeConsAbstraction c env tas es 104 | let tr' = substApply s tr 105 | return (s, ps, tr', (c, tr', ExprCombinator l es')) 106 | typeExprInternal env (c, _, ExprApp f a) = do 107 | q <- freshType KType 108 | (s1, ps1, t1, f') <- typeExpr env f 109 | (s2, ps2, t2, a') <- typeExpr (substApply s1 env) a 110 | s3 <- mgu c (substApply s2 t1) (buildFunType t2 q) 111 | let finals = composeSubst s3 (composeSubst s2 s1) 112 | finalt = substApply finals q 113 | finalps = map (substApply finals) (ps1++ps2) 114 | -- typerLog $ show c ++" TYPINGAPP s:" ++ show finals ++ " Call:" ++ show t1 ++ " with:" ++ show t2 115 | return (finals, finalps, finalt, (c, finalt, ExprApp f' a')) 116 | -- TODO: Da qui in poi controllare bene, non so se è giusto 117 | typeExprInternal env (c, _, ExprLambda labl expr) = do 118 | argt <- freshType KType 119 | let env' = tyBindAdd env labl (TyScheme [] $ Qual [] argt) 120 | (s, ps, t, e) <- typeExpr env' expr 121 | let finaldt = buildFunType (substApply s argt) t 122 | in return (s, ps, finaldt, (c, finaldt, ExprLambda labl e)) 123 | typeExprInternal env (c, _, ExprPut vals pses) = do 124 | (s, ps, tvals, vals') <- typeExprs env vals 125 | (s', tvals', pses') <- unifyPats (substApply s env) tvals pses 126 | let temps = composeSubst s' s 127 | tempt <- freshType KType--TODO GIUSTO IL FRESH? 128 | typerLog $ show c ++ " PUT temps:" ++ show temps ++ " ps:" ++ show (map (substApply temps) ps) ++ " tval:" ++ show tvals' 129 | (s'', ps'', texpr, pses'') <- typePutBranches (substApply temps env) (map (substApply temps) ps) tvals' tempt pses' 130 | typerLog $ show c ++ " PUT" ++ show tempt ++ " tval:" ++ show tvals' ++ " texpr:"++show texpr 131 | let finals = composeSubst s'' temps 132 | finalps = map (substApply finals) (ps++ps'') 133 | finalt = substApply finals texpr 134 | in return (finals, finalps, finalt, (c, finalt, ExprPut vals' pses'')) 135 | typeExprInternal env (c, _, ExprHint hint e) = do 136 | (s, ps, t, e') <- typeExpr env e 137 | s' <- match c t hint 138 | let t' = substApply s' t 139 | return (composeSubst s' s, map (substApply s') ps, t', (c, t', ExprHint hint e')) 140 | 141 | typeExpr :: TypingEnv -> HLExpr -> TyperState (Subst, [Pred], DataType, HLExpr) 142 | typeExpr env expr@(c, _, _) = do 143 | (s, ps, t, expr') <- typeExprInternal env expr 144 | ps' <- reduce c env ps 145 | --checkAmbiguousQual c env (Qual ps' t) 146 | return (s, ps', t, expr') 147 | 148 | --Funzioni helper per putexpr 149 | unifyPats :: TypingEnv -> [DataType] -> [([HLPattern], HLExpr)] -> TyperState (Subst, [DataType], [([HLPattern], HLExpr)]) 150 | unifyPats _ ts [] = return (nullSubst, ts, []) 151 | unifyPats env ts ((pats, e@(c, _, _)):branches) 152 | | length pats /= length ts = fail $ show c ++ " Match has " ++ show (length pats) ++ " patterns, but matches on " ++ show (length ts) ++ " expressions" 153 | | otherwise = do 154 | (tpats, pats') <- typePats env pats 155 | s <- liftUnionList mgu c (zip ts tpats) 156 | (s', ts', branches') <- unifyPats (substApply s env) (map (substApply s) ts) branches 157 | return (composeSubst s' s, ts', (pats', e):branches') 158 | 159 | typePutBranches :: TypingEnv -> [Pred] -> [DataType] -> DataType -> [([HLPattern], HLExpr)] -> TyperState (Subst, [Pred], DataType, [([HLPattern], HLExpr)]) 160 | typePutBranches _ _ _ texpr [] = return (nullSubst, [], texpr, []) 161 | typePutBranches env pspat tpats texpr ((pats, expr@(c, _, _)):branches) = do 162 | typerLog $ " PUTBRANCH_SRT tpat:" ++ show tpats ++ " texpr:" ++ show texpr 163 | env' <- patListPatVarsInEnv (TyScheme [] . Qual pspat) env pats tpats 164 | (s, psexpr, texpr', expr') <- typeExpr env' expr 165 | typerLog $ " PUTBRANCH_TEX texpr: " ++ show texpr ++ " texpr':" ++ show texpr' 166 | s' <- mgu c (substApply s texpr') (substApply s texpr) --TODO: è giusto l'ordine (texpr' prima)? 167 | let mys = composeSubst s' s 168 | typerLog $ " PUTBRANCH_MYS: " ++ show mys 169 | (s'', psbranches, tfinal, others) <- typePutBranches (substApply mys env) (map (substApply mys) pspat) (map (substApply mys) tpats) (substApply s' texpr) branches 170 | typerLog $ " PUTBRANCH_END tfinal:" ++ show tfinal ++ " s:" ++ show (composeSubst s'' mys) 171 | let finals = composeSubst s'' mys 172 | finalps = map (substApply finals) (psexpr++psbranches) 173 | in return (finals, finalps, tfinal, (pats, expr'):others) 174 | 175 | --Sostituzioni su espressioni e definizioni, eseguite solo nel toplevel (riduci ancora il numero di applicazioni) 176 | substApplyPat :: Subst -> HLPattern -> HLPattern 177 | substApplyPat s (c, dt, ml, PatWildcard) = (c, substApply s dt, ml, PatWildcard) 178 | substApplyPat s (c, dt, ml, PatLiteral lit) = (c, substApply s dt, ml, PatLiteral lit) 179 | substApplyPat s (c, dt, ml, PatVariant v ps) = (c, substApply s dt, ml, PatVariant v (substApplyPats s ps)) 180 | 181 | substApplyPats :: Subst -> [HLPattern] -> [HLPattern] 182 | substApplyPats = map . substApplyPat 183 | 184 | substApplyExpr :: Subst -> HLExpr -> HLExpr 185 | substApplyExpr s (c, dt, ExprLiteral l) = (c, substApply s dt, ExprLiteral l) 186 | substApplyExpr s (c, dt, ExprApp f a) = (c, substApply s dt, ExprApp (substApplyExpr s f) (substApplyExpr s a)) 187 | substApplyExpr s (c, dt, ExprLabel l) = (c, substApply s dt, ExprLabel l) 188 | substApplyExpr s (c, dt, ExprConstructor l es) = (c, substApply s dt, ExprConstructor l (map (substApplyExpr s) es)) 189 | substApplyExpr s (c, dt, ExprCombinator l es) = (c, substApply s dt, ExprCombinator l (map (substApplyExpr s) es)) 190 | substApplyExpr s (c, dt, ExprLambda p e) = (c, substApply s dt, ExprLambda p (substApplyExpr s e)) 191 | substApplyExpr s (c, dt, ExprPut vs psandes) = (c, substApply s dt, ExprPut (map (substApplyExpr s) vs) (map (\(p, e) -> (substApplyPats s p, substApplyExpr s e)) psandes)) 192 | substApplyExpr s (c, dt, ExprHint hint e) = (c, substApply s dt, ExprHint hint (substApplyExpr s e)) 193 | 194 | substApplyValDef :: Subst -> HLValDef -> HLValDef 195 | substApplyValDef s (ValDef c l t ps e) = ValDef c l t (map (substApply s) ps) (substApplyExpr s e) 196 | 197 | --Funzioni per le definizioni globali 198 | 199 | quantifiedValDefEnv :: TypingEnv -> [HLValDef] -> TyperState TypingEnv 200 | quantifiedValDefEnv init_env [] = return init_env 201 | quantifiedValDefEnv env (ValDef c l mhint _ _:vdefs) = do 202 | t <- case mhint of 203 | Nothing -> fmap (Qual []) (freshType KType) 204 | Just hint -> return hint 205 | typerLog $ show c ++ " Binding label: " ++ show l ++ " to temporary type: " ++ show t 206 | let env' = tyBindAdd env l (TyScheme [] t) 207 | quantifiedValDefEnv env' vdefs 208 | 209 | typeValDefsLoop :: TypingEnv -> [HLValDef] -> TyperState (Subst, [HLValDef]) 210 | typeValDefsLoop _ [] = return (nullSubst, []) 211 | typeValDefsLoop env (vdef:vdefs) = do 212 | (s, vdef') <- typeValDef vdef 213 | (s', vdefs') <- typeValDefsLoop (substApply s env) (map (substApplyValDef s) vdefs) 214 | return (composeSubst s' s, substApplyValDef s' vdef':vdefs') 215 | where typeValDef (ValDef c l t _ e) = do --TODO: Qui dimentico i predicati già presenti, dovrebbero essere spazzatura dalle tipizzazioni precedenti 216 | (s, ps, dt, e') <- typeExpr env e 217 | typerLog $ "typed valdef: " ++ l ++ " with type: " ++ show (Qual ps dt) ++ " and subst: " ++ show s 218 | return (s, ValDef c l t ps e') 219 | 220 | addValDefsEnv :: TypingEnv -> [HLValDef] -> TypingEnv 221 | addValDefsEnv env vdefs = foldl 222 | (\e (ValDef _ l _ ps (_, t, _))-> 223 | tyBindAdd e l (generalize e (Qual ps t)) 224 | ) env vdefs 225 | 226 | unionValDefsEnv :: TypingEnv -> [HLValDef] -> TyperState Subst 227 | unionValDefsEnv env [] = return nullSubst 228 | unionValDefsEnv env@(TypingEnv ts _ _ _ _) (vdef:vdefs) = do 229 | s <- unionValDefEnv vdef 230 | s' <- unionValDefsEnv (substApply s env) (map (substApplyValDef s) vdefs) 231 | return (composeSubst s' s) 232 | where unionValDefEnv (ValDef c l _ ps (_, t, _)) = do 233 | Qual ps' tFromEnv <- case Map.lookup l ts of --TODO: Sto dimenticando i predicati, è giusto? 234 | Just scheme -> instantiate scheme 235 | s <- mgu c t tFromEnv 236 | typerLog $ "union of env and vdef "++ l ++": " ++ show s 237 | return s 238 | 239 | checkHintType :: StdCoord -> TypingEnv -> DataType -> DataType -> TyperState Subst 240 | checkHintType c env typehint typet = match c typet typehint 241 | 242 | checkHintPreds :: StdCoord -> TypingEnv -> [Pred] -> [Pred] -> TyperState () 243 | checkHintPreds c env pshint pst = mapM_ checkHintPred pst 244 | where checkHintPred pt = if entail env pshint pt 245 | then return () 246 | else fail $ show c ++ " Type hint qualifiers: " ++ show pshint ++ " do not entail constraint: " ++ show pt 247 | 248 | checkValDefsHint :: TypingEnv -> [HLValDef] -> TyperState Subst 249 | checkValDefsHint _ [] = return nullSubst 250 | checkValDefsHint env (ValDef c l Nothing _ _:vdefs) = checkValDefsHint env vdefs 251 | checkValDefsHint env@(TypingEnv ts _ _ _ _) (ValDef c l (Just (Qual _ hint)) ps (_, t, _):vdefs) = do 252 | s <- checkHintType c env hint t 253 | s' <- checkValDefsHint (substApply s env) (map (substApplyValDef s) vdefs) 254 | return (composeSubst s' s) 255 | 256 | checkValDefsHintPreds :: TypingEnv -> [HLValDef] -> TyperState [HLValDef] 257 | checkValDefsHintPreds env vdefs = mapM checkValDefHintPreds vdefs 258 | where checkValDefHintPreds vdef@(ValDef _ _ Nothing _ _) = return vdef 259 | checkValDefHintPreds (ValDef c l hint@(Just (Qual pshint thint)) pst e) = do 260 | checkHintPreds c env pshint pst 261 | return $ ValDef c l hint pshint e 262 | 263 | -- TODO: Quali di queste sostituzioni possono essere eliminate? (probabilmente quelle introdotte da typeValDefsLoop...) 264 | -- TODO: Mi sa che questa funzione non dovrebbe restituire una sostituzione 265 | typeValDefGroup :: TypingEnv -> [HLValDef] -> TyperState (Subst, TypingEnv, [HLValDef]) 266 | typeValDefGroup env vdefs = do 267 | vars_env <- quantifiedValDefEnv env vdefs 268 | (s, vdefs') <- typeValDefsLoop vars_env vdefs 269 | s' <- unionValDefsEnv (substApply s vars_env) vdefs' 270 | s'' <- checkValDefsHint (substApply s' vars_env) (map (substApplyValDef s') vdefs') --TODO: La posizione è giusta? 271 | let sfinal = composeSubst s'' (composeSubst s' s) 272 | vdefs'' = map (substApplyValDef sfinal) vdefs' 273 | ps = concatMap (\(ValDef _ _ _ myps _) -> myps) vdefs'' 274 | vdefs''' = map (\(ValDef c l h _ e)->ValDef c l h ps e) vdefs'' 275 | --typerLog $ "Final ValDef Subst: " ++ show sfinal 276 | mapM_ (\(ValDef c _ _ ps' (_, t, _))->checkAmbiguousQual c env (Qual ps' t)) vdefs''' 277 | vdefs'''' <- checkValDefsHintPreds env vdefs''' 278 | let env' = addValDefsEnv (substApply sfinal env) vdefs'''' 279 | if null $ freetyvars env' then return (sfinal, env', vdefs'''') 280 | else fail $ show ((\(ValDef c _ _ _ _)->c) $ head vdefs''') ++ " Ci sono delle variabili di tipo libere dopo la tipizzazione di un gruppo di valdef" 281 | 282 | typeValDefGroups :: TypingEnv -> [[HLValDef]] -> TyperState (Subst, TypingEnv, [[HLValDef]]) 283 | typeValDefGroups env [] = return (nullSubst, env, []) 284 | typeValDefGroups env (vdefs:vdefss) = do 285 | (s, env', vdefs') <- typeValDefGroup env vdefs --TODO: forse anche questa sostituzione dopo averla applicata al contesto può essere eliminata 286 | (s', env'', vdefss') <- typeValDefGroups env' vdefss 287 | return (composeSubst s' s, env'', map (substApplyValDef s') vdefs':vdefss') 288 | 289 | typeInstDef :: TypingEnv -> HLInstDef -> TyperState HLInstDef 290 | typeInstDef env@(TypingEnv _ _ _ _ rs) (InstDef c qh@(Qual ps h@(Pred l ts)) defs) = 291 | case Map.lookup l rs of 292 | Just (RelData qs preds decls _) -> do --TODO: controlla validità dei preds 293 | let instSubst = Map.fromList $ zip qs ts 294 | substdecls = map (\(ld, td)->(ld, substApply instSubst td)) decls 295 | defs' <- typeInstMembers (Map.fromList substdecls) [] defs 296 | -- TODO: Forse questo controllo va spostato da qualche altra parte (check superrel) 297 | mapM_ ((\p -> 298 | if entail env ps p 299 | then return () 300 | else fail $ show c ++ " L'istanza " ++ show qh ++ " non verifica il predicato: " ++ show p 301 | ) . substApply instSubst) preds 302 | return $ InstDef c qh defs' 303 | where typeInstMembers declmap final [] = return $ reverse final 304 | typeInstMembers declmap final ((dc,dl,e):members) = 305 | case Map.lookup dl declmap of 306 | Just (Qual dps dt) -> do 307 | (s, eps, te, e') <- typeExpr env e 308 | s' <- checkHintType dc env dt te 309 | let finals = composeSubst s' s 310 | finale = substApplyExpr finals e' 311 | checkHintPreds dc env (ps ++ dps) (map (substApply s') eps) 312 | typeInstMembers declmap ((dc, dl, finale):final) members 313 | typeInstDefs :: TypingEnv -> [HLInstDef] -> TyperState [HLInstDef] 314 | typeInstDefs env = mapM (typeInstDef env) 315 | -------------------------------------------------------------------------------- /src/Typer/Typer.hs: -------------------------------------------------------------------------------- 1 | module Typer.Typer (typeBlockProgram) where 2 | import qualified Data.Map as Map 3 | 4 | import HLDefs 5 | import Typer.TypingDefs 6 | import Typer.KindTyper 7 | import Typer.TypeTyper 8 | import Typer.VariantComplete 9 | 10 | --Definizioni builtin per Typing 11 | builtinTypingTypes :: [(String, Kind)] 12 | builtinTypingTypes = 13 | [ ("->#BI", KFun KType (KFun KType KType)) 14 | , ("Int#BI", KType) 15 | , ("Flt#BI", KType) 16 | , ("Chr#BI", KType) 17 | , ("Bool#BI", KType) 18 | , ("RealWorld_#BI", KType) 19 | ] 20 | builtinTypingVars :: [VariantData] 21 | builtinTypingVars = 22 | [ VariantData "RealWorld_" [] [] realworldT 23 | , VariantData "True" [] [] boolT 24 | , VariantData "False" [] [] boolT 25 | ] 26 | initTypingEnv :: TypingEnv 27 | initTypingEnv = TypingEnv Map.empty (Map.fromList builtinTypingTypes) (Map.fromList $ map (\v@(VariantData l _ _ _)->(l,v)) builtinTypingVars) Map.empty Map.empty 28 | 29 | --Programma typer 30 | typeBlockProgram :: BlockProgram -> TyperState (TypingEnv, BlockProgram) 31 | typeBlockProgram (BlockProgram ddefgroups reldefs extdefs vdefgroups instdefs) = do 32 | (ks, e, ddefgroups') <- typeDataDefGroups initTypingEnv ddefgroups 33 | extdefs' <- typeExtDefs e extdefs 34 | let e' = extDefsInEnv e extdefs' 35 | vdefgroups' <- completeVariantValDefGroups e' vdefgroups 36 | instdefs' <- completeVariantInstDefs e' instdefs 37 | (ks', e'', reldefs') <- typeRelDefs e' reldefs 38 | (ks'', e''', instdefs'') <- typeKInstDefs (addRelDecls e'') instdefs' 39 | vdefgroups'' <- typeValDefHints e'' vdefgroups' 40 | (ts, e'''', vdefgroups''') <- typeValDefGroups e''' vdefgroups'' 41 | instdefs''' <- typeInstDefs e'''' instdefs'' 42 | typerLog $ "Final kind substitution (datas): " ++ show ks 43 | typerLog $ "Final kind substitution (rels): " ++ show ks' 44 | typerLog $ "Final kind substitution (insts): " ++ show ks'' 45 | typerLog $ "Final type substitution: " ++ show ts 46 | typerLog $ "Final env: " ++ show e'''' 47 | typerLog $ "Final env freetyvars: " ++ show (freetyvars e'''') 48 | return (e'''', BlockProgram ddefgroups' reldefs' extdefs' vdefgroups''' instdefs''') 49 | -------------------------------------------------------------------------------- /src/Typer/TypingDefs.hs: -------------------------------------------------------------------------------- 1 | module Typer.TypingDefs where 2 | import Control.Monad.State 3 | import qualified Data.Map as Map 4 | import qualified Data.Set as Set 5 | 6 | import CompDefs 7 | import ResultT 8 | import Parser.MPCL(StdCoord) 9 | 10 | type KindQuant = Int 11 | 12 | data Kind 13 | = KindNOTHING --Kind temporaneo generato dal parser 14 | | KType 15 | | KindQuant KindQuant --Questo l'ho tolto perché alla fine dell'inferenza tutti i kind liberi diventano * 16 | | KFun Kind Kind 17 | deriving Eq 18 | 19 | data DataType 20 | = DataNOTHING --Tipo temporaneo generato dal parser 21 | | DataCOORD StdCoord DataType --Tipo temporaneo generato dal parser, serve per migliorare i messaggi di errore nel kind inference, dopodiché vengono eliminati. 22 | | DataQuant TyQuant --Quantificatore 23 | | DataTypeName String Kind -- Nome del tipo, kind del tipo 24 | | DataTypeApp DataType DataType --Funzione di tipi, argomento 25 | 26 | type KindSubst = Map.Map KindQuant Kind 27 | -- Classe kinds, usata per sostituzioni e per avere il kind 28 | class Kinds t where 29 | kind :: t->Kind 30 | kSubstApply :: KindSubst -> t -> t 31 | freeKindQuants :: t -> Set.Set KindQuant 32 | 33 | type Subst = Map.Map TyQuant DataType 34 | class Types t where 35 | freetyvars :: t -> Set.Set TyQuant 36 | substApply :: Subst -> t -> t 37 | 38 | 39 | instance Kinds Kind where 40 | kind = id 41 | kSubstApply _ KType = KType 42 | kSubstApply s (KindQuant q) = case Map.lookup q s of 43 | Nothing -> KindQuant q 44 | Just k -> k 45 | kSubstApply s (KFun a r) = KFun (kSubstApply s a) (kSubstApply s r) 46 | kSubstApply _ KindNOTHING = KindNOTHING 47 | 48 | freeKindQuants KType = Set.empty 49 | freeKindQuants (KindQuant q) = Set.singleton q 50 | freeKindQuants (KFun k k') = Set.union (freeKindQuants k) (freeKindQuants k') 51 | 52 | 53 | instance Kinds TyQuant where 54 | kind (TyQuant _ k) = k 55 | kSubstApply s (TyQuant t k) = TyQuant t (kSubstApply s k) 56 | freeKindQuants (TyQuant _ k) = freeKindQuants k 57 | 58 | instance Kinds DataType where 59 | kind (DataQuant q) = kind q 60 | --kind (DataTuple _) = KType 61 | kind (DataTypeName _ k) = k 62 | kind (DataTypeApp t _) = let (KFun _ k) = kind t in k 63 | kind (DataCOORD _ t) = kind t 64 | 65 | kSubstApply s (DataQuant q) = DataQuant (kSubstApply s q) 66 | --kSubstApply s (DataTuple ts) = DataTuple (map (kSubstApply s) ts) 67 | kSubstApply s (DataTypeName l k) = DataTypeName l (kSubstApply s k) 68 | kSubstApply s (DataTypeApp t1 t2) = DataTypeApp (kSubstApply s t1) (kSubstApply s t2) 69 | kSubstApply s (DataCOORD c t) = DataCOORD c (kSubstApply s t) 70 | 71 | freeKindQuants (DataQuant q) = freeKindQuants q 72 | freeKindQuants (DataTypeName _ k) = freeKindQuants k 73 | freeKindQuants (DataTypeApp f a) = Set.union (freeKindQuants f) (freeKindQuants a) 74 | freeKindQuants (DataCOORD _ t) = freeKindQuants t 75 | 76 | substApplyPred :: KindSubst -> Pred -> Pred 77 | substApplyPred s (Pred l ts) = Pred l $ map (kSubstApply s) ts 78 | 79 | freeKindQuantsPred :: Pred -> Set.Set KindQuant 80 | freeKindQuantsPred (Pred l ts) = Set.unions (map freeKindQuants ts) 81 | 82 | instance Kinds t => Kinds (Qual t) where 83 | kind (Qual _ t) = kind t 84 | kSubstApply s (Qual ps t) = Qual (map (substApplyPred s) ps) (kSubstApply s t) 85 | freeKindQuants (Qual ps t) = Set.unions (freeKindQuants t : map freeKindQuantsPred ps) 86 | 87 | instance Types DataType where 88 | freetyvars (DataQuant q) = Set.singleton q 89 | freetyvars (DataTypeName _ _) = Set.empty 90 | freetyvars (DataTypeApp dta dtb) = Set.union (freetyvars dta) (freetyvars dtb) 91 | freetyvars (DataCOORD _ t) = freetyvars t 92 | 93 | substApply s (DataQuant q) = case Map.lookup q s of 94 | Nothing -> DataQuant q 95 | Just t -> t 96 | substApply s (DataTypeApp dta dtb) = 97 | DataTypeApp (substApply s dta) (substApply s dtb) 98 | substApply s (DataTypeName tn k) = DataTypeName tn k 99 | --substApply s t = error $ "APPLY: " ++ show s ++ show t 100 | substApply s (DataCOORD c t) = DataCOORD c (substApply s t) 101 | 102 | instance Types Pred where 103 | freetyvars (Pred _ ts) = Set.unions $ map freetyvars ts 104 | substApply s (Pred l ts) = Pred l $ map (substApply s) ts 105 | 106 | instance Types t => Types (Qual t) where 107 | freetyvars (Qual ps t) = Set.unions $ freetyvars t : map freetyvars ps 108 | substApply s (Qual ps t) = Qual (map (substApply s) ps) (substApply s t) 109 | 110 | instance Types TyScheme where 111 | freetyvars (TyScheme qs dt) = Set.difference (freetyvars dt) (Set.fromList qs) 112 | substApply s (TyScheme qs dt) = TyScheme qs (substApply (foldr Map.delete s qs) dt) 113 | 114 | instance Types TypingEnv where 115 | freetyvars (TypingEnv ts _ _ _ _) = Set.unions $ map freetyvars (Map.elems ts) 116 | substApply s (TypingEnv ts ks vs cs rs) = TypingEnv (Map.map (substApply s) ts) ks vs cs rs 117 | 118 | instance Show Kind where 119 | show KindNOTHING = "NOTHING" 120 | show KType = "T" 121 | show (KindQuant q) = "k" ++ show q 122 | show (KFun a r) = "(" ++ show a ++ "->" ++ show r ++ ")" 123 | 124 | type TyQuantId = Int 125 | data TyQuant = TyQuant TyQuantId Kind 126 | deriving Eq 127 | instance Ord TyQuant where 128 | compare (TyQuant t1 _) (TyQuant t2 _) = compare t1 t2 129 | instance Show TyQuant where 130 | show (TyQuant i k) = "q"++show i++":"++show k 131 | 132 | instance Eq DataType where 133 | DataCOORD _ t == t' = t == t' 134 | t == DataCOORD _ t' = t == t' 135 | DataQuant q == DataQuant q' = q == q' 136 | DataTypeName l k == DataTypeName l' k' = l == l' && k == k' 137 | DataTypeApp f a == DataTypeApp f' a' = f == f' && a == a' 138 | _ == _ = False 139 | 140 | -- TODO: Miglior debug per tipi tupla (o tipi-nome) 141 | instance Show DataType where 142 | show DataNOTHING = "NOTHING" 143 | show (DataCOORD c dt) = "(AT"++show c ++ " " ++ show dt ++ ")" 144 | show (DataQuant q) = show q 145 | show (DataTypeName s k) = s++":"++show k 146 | show (DataTypeApp (DataTypeApp (DataTypeName "->#BI" _) a) r) = "(" ++ show a ++ "->" ++ show r ++ ")" --Caso speciale per le funzioni 147 | show (DataTypeApp f a) = "(" ++ show f ++ " " ++ show a ++ ")" 148 | 149 | data Pred = Pred String [DataType] 150 | deriving Eq 151 | instance Show Pred where 152 | show (Pred l ts) = l ++ concatMap ((' ':) . show) ts 153 | 154 | data Qual t = Qual [Pred] t 155 | deriving Eq 156 | 157 | instance Show t => Show (Qual t) where 158 | show (Qual ps a) = '{': foldr (\l r->show l ++ ", " ++ r) "} => " ps ++ show a 159 | 160 | data TyScheme = TyScheme [TyQuant] (Qual DataType) 161 | instance Show TyScheme where 162 | show (TyScheme qs dt) = let showq (TyQuant q k) = " " ++ show q ++ ":" ++ show k in 163 | "forall" ++ concatMap showq qs ++ "." ++ show dt 164 | 165 | data VariantData = VariantData String [TyQuant] [DataType] DataType -- Nome della variante, quantificatori generici, argomenti, datatype di appartenenza 166 | deriving Show 167 | 168 | type CombData = ([DataType], DataType) 169 | -- Definizioni rel 170 | type InstData = Qual Pred 171 | data RelData = RelData [TyQuant] [Pred] [(String, Qual DataType)] [InstData] 172 | deriving Show 173 | type RelEnv = Map.Map String RelData 174 | 175 | -- contesto dei tipi (Types), specie (Kinds), costruttori (Variants), combinatori e relazioni 176 | data TypingEnv = TypingEnv (Map.Map String TyScheme) (Map.Map String Kind) (Map.Map String VariantData) (Map.Map String CombData) RelEnv --NOTE: Il nome della variante qui è duplicato 177 | deriving Show 178 | 179 | --Definizioni utili 180 | isTupLabl :: String -> (Bool, Int) --TODO: usa un maybe 181 | isTupLabl "()" = (True, 0) 182 | isTupLabl ('(':rest) = (")" == dropWhile (','==) rest, length rest) 183 | isTupLabl _ = (False, 0) 184 | 185 | makeTupLabl :: Int -> String 186 | makeTupLabl 0 = "()" 187 | makeTupLabl 1 = error "Tuples of length 1 are forbidden" 188 | makeTupLabl len = '(':replicate (len - 1) ',' ++")" 189 | 190 | buildTupKind :: Int -> Kind 191 | buildTupKind len = foldr (\_ ret -> KFun KType ret) KType [1..len] 192 | 193 | buildTupType :: [DataType] -> DataType 194 | buildTupType ts = 195 | let len = length ts 196 | labl = makeTupLabl len 197 | in foldl DataTypeApp (DataTypeName labl $ buildTupKind len) ts 198 | 199 | buildFunType :: DataType -> DataType -> DataType 200 | buildFunType a r = 201 | DataTypeApp (DataTypeApp (DataTypeName "->#BI" (KFun KType (KFun KType KType))) a) r 202 | 203 | intT, fltT, boolT, chrT, realworldT :: DataType 204 | intT = DataTypeName "Int#BI" KType 205 | fltT = DataTypeName "Flt#BI" KType 206 | boolT = DataTypeName "Bool#BI" KType 207 | chrT = DataTypeName "Chr#BI" KType 208 | realworldT = DataTypeName "RealWorld_#BI" KType 209 | 210 | -- Infrastruttura monadica 211 | 212 | type TyperStateData = (Int, KindQuant, TyQuantId) 213 | 214 | type TyperState t = ResultT (StateT TyperStateData CompMon) t 215 | 216 | typerLog :: String -> TyperState () 217 | typerLog = lift . lift . compLog 218 | 219 | newUniqueSuffix :: TyperState String 220 | newUniqueSuffix = do 221 | (u, k, t) <- get 222 | put (u+1, k, t) 223 | return ('#':show u) 224 | 225 | newTyQuant :: Kind -> TyperState TyQuant 226 | newTyQuant k = do 227 | (u, kq, tq) <- get 228 | put (u, kq, tq+1) 229 | return $ TyQuant tq k 230 | 231 | freshType :: Kind -> TyperState DataType 232 | freshType k = do 233 | q <- newTyQuant k 234 | return $ DataQuant q 235 | 236 | newKindQuant :: TyperState KindQuant 237 | newKindQuant = do 238 | (u, k, t) <- get 239 | put (u, k+1, t) 240 | return k 241 | 242 | freshKind :: TyperState Kind 243 | freshKind = KindQuant <$> newKindQuant 244 | 245 | dataQsToKind :: [(String, TyQuant)] -> Kind 246 | dataQsToKind = foldr (KFun . kind . snd) KType 247 | 248 | runTyperState :: TyperStateData -> TyperState t -> CompMon (Either String t, TyperStateData) 249 | runTyperState s t = 250 | runStateT (runResultT t) s 251 | -------------------------------------------------------------------------------- /src/Typer/VariantComplete.hs: -------------------------------------------------------------------------------- 1 | module Typer.VariantComplete(completeVariantValDefGroups, completeVariantInstDefs) where 2 | import HLDefs 3 | import Typer.TypingDefs 4 | import Typer.TypeTyper(getVariantData) 5 | 6 | completeVariant :: TypingEnv -> HLExpr -> TyperState HLExpr 7 | completeVariant _ e@(_, _, ExprLiteral _) = return e 8 | completeVariant env (c, t, ExprApp e0 e1) = do 9 | e0' <- completeVariant env e0 10 | e1' <- completeVariant env e1 11 | return (c, t, ExprApp e0' e1') 12 | completeVariant _ e@(_, _, ExprLabel _) = return e 13 | completeVariant env (c, t, ExprConstructor l es) = do 14 | es' <- mapM (completeVariant env) es 15 | VariantData _ vqs vts vt <- getVariantData env l 16 | let zerotoadde = [0..length vts - length es -1] 17 | addesuffixes <- mapM (const newUniqueSuffix) zerotoadde 18 | return $ let 19 | addenames = map ("_v"++) addesuffixes 20 | addees = map (\myl -> (c, DataNOTHING, ExprLabel myl)) addenames 21 | in foldr (\myl e -> (c, DataNOTHING, ExprLambda myl e)) (c, DataNOTHING, ExprConstructor l (es' ++ addees)) addenames 22 | completeVariant env (c, t, ExprCombinator l es) = do 23 | es' <- mapM (completeVariant env) es 24 | return (c, t, ExprCombinator l es') 25 | completeVariant env (c, t, ExprLambda l e) = do 26 | e' <- completeVariant env e 27 | return (c, t, ExprLambda l e') 28 | completeVariant env (c, t, ExprPut vs pses) = do 29 | vs' <- mapM (completeVariant env) vs 30 | pses' <- mapM (\(p, e)-> completeVariant env e >>= \e' -> return (p, e')) pses 31 | return (c, t, ExprPut vs' pses') 32 | completeVariant env (c, t, ExprHint hint e) = do 33 | e' <- completeVariant env e 34 | return (c, t, ExprHint hint e') 35 | 36 | completeVariantValDefGroups :: TypingEnv -> [[HLValDef]] -> TyperState [[HLValDef]] 37 | completeVariantValDefGroups env = mapM (mapM (\(ValDef c l t ps e)-> ValDef c l t ps <$> completeVariant env e)) 38 | 39 | completeVariantInstDefs :: TypingEnv -> [HLInstDef] -> TyperState [HLInstDef] 40 | completeVariantInstDefs env = mapM (\(InstDef c qh defs)-> 41 | InstDef c qh <$> 42 | mapM (\(c', l, e)-> do 43 | e' <- completeVariant env e 44 | return (c', l, e')) defs) 45 | -------------------------------------------------------------------------------- /stdlib/core.spk: -------------------------------------------------------------------------------- 1 | # TODO:Questo file andrà copiato in una stringa nel compilatore 2 | 3 | mod Builtins { 4 | # Combinatori primitivi 5 | ext pub _addInt "spinnaker_addInt" : Int, Int -> Int 6 | ext pub _subInt "spinnaker_subInt" : Int, Int -> Int 7 | ext pub _mulInt "spinnaker_mulInt" : Int, Int -> Int 8 | ext pub _divInt "spinnaker_divInt" : Int, Int -> Int 9 | ext pub _remInt "spinnaker_remInt" : Int, Int -> Int 10 | ext pub _equInt "spinnaker_equInt" : Int, Int -> Bool 11 | ext pub _neqInt "spinnaker_neqInt" : Int, Int -> Bool 12 | ext pub _leqInt "spinnaker_leqInt" : Int, Int -> Bool 13 | ext pub _greInt "spinnaker_greInt" : Int, Int -> Bool 14 | 15 | ext pub _addFlt "spinnaker_addFlt" : Flt, Flt -> Flt 16 | ext pub _subFlt "spinnaker_subFlt" : Flt, Flt -> Flt 17 | ext pub _mulFlt "spinnaker_mulFlt" : Flt, Flt -> Flt 18 | ext pub _divFlt "spinnaker_divFlt" : Flt, Flt -> Flt 19 | ext pub _equFlt "spinnaker_equFlt" : Flt, Flt -> Bool 20 | ext pub _neqFlt "spinnaker_neqFlt" : Flt, Flt -> Bool 21 | ext pub _leqFlt "spinnaker_leqFlt" : Flt, Flt -> Bool 22 | ext pub _greFlt "spinnaker_greFlt" : Flt, Flt -> Bool 23 | ext pub _floorFlt "spinnaker_floorFlt" : Flt -> Int 24 | ext pub _convItoF "spinnaker_convItoF" : Int -> Flt 25 | 26 | ext pub _andBool "spinnaker_andBool" : Bool, Bool -> Bool 27 | ext pub _orBool "spinnaker_orBool" : Bool, Bool -> Bool 28 | ext pub _notBool "spinnaker_notBool" : Bool -> Bool 29 | 30 | ext pub _convItoC "spinnaker_convItoC" : Int -> Chr 31 | ext pub _convCtoI "spinnaker_convCtoI" : Chr -> Int 32 | ext pub _putChr "spinnaker_putChr" : Chr, RealWorld_ -> RealWorld_ 33 | ext pub _getChr "spinnaker_getChr" : RealWorld_ -> (Chr, RealWorld_) 34 | ext pub _isEOF "spinnaker_isEOF" : RealWorld_ -> (Bool, RealWorld_) 35 | ext pub _exit "spinnaker_exit" : Int, RealWorld_ -> RealWorld_ 36 | } 37 | 38 | def pub (|>) : forall a b c. (a -> b) -> (b -> c) -> (a -> c) 39 | = \f,g,x->g(f x) 40 | def pub (<|) : forall a b c. (b -> c) -> (a -> b) -> (a -> c) 41 | = \f,g,x->f(g x) 42 | def pub (|->) : forall a b. a -> (a -> b) -> b 43 | = \x,f->f x 44 | def pub ($) : forall a b. (a -> b) -> a -> b 45 | = \a,b->a b 46 | def pub flip : forall a b c. (a -> b -> c) -> b -> a -> c 47 | = \f,x,y->f y x 48 | def pub curry : forall a b c. ((a, b) -> c) -> a -> b -> c 49 | = \f,x,y->f(x,y) 50 | def pub uncurry : forall a b c. (a -> b -> c) -> (a, b) -> c 51 | = \f,(x,y)->f x y 52 | def pub id : forall a. a -> a 53 | = \x->x 54 | def pub const : forall a b. a -> b -> a 55 | = \x,_->x 56 | def pub fst : forall a b. (a, b) -> a 57 | = \(a,_)->a 58 | def pub snd : forall a b. (a, b) -> b 59 | = \(_,b)->b 60 | 61 | mod BoolCore { 62 | use Builtins 63 | 64 | def pub (&&) : Bool -> Bool -> Bool = _andBool 65 | def pub (||) : Bool -> Bool -> Bool = _orBool 66 | def pub not : Bool -> Bool = _notBool 67 | } use pub BoolCore 68 | 69 | mod NumCore { 70 | # Relazioni e istanze dei numerici builtin 71 | 72 | rel pub Num a = 73 | (+) : a -> a -> a, 74 | (-) : a -> a -> a, 75 | (*) : a -> a -> a, 76 | (/) : a -> a -> a, 77 | fromInt : Int -> a 78 | 79 | rel pub Eq a = 80 | (==) : a -> a -> Bool, 81 | (!=) : a -> a -> Bool 82 | 83 | inst forall a b. {Eq a, Eq b} => Eq (a, b) { 84 | def (==) = \(a, b), (a', b') -> (a == a') && (b == b') 85 | def (!=) = \a, b -> not $ a == b 86 | } 87 | 88 | rel pub {Eq a} => Ord a = 89 | (>) : a -> a -> Bool, 90 | (<=) : a -> a -> Bool 91 | def pub (<) : forall a. {Ord a} => a -> a -> Bool 92 | = \a, b -> b > a 93 | def pub (>=) : forall a. {Ord a} => a -> a -> Bool 94 | = \a, b -> b <= a 95 | 96 | 97 | rel pub Enum a = 98 | fromEnum : a -> Int, 99 | toEnum : Int -> a 100 | 101 | 102 | use Builtins 103 | 104 | def pub rem : Int -> Int -> Int 105 | = _remInt 106 | 107 | # TODO: forse incorpora in Num 108 | def pub abs : forall a. {Num a, Ord a} => a -> a 109 | = \a -> put a < fromInt 0 110 | | True -> fromInt 0 - a 111 | | False -> a 112 | 113 | def expAux = 114 | \ curr, a, 0 -> curr 115 | | curr, a, n -> expAux (a * curr) a (n - 1) 116 | and pub (^) : forall a. {Num a} => a -> Int -> a 117 | = expAux (fromInt 1) 118 | 119 | inst Enum Int { 120 | def toEnum = id 121 | def fromEnum = id 122 | } 123 | 124 | inst Enum Chr { 125 | def toEnum = _convItoC 126 | def fromEnum = _convCtoI 127 | } 128 | 129 | inst Num Int { 130 | def (+) = _addInt 131 | def (-) = _subInt 132 | def (*) = _mulInt 133 | def (/) = _divInt 134 | def fromInt = id 135 | } 136 | 137 | inst Eq Int { 138 | def (==) = _equInt 139 | def (!=) = _neqInt 140 | } 141 | 142 | inst Eq Bool { 143 | def (==) = 144 | \ True, True -> True 145 | | False, False -> True 146 | | _, _ -> False 147 | def (!=) = 148 | \ True, True -> False 149 | | False, False -> False 150 | | _, _ -> True 151 | } 152 | 153 | inst Num Flt { 154 | def (+) = _addFlt 155 | def (-) = _subFlt 156 | def (*) = _mulFlt 157 | def (/) = _divFlt 158 | def fromInt = _convItoF 159 | } 160 | def pub floor = _floorFlt 161 | 162 | inst Eq Flt { 163 | def (==) = _equFlt 164 | def (!=) = _neqFlt 165 | } 166 | 167 | inst Eq Chr { 168 | def (==) = \a, b -> fromEnum a == fromEnum b 169 | def (!=) = \a, b -> fromEnum a != fromEnum b 170 | } 171 | 172 | inst Ord Int { 173 | def (>) = _greInt 174 | def (<=)= _leqInt 175 | } 176 | 177 | inst Ord Flt { 178 | def (>) = _greFlt 179 | def (<=)= _leqFlt 180 | } 181 | inst Ord Chr { 182 | def (>) = \a, b -> fromEnum a > fromEnum b 183 | def (<=) = \a, b -> fromEnum a <= fromEnum b 184 | } 185 | 186 | } use pub NumCore 187 | 188 | mod MonadCore { 189 | # Definizione di Monade e delle strutture da cui dipende 190 | 191 | rel pub Functor f = 192 | # LAWS 193 | # 1. fmap id = id 194 | # 2. fmap (f <| g) = fmap f <| fmap g 195 | fmap : forall a b. (a -> b) -> f a -> f b 196 | 197 | rel pub {Functor m} => Monad m = 198 | # LAWS 199 | # 1. bind (return a) k = k a 200 | # 2. bind m return = m 201 | # 3. bind m (\x -> bind (k x) h) = bind (bind m k) h 202 | # 4. fmap f m = bind m (\x -> return f x) 203 | return : forall a. a -> m a, 204 | bind : forall a b. m a -> (a -> m b) -> m b 205 | 206 | def pub (<$>) : forall f a b. {Functor f} => (a -> b) -> f a -> f b 207 | = fmap 208 | def pub (>>=) : forall m a b. {Monad m} => m a -> (a -> m b) -> m b 209 | = bind 210 | def pub (>>) : forall m a b. {Monad m} => m a -> m b -> m b 211 | = \m, m' -> m >>= \_ -> m' 212 | 213 | inst forall m. {Monad m} => Functor m { 214 | def fmap = \f, m -> bind m (\x->return (f x)) 215 | } 216 | 217 | inst forall l. Functor ((,) l) { 218 | def fmap = \f, (l, a) -> (l, f a) 219 | } 220 | } use pub MonadCore 221 | 222 | mod ListCore { 223 | # Definizione di lista e alcuni primitivi molto utili 224 | 225 | data pub List a 226 | = Nil 227 | | Cons a (List a) 228 | 229 | def pub (::) : forall a. a -> List a -> List a 230 | = Cons 231 | 232 | def reverseAux = \acc, l -> 233 | put l 234 | | Nil -> acc 235 | | Cons x xs -> reverseAux (x :: acc) xs 236 | def pub reverse : forall a. List a -> List a 237 | = reverseAux Nil 238 | 239 | def mapAux = \procd, f, l -> put l 240 | | Nil -> reverse procd 241 | | Cons x xs -> mapAux (f x :: procd) f xs 242 | def pub map : forall a b. (a -> b) -> List a -> List b 243 | = mapAux Nil 244 | 245 | inst Functor List {def fmap = map} 246 | 247 | def pub (++) : forall a. List a -> List a -> List a 248 | = \l, l' -> put l 249 | | Nil -> l' 250 | | Cons x xs -> x :: (xs ++ l') 251 | 252 | inst Monad List { 253 | def return = \x -> x :: Nil 254 | def bind = \l, f-> 255 | put l 256 | | Nil -> Nil 257 | | Cons x xs -> f x ++ bind xs f 258 | } 259 | 260 | inst forall a. {Eq a} => Eq (List a) { 261 | def (==) = 262 | \ Nil, Nil -> True 263 | | Cons x xs, Cons y ys -> (put x == y 264 | | True -> xs == ys 265 | | _ -> False) 266 | | _, _ -> False 267 | def (!=) = \a, b -> not (a == b) 268 | } 269 | 270 | } use pub ListCore 271 | 272 | mod ShowCore { 273 | typesyn pub String = List Chr 274 | # Definizione di Show e delle sue istanze sui tipi builtin 275 | 276 | rel pub Show a = show : a -> String 277 | 278 | def pub strNil : String = Nil 279 | inst Show () { 280 | def show = \() -> '('::')':: Nil 281 | } 282 | 283 | inst Show Bool { 284 | def show = 285 | \ True -> 'T'::'r'::'u'::'e'::Nil 286 | | False -> 'F'::'a'::'l'::'s'::'e'::Nil 287 | } 288 | 289 | def showDigit : Int -> Chr = \i -> toEnum (fromEnum '0' + i) 290 | 291 | def showNosignAux = \s, i -> put i 292 | | 0 -> s 293 | | _ -> showNosignAux (showDigit (abs $ rem i 10) :: s) (i / 10) 294 | def showNosign = showNosignAux Nil 295 | 296 | def showInt = \i -> put i 297 | | 0 -> '0' :: Nil 298 | | _ -> put i > 0 299 | | True -> showNosign i 300 | | _ -> '-' :: showNosign i 301 | 302 | inst Show Int { def show = showInt } 303 | 304 | # TODO Test 305 | def showFracAux = 306 | \ 0, _ -> Nil 307 | | n, f -> 308 | let f' = f * 10.0 -> 309 | let flr = floor f' -> 310 | showDigit flr :: showFracAux (n - 1) (f' - fromInt flr) 311 | # TODO taglio a 4 cifre decimali, non considera la prima cifra esclusa (fa un floor quindi) 312 | def showFrac = showFracAux 4 313 | 314 | def showNonNegFlt = \f -> 315 | let flr = floor f -> 316 | show flr ++ '.' :: showFrac (f - fromInt flr) 317 | 318 | def showFlt = \f -> 319 | put f < 0.0 320 | | True -> '-' :: showNonNegFlt (0.0-f) 321 | | _ -> showNonNegFlt f 322 | 323 | inst Show Flt { def show = showFlt } 324 | 325 | def escapeChr = 326 | \ '\n' -> '\\'::'n'::Nil 327 | | '\\' -> '\\'::'\\'::Nil 328 | | '\'' -> '\\'::'\''::Nil 329 | | '"' -> '\\'::'"'::Nil 330 | | c -> c::Nil #Solo se 32 <= fromEnum c <= 126 331 | 332 | inst Show Chr { 333 | def show = \c -> Cons '\'' (escapeChr c) ++ ('\''::Nil) 334 | } 335 | 336 | inst Show String { 337 | def show = \l -> Cons '"' (l >>= escapeChr) ++ ('"'::Nil) 338 | } 339 | 340 | def showListAux = 341 | \ Nil -> ']' :: Nil 342 | | Cons x Nil -> show x ++ (']' :: Nil) 343 | | Cons x xs -> show x ++ Cons ',' (Cons ' ' Nil) ++ showListAux xs 344 | 345 | def showList = \l -> '[' :: showListAux l 346 | 347 | def showtup2 = \a, b -> show a ++ ','::' ':: show b 348 | def showtup3 = \a, b, c -> show a ++ ','::' '::showtup2 b c 349 | def showtup4 = \a, b, c, d -> showtup2 a b ++ ','::' ':: showtup2 c d 350 | 351 | inst forall a. {Show a} => Show (List a) { 352 | def show = showList 353 | } 354 | 355 | inst forall a b. {Show a, Show b} => Show (a, b) { 356 | def show = \(a, b) -> '(' :: showtup2 a b ++ (')'::Nil) 357 | } 358 | 359 | inst forall a b c. {Show a, Show b, Show c} => Show (a, b, c) { 360 | def show = \(a, b, c) -> '(' :: showtup3 a b c ++ (')'::Nil) 361 | } 362 | 363 | inst forall a b c d. {Show a, Show b, Show c, Show d} => Show (a, b, c, d) { 364 | def show = \(a, b, c, d) -> '(':: showtup4 a b c d ++ (')'::Nil) 365 | } 366 | 367 | } use pub ShowCore 368 | 369 | mod IOCore { 370 | # Definizione del monade IO e delle operazioni primitive 371 | 372 | data pub IO a = IO (RealWorld_ -> (a, RealWorld_)) 373 | 374 | inst Functor IO { 375 | def fmap = \f, IO tf -> IO (\rw-> 376 | let (a, rw') = tf rw -> 377 | (f a, rw') 378 | ) 379 | } 380 | 381 | inst Monad IO { 382 | def return = \a -> IO(\rw -> (a, rw)) 383 | def bind = \IO tf, f -> IO (\rw-> 384 | let (a, rw') = tf rw -> 385 | let IO tf' = f a -> 386 | tf' rw' 387 | ) 388 | } 389 | 390 | mod pub UnsafeIO { 391 | use Builtins 392 | 393 | def pub putChr = _putChr 394 | def pub getChr = _getChr 395 | def pub isEOF = _isEOF 396 | def pub exit = _exit 397 | 398 | def pub putStr : String -> RealWorld_ -> RealWorld_ 399 | = \s, rw -> put s 400 | | Nil -> rw 401 | | Cons c cs -> 402 | let rw' = putChr c rw -> 403 | putStr cs rw' 404 | def pub putStrLn = \s, rw -> 405 | let rw' = putStr s rw -> 406 | putChr '\n' rw' 407 | def pub getLn = \rw -> 408 | put getChr rw 409 | | ('\n', rw') -> (Nil, rw') 410 | | (c, rw') -> 411 | let (s, rw'') = getLn rw' -> 412 | (c :: s, rw'') 413 | 414 | def pub runTopIO = \IO tf -> let ((), rw) = tf RealWorld_ -> rw 415 | } 416 | 417 | def pub putChr : Chr -> IO () 418 | = \c -> IO(\s->((), UnsafeIO.putChr c s)) 419 | def pub getChr : IO Chr 420 | = IO UnsafeIO.getChr 421 | def pub putStr : String -> IO () 422 | = \l -> IO(\s->((), UnsafeIO.putStr l s)) 423 | def pub putStrLn : String -> IO () 424 | = \l -> IO(\s->((), UnsafeIO.putStrLn l s)) 425 | def pub getLn : IO String 426 | = IO UnsafeIO.getLn 427 | def pub isEOF : IO Bool 428 | = IO UnsafeIO.isEOF 429 | 430 | def pub print : forall a. {Show a} => a -> IO () 431 | = show |> putStrLn 432 | 433 | #TODO: sposta in Debug 434 | def errorAux : forall a. String -> RealWorld_ -> a 435 | = \s, rw -> errorAux s (UnsafeIO.exit 1 (UnsafeIO.putStrLn s rw)) 436 | def pub error : forall a. String -> a 437 | = \s -> errorAux ('e'::'r'::'r'::'o'::'r'::':'::' '::s) RealWorld_ 438 | } use pub IOCore 439 | 440 | 441 | # TODO: inserisci in un modulo a sé 442 | 443 | rel pub ProgramTop a = runProgramTop : a -> IO () 444 | 445 | inst ProgramTop (IO ()) { def runProgramTop = id } 446 | 447 | inst forall a. {Show a} => ProgramTop a { def runProgramTop = print } 448 | 449 | inst forall a. {ProgramTop a} => ProgramTop (IO a) { 450 | def runProgramTop = (>>= runProgramTop) 451 | } 452 | -------------------------------------------------------------------------------- /stdlib/debug.spk: -------------------------------------------------------------------------------- 1 | use Core 2 | 3 | def carryPutStrLn : forall a. RealWorld_ -> String -> a -> (a, RealWorld_) 4 | = \rw, s, a -> put s 5 | | [] -> (a, UnsafeIO.putChr '\n' rw) 6 | | [c | cs] -> 7 | let rw' = UnsafeIO.putChr c rw -> 8 | carryPutStrLn rw' cs a 9 | 10 | def pub trace : forall a. String -> a -> a 11 | = \s, a -> fst (carryPutStrLn RealWorld_ s a) 12 | 13 | def pub traceit : forall a. {Show a} => a -> a 14 | = \a -> trace (show a) a 15 | -------------------------------------------------------------------------------- /stdlib/either.spk: -------------------------------------------------------------------------------- 1 | use Core 2 | 3 | data pub Either a b = Left a | Right b 4 | 5 | def pub isLeft : forall a b. Either a b -> Bool = 6 | \ Left _ -> True 7 | | Right _ -> False 8 | 9 | def pub isRight : forall a b. Either a b -> Bool = 10 | \ Left _ -> True 11 | | Right _ -> False 12 | 13 | def pub either : forall a b c. (a -> c) -> (b -> c) -> Either a b -> c = 14 | \ fa, _, Left a -> fa a 15 | | _, fb, Right b -> fb b 16 | 17 | inst forall a b. {Show a, Show b} => Show (Either a b) { 18 | def show = 19 | \ Left a -> "Left (" ++ show a ++ ")" 20 | | Right b -> "Right (" ++ show b ++ ")" 21 | } 22 | 23 | inst forall a. Functor (Either a) { 24 | def fmap = 25 | \ _, Left a -> Left a 26 | | f, Right b -> Right (f b) 27 | } 28 | 29 | inst forall a. Monad (Either a) { 30 | def return = Right 31 | def bind = 32 | \ Left a, _ -> Left a 33 | | Right b, f -> f b 34 | } 35 | -------------------------------------------------------------------------------- /stdlib/list.spk: -------------------------------------------------------------------------------- 1 | use Core 2 | 3 | mod Maybe "maybe.spk" 4 | 5 | 6 | def pub head : forall a. [a] -> a 7 | = \[x | xs] -> x 8 | def pub tail : forall a. [a] -> [a] 9 | = \[x | xs] -> xs 10 | def pub last : forall a. [a] -> a = 11 | \ [x] -> x 12 | | [x | xs] -> last xs 13 | def pub init : forall a. [a] -> [a] = 14 | \ [x] -> [] 15 | | [x | xs] -> x :: (init xs) 16 | 17 | # TODO: uncons 18 | 19 | def pub null : forall a. [a] -> Bool = 20 | \ [] -> True 21 | | _ -> False 22 | 23 | def lengthAux = \n, l -> put l 24 | | [] -> n 25 | | [_ | xs] -> lengthAux (n + 1) xs 26 | and pub length : forall a. [a] -> Int 27 | = lengthAux 0 28 | 29 | def pub foldl : forall a s. (s -> a -> s) -> s -> [a] -> s 30 | = \f, s, l -> put l 31 | | [] -> s 32 | | [x | xs] -> foldl f (f s x) xs 33 | 34 | def pub foldr : forall s a. (a -> s -> s) -> s -> [a] -> s 35 | = \f, s, l -> put l 36 | | [] -> s 37 | | [x | xs] -> f x (foldr f s xs) 38 | 39 | def pub foldl1 : forall a. (a -> a -> a) -> [a] -> a 40 | = \f, [x | xs] -> foldl f x xs 41 | 42 | # TODO: foldr1, scanl, scanr, scanl1, scanr1 43 | 44 | # TODO: versione tail-call? 45 | def pub unfoldr : forall a b. (a -> Maybe.Maybe (b, a)) -> a -> [b] 46 | = \f, a -> put f a 47 | | Maybe.None -> [] 48 | | Maybe.Some (b, a') -> b :: unfoldr f a' 49 | 50 | def replicateAux = 51 | \ curr, 0, _ -> curr 52 | | curr, n, a -> replicateAux (a :: curr) (n - 1) a 53 | and pub replicate : forall a. Int -> a -> [a] 54 | = replicateAux [] 55 | 56 | # TODO: Tailcall 57 | def pub map2 : forall a b c. (a -> b -> c) -> [a] -> [b] -> [c] 58 | = \f, l, l' -> put l, l' 59 | | [], _ -> [] 60 | | _, [] -> [] 61 | | [x | xs], [x' | xs'] -> f x x' :: map2 f xs xs' 62 | 63 | # TODO: Tailcall 64 | # zip = map2 (,) 65 | def pub zip : forall a b. [a] -> [b] -> [(a, b)] = 66 | \ [], _ -> [] 67 | | _, [] -> [] 68 | | [x | xs], [x' | xs'] -> (x, x') :: zip xs xs' 69 | 70 | def pub any : forall a. (a -> Bool) -> [a] -> Bool 71 | = \bf, l -> put l 72 | | [] -> False 73 | | [x | xs] -> put bf x 74 | | True -> True 75 | | _ -> any bf xs 76 | 77 | def pub all : forall a. (a -> Bool) -> [a] -> Bool 78 | = \bf, l -> put l 79 | | [] -> True 80 | | [x | xs] -> put bf x 81 | | False -> False 82 | | _ -> all bf xs 83 | 84 | # TODO: and, or, sum, product, maximum, minimum 85 | 86 | def pub elem : forall a. {Eq a} => a -> [a] -> Bool 87 | = \x -> any (x ==) 88 | 89 | def pub nth : forall a. Int -> [a] -> a 90 | = \n, l -> put n 91 | | 0 -> head l 92 | | _ -> nth (n - 1) (tail l) 93 | 94 | # TODO: versione tail-call 95 | def pub updateAt : forall a. Int -> (a -> a) -> [a] -> [a] 96 | = \n, f, l -> put n, l 97 | | 0, [x | xs] -> f x :: xs 98 | | _, [x | xs] -> x :: updateAt (n - 1) f xs 99 | 100 | def filterAux 101 | = \c, f, l -> put l 102 | | [] -> reverse c 103 | | [x | xs] -> 104 | let newc = if f x then x :: c else c -> 105 | filterAux newc f xs 106 | and pub filter : forall a. (a -> Bool) -> [a] -> [a] 107 | = filterAux [] 108 | 109 | def pub find : forall a. (a -> Bool) -> [a] -> Maybe.Maybe a = 110 | \ f, [] -> Maybe.None 111 | | f, [x | xs] -> if f x 112 | then Maybe.Some x 113 | else find f xs 114 | 115 | def findIndexAux = 116 | \ _, _, [] -> Maybe.None 117 | | n, f, [x | xs] -> if f x 118 | then Maybe.Some n 119 | else findIndexAux (n+1) f xs 120 | and pub findIndex : forall a. (a -> Bool) -> [a] -> Maybe.Maybe Int 121 | = findIndexAux 0 122 | 123 | def pub elemIndex : forall a. {Eq a} => a -> [a] -> Maybe.Maybe Int 124 | = \a -> findIndex (a ==) 125 | 126 | def pub lookup : forall a b. {Eq a} => a -> [(a, b)] -> Maybe.Maybe b 127 | = \a -> fmap snd <| find (fst |> (a ==)) 128 | 129 | # TODO: partition, elemIndices, findIndex serio, findIndices 130 | 131 | # TODO: forse conviene utilizzare una funzione passata come argomento come trasformazione di c e l, oppure reimplementare take e drop autonomamente 132 | def splitAtAux = \c, i, l -> put i, l 133 | | 0, _ -> (reverse c, l) 134 | | _, [x | xs] -> splitAtAux (x::c) (i - 1) xs 135 | and pub splitAt : forall a. Int -> [a] -> ([a], [a]) 136 | = splitAtAux [] 137 | 138 | def pub take : forall a. Int -> [a] -> [a] 139 | = \i, l -> fst (splitAtAux [] i l) 140 | def pub drop : forall a. Int -> [a] -> [a] 141 | = \i, l -> snd (splitAtAux [] i l) 142 | 143 | def splitWhileAux = 144 | \ c, _, [] -> (reverse c, []) 145 | | c, f, [x | xs] -> 146 | if f x then splitWhileAux (x :: c) f xs 147 | else (reverse c, x :: xs) 148 | #TODO: equivalente a 'span' di haskell. considera di rinominare 149 | and pub splitWhile : forall a. (a -> Bool) -> [a] -> ([a], [a]) 150 | = splitWhileAux [] 151 | 152 | def pub takeWhile : forall a. (a -> Bool) -> [a] -> [a] 153 | = \f, l -> fst (splitWhileAux [] f l) 154 | def pub dropWhile : forall a. (a -> Bool) -> [a] -> [a] 155 | = \f, l -> snd (splitWhileAux [] f l) 156 | 157 | def pub (\\) : forall a. {Eq a} => [a] -> [a] -> [a] 158 | = \l, m -> filter (\x -> all (x !=) m) l 159 | 160 | # TODO?: inits, tails 161 | 162 | # TODO: Tailcall 163 | def pub nub : forall a. {Eq a} => [a] -> [a] = 164 | \ [] -> [] 165 | | [x | xs] -> x :: nub (filter (x !=) xs) 166 | 167 | # TODO: nubBy, union, unionBy, intersect, intersectBy, insert, insertBy, sort, sortBy, sortOn, delete, deleteBy 168 | 169 | def pub concat : forall a. [[a]] -> [a] 170 | = (>>= id) 171 | 172 | def transposeAux = 173 | \ [l] -> map (::[]) l 174 | | [l | ls] -> map2 (::) l (transposeAux ls) 175 | and pub transpose : forall a. [[a]] -> [[a]] = 176 | \ [] -> [] 177 | | ls -> transposeAux ls 178 | # TODO: intercalate, subsequences, permutations, enumerate 179 | 180 | 181 | # NOTE: forse deve andare in un altro file 182 | def pub catMaybes : forall a. [Maybe.Maybe a] -> [a] = 183 | \ [] -> [] 184 | | [Maybe.None | xs] -> catMaybes xs 185 | | [Maybe.Some x | xs] -> x :: catMaybes xs 186 | 187 | -------------------------------------------------------------------------------- /stdlib/maybe.spk: -------------------------------------------------------------------------------- 1 | use Core 2 | 3 | mod Monad "monad.spk" 4 | 5 | data pub Maybe a = None | Some a 6 | 7 | def pub isSome : forall a. Maybe a -> Bool = 8 | \ None -> False 9 | | Some _ -> True 10 | 11 | def pub isNone : forall a. Maybe a -> Bool = 12 | \ None -> True 13 | | Some _ -> False 14 | 15 | def pub fromSome : forall a. Maybe a -> a 16 | = \Some a -> a 17 | 18 | def pub maybe : forall a b. b -> (a -> b) -> Maybe a -> b 19 | = \b, f, m -> put m 20 | | None -> b 21 | | Some a -> f a 22 | 23 | def pub fromMaybe : forall a. a -> Maybe a -> a 24 | = \a -> maybe a id 25 | 26 | inst forall a. {Show a} => Show (Maybe a) { #NOTE: temporaneo, aggiunge parentesi di troppo 27 | def show = 28 | \ None -> "None" 29 | | Some a -> "Some (" ++ show a ++ ")" 30 | } 31 | 32 | inst forall a. {Eq a} => Eq (Maybe a) { 33 | def (==) = 34 | \ None, None -> True 35 | | Some a, Some b -> a == b 36 | | _, _ -> False 37 | def (!=) = 38 | \ None, None -> False 39 | | Some a, Some b -> a != b 40 | | _, _ -> True 41 | } 42 | 43 | inst Functor Maybe { 44 | def fmap = \f, m -> put m 45 | | None -> None 46 | | Some a -> Some (f a) 47 | } 48 | inst Monad Maybe { 49 | def return = \a -> Some a 50 | def bind = \m, f -> put m 51 | | None -> None 52 | | Some a -> f a 53 | } 54 | 55 | inst Monad.MonadPlus Maybe { 56 | def mzero = None 57 | def mplus = \a, b -> put a 58 | | None -> b 59 | | Some _ -> a 60 | } 61 | 62 | inst Monad.MonadFail Maybe { def fail = \_ -> None } 63 | -------------------------------------------------------------------------------- /stdlib/monad.spk: -------------------------------------------------------------------------------- 1 | use Core 2 | 3 | def mapMAux = \procd, f, l -> put l 4 | | [] -> return (reverse procd) 5 | | [x | xs] -> 6 | a <- f x; 7 | mapMAux (a :: procd) f xs 8 | def pub mapM : forall m a b. {Monad m} => (a -> m b) -> [a] -> m [b] 9 | = mapMAux [] 10 | 11 | def pub mapM_ : forall m a b. {Monad m} => (a -> m b) -> [a] -> m () = 12 | \ _, [] -> return () 13 | | f, [x | xs] -> f x >> mapM_ f xs 14 | 15 | rel pub {Monad m} => MonadPlus m = 16 | # LAWS 17 | # 1) MONOID 18 | # 1.1) mplus mzero a = a 19 | # 1.2) mplus a mzero = a 20 | # 1.3) mplus (mplus a b) c = mplus a (mplus b c) 21 | # 2) mzero >>= f = mzero 22 | # 3) m >> mzero = mzero 23 | mzero : forall a. m a, 24 | mplus : forall a. m a -> m a -> m a 25 | 26 | def pub guard : forall m. {MonadPlus m} => Bool -> m () = 27 | \ True -> return () 28 | | False -> mzero 29 | 30 | inst MonadPlus [] { 31 | def mzero = [] 32 | def mplus = (++) 33 | } 34 | 35 | rel pub {Monad m} => MonadFail m = 36 | # Se {MonadPlus m} si può considerare: mzero = fail "mzero" 37 | fail : forall a. String -> m a 38 | 39 | inst MonadFail IO { 40 | def fail = error 41 | } 42 | 43 | rel pub MonadTrans t = lift : forall m a. {Monad m, Monad (t m)} => m a -> t m a 44 | # LAWS 45 | # 1) lift <| return = return 46 | # 2) lift (m >>= f) = lift m >>= (lift <| f) 47 | 48 | # Monade identità, può servire come placeholder nei transformers, ad esempio State s a = StateT s Id a 49 | mod pub Id { 50 | data pub Id a = Id a 51 | def pub runId : forall a. Id a -> a = \Id a -> a 52 | 53 | inst Functor Id { 54 | def fmap = \f, Id a -> Id (f a) 55 | } 56 | inst Monad Id { 57 | def return = Id 58 | def bind = \Id a, f -> f a 59 | } 60 | } 61 | 62 | mod pub State { 63 | # qui la mancanza delle FunctionalDependencies è un problema, in diversi casi l'inferenza è indecidibile 64 | # rel pub {Monad m} => MonadState m s = 65 | # gets : m s, 66 | # puts : s -> m () 67 | 68 | data pub StateT s m a = StateT (s -> m (a, s)) 69 | def pub runStateT : forall s m a. StateT s m a -> s -> m (a, s) 70 | = \StateT sf -> sf 71 | 72 | inst forall s f. {Functor f} => Functor (StateT s f) { 73 | def fmap = \f, StateT sf -> StateT (\s -> 74 | fmap (\(a, s') -> (f a, s')) (sf s) ) 75 | } 76 | 77 | inst forall s m. {Monad m} => Monad (StateT s m) { 78 | def return = \a -> StateT (\s -> return (a, s)) 79 | def bind = \StateT sf, f -> StateT (\s -> 80 | (a, s') <- sf s; 81 | let StateT sf' = f a -> 82 | sf' s' 83 | ) 84 | } 85 | 86 | # inst forall s m. {Monad m} => MonadState (StateT s m) s { 87 | def pub gets = StateT (\s -> return (s, s)) 88 | def pub puts = \s -> StateT(const $ return ((), s)) 89 | # } 90 | 91 | inst forall s. MonadTrans (StateT s) { 92 | def lift = \m -> StateT (\s -> fmap (\a -> (a, s)) m) 93 | } 94 | 95 | typesyn pub State s = StateT s Id.Id 96 | def pub runState : forall s a. (State s) a -> s -> (a, s) = \m, s -> Id.runId (runStateT m s) 97 | } 98 | -------------------------------------------------------------------------------- /stdlib/std.spk: -------------------------------------------------------------------------------- 1 | use pub Core 2 | 3 | mod pub Monad "monad.spk" 4 | 5 | mod pub List "list.spk" 6 | 7 | mod pub Maybe "maybe.spk" 8 | 9 | mod pub Either "either.spk" 10 | 11 | mod pub Text "text.spk" 12 | 13 | mod pub Debug "debug.spk" 14 | 15 | def pub max : forall a. {Ord a} => a -> a -> a 16 | = \a, b -> if a > b then a else b 17 | def pub min : forall a. {Ord a} => a -> a -> a 18 | = \a, b -> if a < b then a else b 19 | 20 | def innerdotdot = \acc, from, to -> 21 | if from > to then acc 22 | else innerdotdot (toEnum to :: acc) from (to - 1) 23 | def pub (..) : forall a. {Enum a} => a -> a -> [a] 24 | = \from, to -> 25 | innerdotdot [] (fromEnum from) (fromEnum to - 1) 26 | def pub (..=) : forall a. {Enum a} => a -> a -> [a] 27 | = \from, to -> 28 | innerdotdot [] (fromEnum from) (fromEnum to) 29 | 30 | #TODO sposta in debug 31 | def pub todo : forall a. a 32 | = error "TODO" 33 | -------------------------------------------------------------------------------- /stdlib/text.spk: -------------------------------------------------------------------------------- 1 | use Core 2 | 3 | mod Maybe "maybe.spk" 4 | use Maybe 5 | mod List "list.spk" 6 | use List 7 | 8 | def pub unwords : [String] -> String = 9 | \ [] -> [] 10 | | ws -> foldl1 (\t, w -> t ++ ' ':: w) ws 11 | 12 | # TODO: Tailcall 13 | def pub unlines : [String] -> String = 14 | \ [] -> [] 15 | | [l | ls] -> l ++ '\n'::unlines ls 16 | 17 | def linesAux = 18 | \ ls, [], "" -> reverse ls 19 | | ls, w, "" -> reverse (reverse w :: ls) 20 | | ls, w, ['\n' | s] -> linesAux (reverse w :: ls) [] s 21 | | ls, w, [c | s] -> linesAux ls (c :: w) s 22 | and pub lines : String -> [String] 23 | = linesAux [] [] 24 | 25 | # TODO: words 26 | 27 | rel pub Read a = readRest : String -> Maybe (a, String) 28 | 29 | def pub readMaybe : forall a. {Read a} => String -> Maybe a 30 | = \s -> 31 | (p, r) <- readRest s; 32 | if null r then return p 33 | else None 34 | 35 | def pub read : forall a. {Read a} => String -> a 36 | = \s -> put readMaybe s 37 | | None -> error "read: no parse" 38 | | Some p -> p 39 | 40 | # TODO: whitespace e paren-insensitive ? Ammetti underscore? 41 | 42 | def readDigits = \s -> 43 | let (ds, r) = splitWhile (\d -> any (d ==) "0123456789") s -> 44 | (map (fromEnum |> (- fromEnum '0')) ds, r) 45 | 46 | def readInt = 47 | let aux = \s -> 48 | let (ds, r) = readDigits s -> 49 | if (length ds != 0) 50 | then Some (foldl (\st, d -> (10 * st) + d) 0 ds, r) 51 | else None 52 | -> 53 | \ ['-' | s] -> fmap (\(n, r) -> (0 - n, r)) (aux s) 54 | | s -> aux s 55 | 56 | #TODO TEST 57 | def readFlt = 58 | let aux = \s -> 59 | let (intds, r) = readDigits s -> 60 | if null intds then None 61 | else let (fltds, r') = 62 | put r 63 | | ['.' | chrs] -> readDigits chrs #TODO None se è vuoto 64 | | _ -> ([], r) 65 | -> Some (fst $ foldl (\(n, p), d -> (n + fromInt d * p, p * 0.1)) (0.0, 10.0 ^ (length intds - 1)) (intds ++ fltds), r') 66 | -> 67 | \ ['-' | s] -> fmap (\(n, r) -> (0.0 - n, r)) (aux s) 68 | | s -> aux s 69 | 70 | def readBool = 71 | \ ['T','r','u','e' | xs] -> Some (True, xs) 72 | | ['F','a','l','s','e' | xs] -> Some (False, xs) 73 | | _ -> None 74 | 75 | inst Read Int { 76 | def readRest = readInt 77 | } 78 | 79 | inst Read Flt { 80 | def readRest = readFlt 81 | } 82 | 83 | inst Read Bool { 84 | def readRest = readBool 85 | } 86 | -------------------------------------------------------------------------------- /vim/ftdetect/spinnaker.vim: -------------------------------------------------------------------------------- 1 | au BufNewFile,BufRead *.spk set filetype=spinnaker 2 | -------------------------------------------------------------------------------- /vim/syntax/spinnaker.vim: -------------------------------------------------------------------------------- 1 | " Syntax highlighting di base per spinnaker (file .spk) 2 | 3 | if version < 600 4 | syn clear 5 | elseif exists("b:current_syntax") 6 | finish 7 | endif 8 | 9 | syn keyword spinnakerTopDef mod data def and ext rel inst typesyn 10 | syn keyword spinnakerModKeyword use pub 11 | syn keyword spinnakerKeyword let put forall 12 | syn keyword spinnakerConditional if then else 13 | syn match spinnakerOperator "\v[:!$%&\*\+/\-<=>\?@\\^|~.]+" 14 | syn match spinnakerNumber "\v[0-9][0-9_]*" 15 | syn match spinnakerFloat "\v[0-9][0-9_]*\.[0-9_]+" 16 | syn keyword spinnakerTodo TODO FIXME NOTE contained 17 | syn match spinnakerLineComment "#.*$" contains=spinnakerTodo 18 | syn match spinnakerIdentifier "[_a-z][a-zA-Z0-9_']*" 19 | syn match spinnakerChar "'[^'\\]'\|'\\.'\|'\\u[0-9a-fA-F]\{4}'" 20 | syn region spinnakerString start=+"+ skip=+\\\\\|\\"+ end=+"+ 21 | \ contains=@Spell 22 | syn match spinnakerType "[A-Z][a-zA-Z0-9_']*" 23 | syn region spinnakerParens matchgroup=spinnakerDelimiter start="(" end=")" contains=TOP,@Spell 24 | syn region spinnakerBrackets matchgroup=spinnakerDelimiter start="\[" end="]" contains=TOP,@Spell 25 | syn region spinnakerBlock matchgroup=spinnakerDelimiter start="{" end="}" contains=TOP,@Spell 26 | syn match spinnakerSeparator "[,;]" 27 | 28 | highlight def link spinnakerIdentifier Normal 29 | highlight def link spinnakerType Type 30 | highlight def link spinnakerTopDef Define 31 | highlight def link spinnakerModKeyword Include 32 | highlight def link spinnakerKeyword Keyword 33 | highlight def link spinnakerConditional Conditional 34 | highlight def link spinnakerNumber Number 35 | highlight def link spinnakerFloat Float 36 | highlight def link spinnakerOperator Operator 37 | highlight def link spinnakerTodo Todo 38 | highlight def link spinnakerLineComment Comment 39 | highlight def link spinnakerChar String 40 | highlight def link spinnakerString String 41 | highlight def link spinnakerDelimiter Delimiter 42 | highlight def link spinnakerSeparator Delimiter 43 | 44 | let b:current_syntax = "spinnaker" 45 | " in A.B.c A e B devono essere blu (Include) 46 | " highlight per |, ->, \ 47 | --------------------------------------------------------------------------------