zwirn 0.1.0.0 → 0.2.2.0
raw patch · 91 files changed
+9021/−3727 lines, 91 filesdep +aesondep +confererdep +conferer-yamldep −prettydep −zwirn-coredep ~basedep ~containersdep ~exceptionsnew-component:exe:zwirn-docsnew-component:exe:zwirn-plotnew-component:exe:zwirnzi
Dependencies added: aeson, conferer, conferer-yaml, criterion, deepseq, directory, easyplot, file-io, haskeline, hmt, lens, lsp, prettyprinter, pure-noise, random, stm, tasty, tasty-hunit, tasty-quickcheck, tasty-smallcheck, text-rope, utf8-string, zwirn
Dependencies removed: pretty, zwirn-core
Dependency ranges changed: base, containers, exceptions, filepath, network
Files
- README.md +3/−7
- app/zwirn-docs/Main.hs +45/−0
- app/zwirn-plot/Main.hs +65/−0
- app/zwirnzi/CI/Backend.hs +61/−0
- app/zwirnzi/CI/Config.hs +193/−0
- app/zwirnzi/CI/Setup.hs +80/−0
- app/zwirnzi/LSP/Diagnostic.hs +88/−0
- app/zwirnzi/LSP/Handlers/Action.hs +37/−0
- app/zwirnzi/LSP/Handlers/Command.hs +79/−0
- app/zwirnzi/LSP/Handlers/Completion.hs +46/−0
- app/zwirnzi/LSP/Handlers/File.hs +58/−0
- app/zwirnzi/LSP/Handlers/Hover.hs +30/−0
- app/zwirnzi/LSP/Handlers/InlayHint.hs +37/−0
- app/zwirnzi/LSP/Main.hs +81/−0
- app/zwirnzi/LSP/Util.hs +40/−0
- app/zwirnzi/Main.hs +47/−0
- bench/Core/Main.hs +41/−0
- src/Zwirn/Language.hs +0/−44
- src/Zwirn/Language/Block.hs +0/−44
- src/Zwirn/Language/Builtin/Internal.hs +0/−29
- src/Zwirn/Language/Builtin/Parameters.hs +0/−167
- src/Zwirn/Language/Builtin/Prelude.hs +0/−616
- src/Zwirn/Language/Compiler.hs +0/−425
- src/Zwirn/Language/Environment.hs +0/−44
- src/Zwirn/Language/Evaluate.hs +0/−12
- src/Zwirn/Language/Evaluate/Convert.hs +0/−179
- src/Zwirn/Language/Evaluate/Expression.hs +0/−74
- src/Zwirn/Language/Evaluate/Internal.hs +0/−75
- src/Zwirn/Language/Evaluate/SKI.hs +0/−102
- src/Zwirn/Language/Lexer.x +0/−395
- src/Zwirn/Language/Parser.y +0/−375
- src/Zwirn/Language/Pretty.hs +0/−121
- src/Zwirn/Language/Rotate.hs +0/−126
- src/Zwirn/Language/Simple.hs +0/−104
- src/Zwirn/Language/Syntax.hs +0/−95
- src/Zwirn/Language/TypeCheck/Constraint.hs +0/−152
- src/Zwirn/Language/TypeCheck/Infer.hs +0/−199
- src/Zwirn/Language/TypeCheck/Types.hs +0/−88
- src/Zwirn/Stream.hs +0/−229
- src/zwirn-core/Zwirn/Core/Cord.hs +67/−0
- src/zwirn-core/Zwirn/Core/Core.hs +133/−0
- src/zwirn-core/Zwirn/Core/Lib/Conditional.hs +91/−0
- src/zwirn-core/Zwirn/Core/Lib/Cord.hs +188/−0
- src/zwirn-core/Zwirn/Core/Lib/Core.hs +81/−0
- src/zwirn-core/Zwirn/Core/Lib/Map.hs +132/−0
- src/zwirn-core/Zwirn/Core/Lib/Modulate.hs +169/−0
- src/zwirn-core/Zwirn/Core/Lib/Number.hs +143/−0
- src/zwirn-core/Zwirn/Core/Lib/Random.hs +153/−0
- src/zwirn-core/Zwirn/Core/Lib/State.hs +52/−0
- src/zwirn-core/Zwirn/Core/Lib/Structure.hs +228/−0
- src/zwirn-core/Zwirn/Core/Lib/Text.hs +29/−0
- src/zwirn-core/Zwirn/Core/Query.hs +75/−0
- src/zwirn-core/Zwirn/Core/Time.hs +89/−0
- src/zwirn-core/Zwirn/Core/Tree.hs +221/−0
- src/zwirn-core/Zwirn/Core/Types.hs +91/−0
- src/zwirn-lang/Zwirn/Language.hs +44/−0
- src/zwirn-lang/Zwirn/Language/Block.hs +109/−0
- src/zwirn-lang/Zwirn/Language/Builtin/Internal.hs +29/−0
- src/zwirn-lang/Zwirn/Language/Builtin/Parameters.hs +470/−0
- src/zwirn-lang/Zwirn/Language/Builtin/Prelude.hs +1032/−0
- src/zwirn-lang/Zwirn/Language/Compiler.hs +526/−0
- src/zwirn-lang/Zwirn/Language/Environment.hs +47/−0
- src/zwirn-lang/Zwirn/Language/Evaluate.hs +12/−0
- src/zwirn-lang/Zwirn/Language/Evaluate/Convert.hs +193/−0
- src/zwirn-lang/Zwirn/Language/Evaluate/Expression.hs +81/−0
- src/zwirn-lang/Zwirn/Language/Evaluate/Internal.hs +263/−0
- src/zwirn-lang/Zwirn/Language/Evaluate/SKI.hs +104/−0
- src/zwirn-lang/Zwirn/Language/LSP/Diagnostics.hs +42/−0
- src/zwirn-lang/Zwirn/Language/LSP/Eval.hs +20/−0
- src/zwirn-lang/Zwirn/Language/LSP/Hover.hs +57/−0
- src/zwirn-lang/Zwirn/Language/LSP/InlayHints.hs +75/−0
- src/zwirn-lang/Zwirn/Language/Lexer.x +383/−0
- src/zwirn-lang/Zwirn/Language/Location.hs +93/−0
- src/zwirn-lang/Zwirn/Language/Macro.hs +100/−0
- src/zwirn-lang/Zwirn/Language/Parser.y +400/−0
- src/zwirn-lang/Zwirn/Language/Pretty.hs +174/−0
- src/zwirn-lang/Zwirn/Language/Rotate.hs +147/−0
- src/zwirn-lang/Zwirn/Language/Simple.hs +110/−0
- src/zwirn-lang/Zwirn/Language/Syntax.hs +156/−0
- src/zwirn-lang/Zwirn/Language/TypeCheck/Constraint.hs +167/−0
- src/zwirn-lang/Zwirn/Language/TypeCheck/Infer.hs +194/−0
- src/zwirn-lang/Zwirn/Language/TypeCheck/Types.hs +100/−0
- src/zwirn-lang/Zwirn/Stream/Handshake.hs +29/−0
- src/zwirn-lang/Zwirn/Stream/Listen.hs +48/−0
- src/zwirn-lang/Zwirn/Stream/Process.hs +164/−0
- src/zwirn-lang/Zwirn/Stream/Target.hs +54/−0
- src/zwirn-lang/Zwirn/Stream/Types.hs +52/−0
- src/zwirn-lang/Zwirn/Stream/UI.hs +203/−0
- test/zwirn-core/Main.hs +173/−0
- test/zwirn-lang/Main.hs +15/−0
- zwirn.cabal +182/−25
README.md view
@@ -6,17 +6,13 @@ zwirn is an experiment in making the pattern language [TidalCycles](https://tidalcycles.org/) into a small functional language of it's own. while zwirn's internals are quite different from Tidal's, zwirns design owes almost everything to tidals design by [Alex McLean](https://slab.org/). -the internal representation of signals of time was implemented together (and parallel) with [Julian Rohrhuber](https://wertlos.org/~rohrhuber/), in an effort to port tidal to SuperCollider. This can be found in the seperate haskell library [zwirn-core](https://lab.al0.de/martin/zwirn-core).+the internal representation of signals of time was implemented together (and parallel) with [Julian Rohrhuber](https://wertlos.org/~rohrhuber/), in an effort to port tidal to SuperCollider. This can be found in the [zwirn-core](https://codeberg.org/uzu/zwirn/src/branch/main/src/zwirn-core/Zwirn/Core) sublibrary. the implementation of the compiler is inspired by the excellent [Write You a Haskell](https://github.com/sdiehl/write-you-a-haskell) by Stephen Diehl. ## Installing zwirn -There are currently two ways to play with zwirn:- * [zwirnzi](https://github.com/polymorphicengine/zwirnzi) - the zwirn zompiler-interpreter- * [zwirn-loom](https://github.com/polymorphicengine/zwirn-loom) - a compiler-interpreter for zwirn with an experimental editor interface--Zwirnzi is meant to serve as a way to play with zwirn in an editor of your choice, currently there are no official editor extensions - but it shouldn't be too hard to implement one. This means that zwirn-loom is the best way to play with zwirn at the moment.+tba ## Limitations @@ -26,4 +22,4 @@ ## Documentation -documentation for zwirn is still in progress and available [here](https://github.com/polymorphicengine/zwirn/wiki), feel free to drop me a message if you have any questions.+documentation for zwirn is still in progress and available [here](https://codeberg.org/uzu/zwirn/wiki), feel free to drop me a message if you have any questions.
+ app/zwirn-docs/Main.hs view
@@ -0,0 +1,45 @@+module Main where++import Data.List (intercalate)+import qualified Data.Map as Map+import Data.Text (Text, unpack)+import Data.Text.Lazy as T (pack)+import Data.Text.Lazy.Encoding as T+import System.Directory.OsPath+import System.File.OsPath as F+import System.OsPath hiding (unpack)+import Zwirn.Language.Builtin.Prelude+import Zwirn.Language.Environment+import Zwirn.Language.Pretty++main :: IO ()+main = do+ curr <- getCurrentDirectory+ path <- (curr <>) <$> encodeUtf "/zwirn-docs.md"+ decoded <- decodeUtf path+ putStrLn ("Generating documentation in: " ++ decoded)+ F.writeFile path (T.encodeUtf8 $ T.pack documentation)++documentation :: String+documentation =+ "# Zwirn\n\n"+ ++ intercalate+ "\n\n"+ [ documentSection "Core Functions" coreFunctions,+ documentSection "Signals" signals,+ documentSection "Randomness" randomFunctions,+ documentSection "Manipulating Time" timeFunctions,+ documentSection "Manipulating Structure" structureFunctions,+ documentSection "Conditionals" conditionalFunctions,+ documentSection "Cords / Layers" cordFunctions,+ documentSection "Functions on Maps" mapFunctions+ ]++documentSection :: String -> Map.Map Text AnnotatedExpression -> String+documentSection header ma = "## " ++ header ++ "\n\n" ++ intercalate "\n\n" (map documentOne as)+ where+ as = Map.toList ma++documentOne :: (Text, AnnotatedExpression) -> String+documentOne (name, Annotated _ ty (Just desc)) = "```" ++ unpack name ++ " :: " ++ unpack (ppscheme ty) ++ "```\n\n" ++ unpack desc+documentOne (name, Annotated _ ty Nothing) = "```" ++ unpack name ++ " :: " ++ unpack (ppscheme ty) ++ "```"
+ app/zwirn-plot/Main.hs view
@@ -0,0 +1,65 @@+{-# OPTIONS_GHC -Wno-orphans #-}++module Main where++import Control.Monad (void)+import Control.Monad.Identity (Identity (..))+import Graphics.EasyPlot+import Zwirn.Core.Lib.Modulate+import Zwirn.Core.Lib.Number (range, sine)+import Zwirn.Core.Query+import Zwirn.Core.Time+import Zwirn.Core.Types++-- | the most simple zwirn - no stacks, no state, no value+type Zwirn = ZwirnT Identity () () ()++-- | silence will just throw an error+instance HasSilence Identity where+ silence = error "no silence"++-- | interpret a zwirn as a transformation of time+fromZwirn :: Zwirn -> Double -> Double+fromZwirn z d = fromRational $ tTime $ time $ fst $ unId $ unzwirn z (Time (toRational d) 1) ()+ where+ unId (Identity a) = a++-- | extract the zeroes of the fractional part of the inner time of a zwirn+toData :: Double -> Double -> Zwirn -> [(Double, Double)]+toData st en z = map (\(t, Value _ t2 _, _) -> (fromRational $ tTime t, fromRational $ tTime t2)) xs+ where+ (xs, _) = findAllBreakpoints 0.005 (Time (toRational st) 1) (Time (toRational en) 1) () z++-- | plots both the zwirns breakpoints and inner time+plotZwirn :: Zwirn -> [Graph2D Double Double]+plotZwirn z = [Function2D [] [Range 0 4, Step 0.005] (fromZwirn z), Data2D [Color Red] [] (toData 0 4 z)]++basic :: Zwirn+basic = pure ()++fastcatEx :: Zwirn+fastcatEx = fastcat [basic, basic]++timeLoopEx :: Zwirn+timeLoopEx = timeloop (pure 0.5) basic++zoomEx :: Zwirn+zoomEx = zoom (pure 0.5) (pure 1) fastcatEx++loopEx :: Zwirn+loopEx = loop (pure 0.5) (pure 1) fastcatEx++ribbonEx :: Zwirn+ribbonEx = ribbon (pure 0.5) (pure 0.75) fastcatEx++swingByEx :: Zwirn+swingByEx = swingBy (pure 0.5) (pure 4) (fast (pure 8) $ pure ())++revEx :: Zwirn+revEx = rev basic++weird :: Zwirn+weird = fast (range (pure 1) (pure 2) sine) basic++main :: IO ()+main = void $ plot X11 $ plotZwirn basic
+ app/zwirnzi/CI/Backend.hs view
@@ -0,0 +1,61 @@+module CI.Backend where++{-+ Backend.hs - Implements the interaction between the compiler-interpreter and the editor+ Copyright (C) 2023, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Control.Monad.State (lift, liftIO)+import Data.Text (pack)+import qualified Data.Text as T+import System.Console.Haskeline+import Zwirn.Language.Compiler+import Zwirn.Language.LSP.Eval (evalBlockAt)++type ZwirnCI = InputT CI++runZwirnCI :: Environment -> ZwirnCI () -> IO ()+runZwirnCI env x = do+ ci <- runCI env (runInputT defaultSettings x)+ case ci of+ Left (CIError err envv) -> print err >> runZwirnCI envv x+ Right _ -> return ()++evalInput :: ZwirnCI ()+evalInput = do+ mayinput <- getInputLine ">> "+ case mayinput of+ Just ":{" -> do+ input <- multiLineLoop ""+ ((_, ms), _) <- lift $ evalBlockAt (pack input) 1+ mapM_ (liftIO . putStrLn . T.unpack) ms+ Just input -> do+ x <- lift $ compilerInterpreterBasic (pack input)+ case x of+ OutMessage m -> liftIO $ putStrLn $ T.unpack m+ _ -> return ()+ Nothing -> return ()++multiLineLoop :: String -> ZwirnCI String+multiLineLoop s = do+ mayinput <- getInputLine ""+ case mayinput of+ Just ":}" -> return s+ Just t -> do multiLineLoop (s ++ "\n" ++ t)+ _ -> return ""++evalInputLoop :: ZwirnCI ()+evalInputLoop = evalInput >> evalInputLoop
+ app/zwirnzi/CI/Config.hs view
@@ -0,0 +1,193 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE MultilineStrings #-}+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -Wno-orphans #-}++module CI.Config where++{-+ CommandLine.hs - configuration+ Copyright (C) 2023, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Conferer as Conf+import Conferer.Source.CLIArgs as Cli+import Conferer.Source.Env as Env+import Conferer.Source.Yaml as Yaml+import Control.Monad (unless)+import qualified Data.ByteString.Lazy.UTF8 as BL+import qualified Data.Text as T+import GHC.Generics (Generic)+import qualified Sound.Tidal.Clock as Clock (ClockConfig (..), defaultConfig)+import System.Directory.OsPath+import System.File.OsPath as F+import System.OsPath+import qualified Zwirn.Language.Compiler as Compiler+import qualified Zwirn.Stream.Target as Stream+import qualified Zwirn.Stream.Types as Stream++data ClockConfig = ClockConfig+ { clockConfigQuantum :: Double,+ clockConfigBeatsPerCycle :: Double,+ clockConfigFrameTimespan :: Double,+ clockConfigEnableLink :: Bool,+ clockConfigSkipTicks :: Int,+ clockConfigProcessAhead :: Double+ }+ deriving (Show, Generic)++data TargetConfig = TargetConfig+ { targetConfigName :: T.Text,+ targetConfigOSCPath :: T.Text,+ targetConfigBusOSCPath :: T.Text,+ targetConfigAddress :: String,+ targetConfigPort :: Int,+ targetConfigBusPort :: Maybe Int+ }+ deriving (Show, Generic)++data StreamConfig = StreamConfig+ { streamConfigTargets :: [TargetConfig],+ streamConfigDefaultTarget :: T.Text,+ streamConfigLocalPort :: Int,+ streamConfigPrecision :: Rational,+ streamConfigClock :: ClockConfig+ }+ deriving (Show, Generic)++data CiConfig = CiConfig+ { ciConfigBootPath :: FilePath,+ ciConfigListener :: Bool,+ ciConfigCli :: Bool,+ ciConfigOverwriteBuiltin :: Bool,+ ciConfigDynamicTypes :: Bool+ }+ deriving (Generic)++data FullConfig = FullConfig+ { fullConfigCi :: CiConfig,+ fullConfigClock :: ClockConfig,+ fullConfigStream :: StreamConfig+ }+ deriving (Generic)++instance DefaultConfig TargetConfig where+ configDef = TargetConfig "superdirt" "/dirt/play" "/c_set" "127.0.0.1" 57120 (Just 57110)++instance DefaultConfig CiConfig where+ configDef = CiConfig "" False False False False++instance DefaultConfig StreamConfig where+ configDef = StreamConfig [configDef] "superdirt" 2323 0.005 configDef++instance DefaultConfig ClockConfig where+ configDef = fromClock Clock.defaultConfig++instance DefaultConfig FullConfig where+ configDef = FullConfig configDef configDef configDef++instance FromConfig TargetConfig++instance FromConfig CiConfig++instance FromConfig StreamConfig++instance FromConfig ClockConfig++instance FromConfig FullConfig++getConfig :: IO Conf.Config+getConfig = do+ home <- getHomeDirectory+ configDirPath <- (home <>) <$> encodeUtf "/.config/zwirnzi/"+ path <- (home <>) <$> encodeUtf "/.config/zwirnzi/config.yaml"+ createDirectoryIfMissing True configDirPath+ exists <- doesFileExist path+ unless exists (F.writeFile path defaultConfigFile)+ decoded <- decodeUtf path+ mkConfig'+ []+ [ Cli.fromConfig,+ Env.fromConfig "zwirnzi",+ Yaml.fromFilePath decoded+ ]++fromClock :: Clock.ClockConfig -> ClockConfig+fromClock (Clock.ClockConfig a b c d e f) = ClockConfig (realToFrac a) (realToFrac b) c d (fromIntegral e) f++toClock :: ClockConfig -> Clock.ClockConfig+toClock (ClockConfig a b c d e f) = Clock.ClockConfig (realToFrac a) (realToFrac b) c d (fromIntegral e) f++toTarget :: TargetConfig -> Stream.TargetConfig+toTarget (TargetConfig a b c d e f) = Stream.TargetConfig a b c d e f++toStream :: StreamConfig -> Stream.StreamConfig+toStream (StreamConfig a b c d e) = Stream.StreamConfig (map toTarget a) b c d (toClock e)++toCiConfig :: CiConfig -> Compiler.CiConfig+toCiConfig (CiConfig _ _ _ x y) = Compiler.CiConfig x y++configPath :: IO String+configPath = do+ home <- getHomeDirectory+ path <- (home <>) <$> encodeUtf "/.config/zwirnzi/config.yaml"+ exists <- doesFileExist path+ decoded <- decodeUtf path+ if exists then return decoded else return "Config file not found!"++resetConfig :: IO String+resetConfig = do+ home <- getHomeDirectory+ configDirPath <- (home <>) <$> encodeUtf "/.config/zwirnzi/"+ path <- (home <>) <$> encodeUtf "/.config/zwirnzi/config.yaml"+ createDirectoryIfMissing True configDirPath+ F.writeFile path defaultConfigFile+ return "Restored default config."++defaultConfigFile :: BL.ByteString+defaultConfigFile =+ BL.fromString+ """+ ci:+ listener: true+ bootpath: ""+ overwritebuiltin: false+ dynamictypes: false+ stream:+ targets:+ - name: "superdirt"+ oscpath: "/dirt/play"+ busoscpath: "/c_set"+ address: "127.0.0.1"+ port: 57120+ busport: 57110+ defaulttarget: "superdirt"+ localport: 52323+ precision: 0.005+ clock:+ quantum: 4+ beatspercycle: 4+ frametimespan: 0.05+ enablelink: false+ skipticks: 10+ processahead: 0.3+ """++getFile :: String -> IO String+getFile p = do+ path <- encodeUtf p+ f <- F.readFile path+ return $ BL.toString f
+ app/zwirnzi/CI/Setup.hs view
@@ -0,0 +1,80 @@+module CI.Setup (setup) where++{-+ Setup.hs - setup of the various components of the backend+ Copyright (C) 2023, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import CI.Config as C+import Control.Concurrent (forkIO)+import Control.Monad (void, when)+import qualified Data.Map as Map+import qualified Data.Text as T+import System.Directory.OsPath+import System.IO (hPutStrLn, stderr)+import System.OsPath+import Zwirn.Language.Builtin.Prelude+import Zwirn.Language.Compiler as Compiler+import Zwirn.Language.Macro (defaultMacroMap)+import Zwirn.Stream.Handshake (sendHandshake)+import Zwirn.Stream.Listen+import Zwirn.Stream.Target (Target (..))+import Zwirn.Stream.Types+import Zwirn.Stream.UI++setup :: FullConfig -> IO Environment+setup config = do+ str <- setupStream config+ when (ciConfigListener $ fullConfigCi config) (setupListener str)+ let initE = getInitialEnv (toCiConfig $ fullConfigCi config) str+ checkBoot (fullConfigCi config) initE++setupStream :: FullConfig -> IO Stream+setupStream config = startStream (toStream $ fullConfigStream config)++setupListener :: Stream -> IO ()+setupListener str = do+ case Map.lookup "superdirt" (sTargetMap str) of+ Nothing -> return ()+ Just (Target _ _ addr _) -> sendHandshake (sLocal str) addr+ void (forkIO $ listen str)++getInitialEnv :: Compiler.CiConfig -> Stream -> Environment+getInitialEnv config str = Environment str (builtinEnvironmentWithStream str) (Just $ ConfigEnv configPath resetConfig) config defaultMacroMap++checkBoot :: C.CiConfig -> Environment -> IO Environment+checkBoot (C.CiConfig "" _ _ _ _) env = hPutStrLn stderr "Starting without Bootfile." >> return env+checkBoot (C.CiConfig path _ _ _ _) env = do+ ospath <- encodeUtf path+ isfile <- doesFileExist ospath+ ps <-+ if isfile+ then return $ decodeUtf ospath+ else do+ isfolder <- doesDirectoryExist ospath+ if isfolder+ then do+ pss <- listDirectory ospath+ fs <- mapM decodeUtf pss+ return $ map (\f -> path ++ "/" ++ f) fs+ else return []+ res <- runCI env (compilerInterpreterBoot $ map T.pack ps)+ case res of+ Left (CIError err newEnv) -> hPutStrLn stderr ("Error in Bootfile: " ++ show err) >> return newEnv+ Right newEnv ->+ if ps /= []+ then hPutStrLn stderr ("Successfully loaded Bootfiles from " ++ path) >> return newEnv+ else hPutStrLn stderr ("No Bootfiles found at " ++ path) >> return newEnv
+ app/zwirnzi/LSP/Diagnostic.hs view
@@ -0,0 +1,88 @@+module LSP.Diagnostic where++import qualified Data.Text as T+import LSP.Util+import Language.LSP.Diagnostics+import Language.LSP.Protocol.Types ()+import qualified Language.LSP.Protocol.Types as LSP+import Language.LSP.Server+import Zwirn.Language.Compiler+import Zwirn.Language.Location (Located (..), SrcLoc (..))+import Zwirn.Language.Rotate (RotationError (..))+import Zwirn.Language.TypeCheck.Constraint (TypeError (..))+import Zwirn.Language.TypeCheck.Types (Predicate (..))++type TextDocumentVersion = LSP.Int32++maxDiagnostics :: Int+maxDiagnostics = 16++publishDiags :: LSP.NormalizedUri -> Maybe TextDocumentVersion -> [LSP.Diagnostic] -> LSP ()+publishDiags doc version = publishDiagnostics maxDiagnostics doc version . partitionBySource++refreshDiagnostics :: LSP ()+refreshDiagnostics = flushDiagnosticsBySource maxDiagnostics (Just "zwirn-lsp")++makeErrorDiagnostic :: ErrorType -> [LSP.Diagnostic]+makeErrorDiagnostic (ParseErr msg r) =+ pure $+ LSP.Diagnostic+ (toLSP r)+ (Just LSP.DiagnosticSeverity_Error) -- severity+ Nothing -- code+ Nothing -- code description+ (Just "zwirn-lsp") -- source+ (T.pack msg)+ Nothing -- tags+ (Just []) -- related info+ Nothing -- data+makeErrorDiagnostic err@(RotErr (RotationError (SrcLoc r))) =+ pure $+ LSP.Diagnostic+ (toLSP r)+ (Just LSP.DiagnosticSeverity_Error)+ Nothing+ Nothing+ (Just "zwirn-lsp")+ (T.pack $ show err)+ Nothing+ (Just [])+ Nothing+makeErrorDiagnostic err@(TypeErr (NoInstance (Located (SrcLoc r) (IsIn _ _)))) =+ pure $+ LSP.Diagnostic+ (toLSP r)+ (Just LSP.DiagnosticSeverity_Error)+ Nothing+ Nothing+ (Just "zwirn-lsp")+ (T.pack $ show err)+ Nothing+ (Just [])+ Nothing+makeErrorDiagnostic err@(TypeErr (UnboundVariable (Located (SrcLoc r) _))) =+ pure $+ LSP.Diagnostic+ (toLSP r)+ (Just LSP.DiagnosticSeverity_Error)+ Nothing+ Nothing+ (Just "zwirn-lsp")+ (T.pack $ show err)+ Nothing+ (Just [])+ Nothing+makeErrorDiagnostic err@(TypeErr (UnificationFail (Located (SrcLoc r) _))) =+ pure $+ LSP.Diagnostic+ (toLSP r)+ (Just LSP.DiagnosticSeverity_Error)+ Nothing+ Nothing+ (Just "zwirn-lsp")+ (T.pack (show err))+ Nothing+ (Just [])+ Nothing+makeErrorDiagnostic (ManyErr errs) = concatMap makeErrorDiagnostic errs+makeErrorDiagnostic _ = []
+ app/zwirnzi/LSP/Handlers/Action.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE RecordWildCards #-}++module LSP.Handlers.Action where++import Control.Lens ((^.))+import qualified Data.Aeson as J+import LSP.Util+import qualified Language.LSP.Protocol.Lens as LSP+import qualified Language.LSP.Protocol.Message as LSP+import Language.LSP.Protocol.Types ()+import qualified Language.LSP.Protocol.Types as LSP+import Language.LSP.Server (Handlers, requestHandler)++codeActionHandler :: Handlers LSP+codeActionHandler = requestHandler LSP.SMethod_TextDocumentCodeAction $ \req responder -> do+ let LSP.CodeActionParams _ _ doc range _ = req ^. LSP.params+ -- uri = doc ^. LSP.uri+ -- debug (T.pack $ "Processing custom code action" ++ show range)+ let title = "Evaluate zwirn code at cursor"+ -- NOTE: the cmd needs to be registered via the InitializeResponse message. See lspOptions above+ cmd = "zwirn-eval"+ args =+ [ J.toJSON doc,+ J.toJSON range+ ]+ command = LSP.Command title cmd (Just args)+ let action = LSP.CodeAction {..}+ where+ _title = "eval"+ _kind = Just $ LSP.CodeActionKind_Custom "zwirn"+ _diagnostics = Nothing+ _isPreferred = Nothing+ _disabled = Nothing+ _edit = Nothing+ _command = Just command+ _data_ = Nothing+ responder $ Right $ LSP.InL $ LSP.InR <$> [action]
+ app/zwirnzi/LSP/Handlers/Command.hs view
@@ -0,0 +1,79 @@+module LSP.Handlers.Command where++import Control.Concurrent.MVar+import Control.Lens ((^.))+import Control.Monad (void)+import Control.Monad.IO.Class+import qualified Data.Aeson as J+import qualified Data.Aeson.Types as J+import qualified Data.Map as Map+import qualified Data.Text as T+import LSP.Diagnostic+import LSP.Handlers.InlayHint+import LSP.Util+import qualified Language.LSP.Protocol.Lens as LSP+import qualified Language.LSP.Protocol.Message as LSP+import Language.LSP.Protocol.Types ()+import qualified Language.LSP.Protocol.Types as LSP+import Language.LSP.Server (Handlers, getVirtualFile, requestHandler, sendRequest)+import Language.LSP.VFS+import Zwirn.Language.Compiler+import Zwirn.Language.LSP.Diagnostics+import Zwirn.Language.LSP.Eval+import Zwirn.Language.Macro (CodeEdit (..))++-- TODO: make this more efficient?+-- currently the document is parsed three times, once for executing code, once for validating it after and once for generating new inlay hints ...+execCommandHandler :: MVar Environment -> Handlers LSP+execCommandHandler envmv = requestHandler LSP.SMethod_WorkspaceExecuteCommand $ \req responder -> do+ debug "Processing a workspace/executeCommand request"+ let params = req ^. LSP.params+ -- name = params ^. LSP.command+ margs = params ^. LSP.arguments++ -- debug ("The arguments are: " <> show margs)+ responder (Right $ LSP.InR LSP.Null) -- respond to the request+ env <- liftIO $ takeMVar envmv+ case getEvalArgs margs of+ Nothing -> liftIO $ putMVar envmv env+ Just (docid, LSP.Range begin _) -> do+ let uri = docid ^. LSP.uri+ doc = LSP.toNormalizedUri uri+ mdoc <- getVirtualFile doc+ case mdoc of+ Just vf@(VirtualFile _ version _) -> do+ mci <- liftIO $ runCI env (evalBlockAt (virtualFileText vf) ((\(LSP.Position l _) -> fromIntegral l) begin))+ case mci of+ Right ((edits, msgs), newEnv) -> do+ liftIO $ putMVar envmv newEnv+ if null msgs then sendInfo "OK" else mapM_ sendInfo msgs+ makeEdits edits uri+ errs <- liftIO $ validateCode newEnv (virtualFileText vf)+ case errs of+ Just err -> publishDiags doc (Just (fromIntegral version)) (makeErrorDiagnostic err)+ Nothing -> refreshDiagnostics+ refreshHints+ Left err -> do+ liftIO $ putMVar envmv env+ sendError (T.pack $ show err)+ Nothing -> return ()+ return ()++getEvalArgs :: Maybe [J.Value] -> Maybe (LSP.TextDocumentIdentifier, LSP.Range)+getEvalArgs (Just [t, x]) = do+ doc <- J.parseMaybe J.parseJSON t+ pos <- J.parseMaybe J.parseJSON x+ return (doc, pos)+getEvalArgs _ = Nothing++makeEdits :: [CodeEdit] -> LSP.Uri -> LSP ()+makeEdits edits uri = do+ let lspedits = map toLSPEdit edits+ par =+ LSP.ApplyWorkspaceEditParams (Just "Howdy edit") $+ LSP.WorkspaceEdit (Just (Map.singleton uri lspedits)) Nothing Nothing++ void $ sendRequest LSP.SMethod_WorkspaceApplyEdit par (const (pure ()))++toLSPEdit :: CodeEdit -> LSP.TextEdit+toLSPEdit (CodeEdit pos x) = LSP.TextEdit (toLSP pos) x
+ app/zwirnzi/LSP/Handlers/Completion.hs view
@@ -0,0 +1,46 @@+module LSP.Handlers.Completion where++import Control.Concurrent (MVar)+import Control.Concurrent.MVar (readMVar)+import Control.Monad.IO.Class (liftIO)+import qualified Data.Map as Map+import qualified Data.Text as T+import LSP.Util (LSP)+import Language.LSP.Protocol.Message (SMethod (..))+import qualified Language.LSP.Protocol.Types as LSP+import Language.LSP.Server (Handlers, requestHandler)+import Zwirn.Language (Environment (..), InterpreterEnv (..))+import Zwirn.Language.Environment (AnnotatedExpression (..))+import Zwirn.Language.Pretty (ppscheme)+import Zwirn.Language.TypeCheck.Types (Scheme)++completionHandler :: MVar Environment -> Handlers LSP+completionHandler envMV = requestHandler SMethod_TextDocumentCompletion $ \_req resp -> do+ env <- liftIO $ readMVar envMV++ let ks = map (\(k, Annotated _ s _) -> (k, s)) $ Map.toList $ eExpressions $ intEnv env+ res = LSP.CompletionList True Nothing (map mkCompletionItem ks)+ resp $ Right $ LSP.InR $ LSP.InL res+ where+ mkCompletionItem :: (T.Text, Scheme) -> LSP.CompletionItem+ mkCompletionItem (k, s) =+ LSP.CompletionItem+ k+ Nothing+ (Just LSP.CompletionItemKind_Constant)+ Nothing+ (Just $ ppscheme s)+ Nothing+ Nothing+ Nothing+ Nothing+ Nothing+ Nothing+ Nothing+ Nothing+ Nothing+ Nothing+ Nothing+ Nothing+ Nothing+ Nothing
+ app/zwirnzi/LSP/Handlers/File.hs view
@@ -0,0 +1,58 @@+module LSP.Handlers.File where++import Control.Concurrent.MVar+import Control.Lens (to, (^.))+import Control.Monad.IO.Class+import LSP.Diagnostic+import LSP.Util+import qualified Language.LSP.Protocol.Lens as LSP+import Language.LSP.Protocol.Message+import qualified Language.LSP.Protocol.Message as LSP+import Language.LSP.Protocol.Types ()+import qualified Language.LSP.Protocol.Types as LSP+import Language.LSP.Server (Handlers, getVirtualFile, notificationHandler)+import Language.LSP.VFS+import Zwirn.Language.Compiler+import Zwirn.Language.LSP.Diagnostics++didSaveHandler :: MVar Environment -> Handlers LSP+didSaveHandler envMV = notificationHandler LSP.SMethod_TextDocumentDidSave $ \msg -> do+ let doc = msg ^. LSP.params . LSP.textDocument . LSP.uri+ mcont = msg ^. LSP.params . LSP.text+ case mcont of+ Just content -> do+ env <- liftIO $ readMVar envMV+ errs <- liftIO $ validateCode env content+ case errs of+ Just err -> publishDiags (LSP.toNormalizedUri doc) Nothing (makeErrorDiagnostic err)+ Nothing -> refreshDiagnostics+ Nothing -> return ()++didOpenHandler :: MVar Environment -> Handlers LSP+didOpenHandler envMV = notificationHandler LSP.SMethod_TextDocumentDidOpen $ \msg -> do+ let doc = msg ^. LSP.params . LSP.textDocument . LSP.uri+ content = msg ^. LSP.params . LSP.textDocument . LSP.text+ env <- liftIO $ readMVar envMV+ errs <- liftIO $ validateCode env content+ case errs of+ Just err -> publishDiags (LSP.toNormalizedUri doc) Nothing (makeErrorDiagnostic err)+ Nothing -> refreshDiagnostics++didCloseHandler :: Handlers LSP+didCloseHandler = notificationHandler LSP.SMethod_TextDocumentDidClose $ const $ return ()++didChangeHandler :: MVar Environment -> Handlers LSP+didChangeHandler envMV = notificationHandler LSP.SMethod_TextDocumentDidChange $ \msg -> do+ let doc = msg ^. LSP.params . LSP.textDocument . LSP.uri . to LSP.toNormalizedUri+ mdoc <- getVirtualFile doc+ case mdoc of+ Just vf@(VirtualFile _ version _rope) -> do+ env <- liftIO $ readMVar envMV+ errs <- liftIO $ validateCode env (virtualFileText vf)+ case errs of+ Just err -> publishDiags doc (Just (fromIntegral version)) (makeErrorDiagnostic err)+ Nothing -> refreshDiagnostics+ _ -> debug "No virtual file found!"++cancelationHandler :: Handlers LSP+cancelationHandler = notificationHandler SMethod_CancelRequest $ \_ -> return ()
+ app/zwirnzi/LSP/Handlers/Hover.hs view
@@ -0,0 +1,30 @@+module LSP.Handlers.Hover where++import Control.Concurrent.MVar+import Control.Lens (to, (^.))+import Control.Monad.IO.Class+import LSP.Util+import qualified Language.LSP.Protocol.Lens as LSP+import qualified Language.LSP.Protocol.Message as LSP+import Language.LSP.Protocol.Types ()+import qualified Language.LSP.Protocol.Types as LSP+import Language.LSP.Server (Handlers, getVirtualFile, requestHandler)+import Language.LSP.VFS+import Zwirn.Language.Compiler+import Zwirn.Language.LSP.Hover++hoverHandler :: MVar Environment -> Handlers LSP+hoverHandler envMV = requestHandler+ LSP.SMethod_TextDocumentHover+ $ \req responder -> do+ let doc = req ^. LSP.params . LSP.textDocument . LSP.uri . to LSP.toNormalizedUri+ pos = req ^. LSP.params . LSP.position . to fromLSPPos+ mdoc <- getVirtualFile doc+ env <- liftIO $ readMVar envMV+ case mdoc of+ Just vf -> do+ mci <- liftIO $ runCI env (parseAndGetInfoAt (virtualFileText vf) pos)+ case mci of+ Right (Just (info, rng)) -> responder . Right . LSP.maybeToNull $ Just $ LSP.Hover (LSP.InL $ LSP.mkMarkdown info) (Just (toLSP rng))+ _ -> return ()+ Nothing -> return ()
+ app/zwirnzi/LSP/Handlers/InlayHint.hs view
@@ -0,0 +1,37 @@+module LSP.Handlers.InlayHint where++import Control.Concurrent.MVar+import Control.Lens (to, (^.))+import Control.Monad (void)+import Control.Monad.IO.Class+import LSP.Util+import qualified Language.LSP.Protocol.Lens as LSP+import qualified Language.LSP.Protocol.Message as LSP+import Language.LSP.Protocol.Types ()+import qualified Language.LSP.Protocol.Types as LSP+import Language.LSP.Server (Handlers, getVirtualFile, requestHandler, sendRequest)+import Language.LSP.VFS+import Zwirn.Language.Compiler+import Zwirn.Language.LSP.InlayHints (Hint (..), getHints)++inlayHintHandler :: MVar Environment -> Handlers LSP+inlayHintHandler envMV = requestHandler LSP.SMethod_TextDocumentInlayHint $ \req responder -> do+ let doc = req ^. LSP.params . LSP.textDocument . LSP.uri . to LSP.toNormalizedUri++ mdoc <- getVirtualFile doc+ env <- liftIO $ readMVar envMV+ psHints <- case mdoc of+ Just vf -> do+ hci <- liftIO $ runCI env (getHints (virtualFileText vf))+ case hci of+ Right hs -> return $ map mkHint hs+ _ -> return []+ Nothing -> return []++ responder $ Right $ LSP.InL psHints++mkHint :: Hint -> LSP.InlayHint+mkHint (Hint pos ct desc) = LSP.InlayHint (toLSPPos pos) (LSP.InL ct) Nothing Nothing (Just $ LSP.InL desc) Nothing Nothing Nothing++refreshHints :: LSP ()+refreshHints = void $ sendRequest LSP.SMethod_WorkspaceInlayHintRefresh Nothing (const $ return ())
+ app/zwirnzi/LSP/Main.hs view
@@ -0,0 +1,81 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE OverloadedStrings #-}++module LSP.Main where++import Control.Concurrent.MVar+import Control.Monad.IO.Class+import LSP.Handlers.Action+import LSP.Handlers.Command+import LSP.Handlers.Completion+import LSP.Handlers.File+import LSP.Handlers.Hover+import LSP.Handlers.InlayHint+import LSP.Util+import qualified Language.LSP.Protocol.Message as LSP+import Language.LSP.Protocol.Types ()+import qualified Language.LSP.Protocol.Types as LSP+import Language.LSP.Server+import Zwirn.Language.Compiler++main :: MVar Environment -> IO Int+main envMV = runServer def+ where+ def :: ServerDefinition ()+ def =+ ServerDefinition+ { parseConfig = \oldEnv _ -> Right oldEnv,+ onConfigChange = const $ pure (),+ defaultConfig = (),+ configSection = "demo",+ doInitialize = \env _req -> pure $ Right env,+ staticHandlers = \_caps -> handlers envMV,+ interpretHandler = \env -> Iso (runLspT env) liftIO,+ options = lspOptions+ }++syncOptions :: LSP.TextDocumentSyncOptions+syncOptions =+ LSP.TextDocumentSyncOptions+ { LSP._openClose = Just True,+ LSP._change = Just LSP.TextDocumentSyncKind_Incremental,+ LSP._willSave = Just False,+ LSP._willSaveWaitUntil = Just False,+ LSP._save = Just $ LSP.InR $ LSP.SaveOptions $ Just False+ }++lspOptions :: Options+lspOptions =+ defaultOptions+ { optTextDocumentSync = Just syncOptions,+ optExecuteCommandCommands = Just ["zwirn-eval"]+ }++handlers :: MVar Environment -> Handlers LSP+handlers envMV =+ mconcat+ [ notificationHandler LSP.SMethod_Initialized $ \_not -> debug "Initialising server.",+ notificationHandler LSP.SMethod_WorkspaceDidChangeConfiguration $ \_ -> return (),+ codeActionHandler,+ -- codeLensHandler,+ -- codeLensResolveHandler,+ inlayHintHandler envMV,+ execCommandHandler envMV,+ didSaveHandler envMV,+ didOpenHandler envMV,+ didCloseHandler,+ didChangeHandler envMV,+ hoverHandler envMV,+ completionHandler envMV,+ cancelationHandler+ ]++-- zed doesn't support showing code lenses in the document yet+-- but they might be a good way to display different available commands+-- codeLensHandler :: Handlers LSP+-- codeLensHandler = requestHandler LSP.SMethod_TextDocumentCodeLens $ \_ _ -> return ()++-- codeLensResolveHandler :: Handlers LSP+-- codeLensResolveHandler = requestHandler LSP.SMethod_CodeLensResolve $ \_ _ -> return ()
+ app/zwirnzi/LSP/Util.hs view
@@ -0,0 +1,40 @@+module LSP.Util where++import Control.Monad (void)+import qualified Data.Text as T+import qualified Language.LSP.Protocol.Message as LSP+import qualified Language.LSP.Protocol.Types as LSP+import Language.LSP.Server (LspM, sendNotification)+import Zwirn.Language.Location++type LSP = LspM ()++sendError :: T.Text -> LSP ()+sendError err =+ void $ sendNotification+ LSP.SMethod_WindowShowMessage+ ( LSP.ShowMessageParams+ LSP.MessageType_Error+ err+ )++sendInfo :: T.Text -> LSP ()+sendInfo msg =+ void $ sendNotification+ LSP.SMethod_WindowShowMessage+ ( LSP.ShowMessageParams+ LSP.MessageType_Info+ msg+ )++fromLSP :: T.Text -> LSP.Range -> RealSrcLoc+fromLSP t (LSP.Range (LSP.Position lst cst) (LSP.Position len cen)) = RealSrcLoc t (fromIntegral lst) (fromIntegral cst + 1) (fromIntegral len) (fromIntegral cen + 1)++toLSP :: RealSrcLoc -> LSP.Range+toLSP (RealSrcLoc _ lst cst len cen) = LSP.Range (LSP.Position (fromIntegral lst) (fromIntegral cst - 1)) (LSP.Position (fromIntegral len) (fromIntegral cen - 1))++fromLSPPos :: LSP.Position -> Position+fromLSPPos (LSP.Position l c) = Position (fromIntegral l) (fromIntegral c + 1)++toLSPPos :: Position -> LSP.Position+toLSPPos (Position l c) = LSP.Position (fromIntegral l) (fromIntegral c - 1)
+ app/zwirnzi/Main.hs view
@@ -0,0 +1,47 @@+module Main where++{-+ Main.hs - entry point of the editor program+ Copyright (C) 2023, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import CI.Backend+import CI.Config+import CI.Setup+import Conferer as Conf+import Control.Concurrent.MVar+import Control.Monad (void)+import qualified LSP.Main as LSP+import System.IO (BufferMode (..), hSetBuffering, stdin, stdout)++main :: IO ()+main = do+ hSetBuffering stdin NoBuffering++ config <- getConfig+ fullConfig <- Conf.fetch config+ env <- setup fullConfig+ envMV <- newMVar env+ if cliMode fullConfig+ then do+ hSetBuffering stdout NoBuffering+ runZwirnCI env evalInputLoop+ else do+ hSetBuffering stdout LineBuffering+ void $ LSP.main envMV++cliMode :: FullConfig -> Bool+cliMode = ciConfigCli . fullConfigCi
+ bench/Core/Main.hs view
@@ -0,0 +1,41 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# OPTIONS_GHC -Wno-orphans #-}++module Main where++import Control.DeepSeq+import Criterion.Main+import Data.Functor.Identity+import GHC.Generics (Generic)+import Zwirn.Core.Cord (Cord, stack)+import qualified Zwirn.Core.Lib.Core as C+import Zwirn.Core.Lib.Modulate (fast)+import Zwirn.Core.Query+import Zwirn.Core.Time (Time (..))+import Zwirn.Core.Types (ZwirnT)++deriving instance Generic Time++deriving instance NFData Time++test :: Cord () () Int+test = fast (stack (replicate 10 (pure 23))) $ stack (replicate 10 (pure 1))++test2 :: Cord () () Int+test2 = C.iterate (pure 100) (pure $ fmap (+ 1)) test++test3 :: ZwirnT Identity () () Int+test3 = fast (pure 23) (pure 1)++main :: IO ()+main =+ defaultMain+ [ bgroup+ "oldquery"+ [ bench "" $ nf (\arc -> findAllValuesWithTime arc () test) (0, 1),+ bench "" $ nf (\arc -> findAllValuesWithTime arc () test2) (0, 1),+ bench "" $ nf (\arc -> findAllValuesWithTime arc () test3) (0, 1)+ ]+ ]
− src/Zwirn/Language.hs
@@ -1,44 +0,0 @@-module Zwirn.Language- ( module Zwirn.Language.Block,- module Zwirn.Language.Compiler,- module Zwirn.Language.Lexer,- module Zwirn.Language.Parser,- module Zwirn.Language.Pretty,- module Zwirn.Language.Simple,- module Zwirn.Language.Syntax,- module Zwirn.Language.TypeCheck.Constraint,- module Zwirn.Language.Environment,- module Zwirn.Language.TypeCheck.Infer,- module Zwirn.Language.TypeCheck.Types,- )-where--import Zwirn.Language.Block-import Zwirn.Language.Compiler-import Zwirn.Language.Environment-import Zwirn.Language.Lexer-import Zwirn.Language.Parser-import Zwirn.Language.Pretty-import Zwirn.Language.Simple-import Zwirn.Language.Syntax-import Zwirn.Language.TypeCheck.Constraint-import Zwirn.Language.TypeCheck.Infer-import Zwirn.Language.TypeCheck.Types--{-- Language.hs - re-exports of all zwirn language modules- Copyright (C) 2023, Martin Gius-- This library is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This library is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this library. If not, see <http://www.gnu.org/licenses/>.--}
− src/Zwirn/Language/Block.hs
@@ -1,44 +0,0 @@-module Zwirn.Language.Block- ( Block (..)- , BlockError- , getBlock- , getLn- ) where--{-- Block.hs - parsing blocks of code and getting blocks at a specific line- Copyright (C) 2023, Martin Gius-- This library is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This library is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this library. If not, see <http://www.gnu.org/licenses/>.--}--import Data.Text as Text (Text, lines)--data Block = Block {bStart :: Int- ,bEnd :: Int- ,bContent :: Text- } deriving (Show, Eq)--type BlockError = String--getBlock :: Int -> [Block] -> Either BlockError Block-getBlock _ [] = Left "no block of code at current line"-getBlock num (block@(Block n1 n2 _):bs) = if n1 <= num && num <= n2- then Right block- else getBlock num bs--getLn :: Int -> [Block] -> Either BlockError Text-getLn i bs = do- (Block start _ cont) <- getBlock i bs- return $ (Text.lines cont)!!(i-start)
− src/Zwirn/Language/Builtin/Internal.hs
@@ -1,29 +0,0 @@-{-# OPTIONS_GHC -Wno-orphans #-}--module Zwirn.Language.Builtin.Internal where--import qualified Data.Map as Map-import Data.String-import Data.Text (Text, pack)-import Zwirn.Language.Environment-import Zwirn.Language.Evaluate hiding (insert)-import Zwirn.Language.Parser (parseScheme)-import Zwirn.Language.TypeCheck.Types--instance IsString Scheme where- fromString s = fromEither $ parseScheme (pack s)- where- fromEither (Right r) = r- fromEither (Left e) = error e--(===) :: Text -> Expression -> Map.Map Text Expression-(===) = Map.singleton--(<::) :: Map.Map Text Expression -> Scheme -> Map.Map Text (Expression, Scheme)-(<::) x s = fmap (\l -> (l, s)) x--(--|) :: Map.Map Text (Expression, Scheme) -> Text -> Map.Map Text AnnotatedExpression-(--|) n t = fmap (\(x, s) -> Annotated x s (Just t)) n--noDesc :: Map.Map Text (Expression, Scheme) -> Map.Map Text AnnotatedExpression-noDesc = fmap (\(x, s) -> Annotated x s Nothing)
− src/Zwirn/Language/Builtin/Parameters.hs
@@ -1,167 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Zwirn.Language.Builtin.Parameters where--import qualified Data.Map as Map-import Data.Text (Text)-import Zwirn.Core.Map-import Zwirn.Language.Builtin.Internal-import Zwirn.Language.Environment-import Zwirn.Language.Evaluate (Expression, Zwirn, toExp)--builtinParams :: Map.Map Text AnnotatedExpression-builtinParams = addAliases aliases $ Map.unions [builtinTextParams, builtinNumberParams, builtinIntParams]--builtinTextParams :: Map.Map Text AnnotatedExpression-builtinTextParams = Map.unions $ map (\t -> noDesc $ t === toExp ((fmap toExp . singleton (pure t)) :: Zwirn Text -> Zwirn Expression) <:: "Text -> Map") textParams--builtinNumberParams :: Map.Map Text AnnotatedExpression-builtinNumberParams = Map.unions $ map (\t -> noDesc $ t === toExp ((fmap toExp . singleton (pure t)) :: Zwirn Double -> Zwirn Expression) <:: "Number -> Map") numberParams--builtinIntParams :: Map.Map Text AnnotatedExpression-builtinIntParams = Map.unions $ map (\t -> noDesc $ t === toExp ((fmap toExp . singleton (pure t)) :: Zwirn Int -> Zwirn Expression) <:: "Number -> Map") intParams--textParams :: [Text]-textParams = ["s", "unit", "vowel", "toArg"]--intParams :: [Text]-intParams = ["cut", "orbit"]--numberParams :: [Text]-numberParams =- [ "accelerate",- "amp",- "attack",- "bandf",- "bandq",- "begin",- "binshift",- "ccn",- "ccv",- "channel",- "coarse",- "comb",- "crush",- "cutoff",- "decay",- "delay",- "delaytime",- "detune",- "distort",- "djf",- "dry",- "dur",- "end",- "enhance",- "expression",- "fadeInTime",- "fadeTime",- "freeze",- "freq",- "from",- "fshift",- "gain",- "gate",- "harmonic",- "hbrick",- "hcutoff",- "hold",- "hresonance",- "imag",- "krush",- "lagogo",- "lbrick",- "legato",- "leslie",- "lock",- "midibend",- "miditouch",- "modwheel",- "n",- "note",- "nudge",- "octave",- "octer",- "octersub",- "octersubsub",- "offset",- "overgain",- "overshape",- "pan",- "panorient",- "panspan",- "pansplay",- "panwidth",- "partials",- "phaserdepth",- "phaserrate",- "rate",- "real",- "release",- "resonance",- "ring",- "ringdf",- "ringf",- "room",- "sagogo",- "scram",- "shape",- "size",- "slide",- "smear",- "speed",- "squiz",- "sustain",- "sustainpedal",- "timescale",- "timescalewin",- "to",- "tremolodepth",- "tremolorate",- "triode",- "tsdelay",- "velocity",- "voice",- "waveloss",- "xsdelay"- ]--aliases :: [(Text, Text)]-aliases =- [ ("sound", "s"),- ("voi", "voice"),- ("up", "n"),- ("tremr", "tremolorate"),- ("tremdp", "tremolodepth"),- ("sz", "size"),- ("sus", "sustain"),- ("sld", "slide"),- ("scr", "scrash"),- ("rel", "release"),- ("por", "portamento"),- ("phasr", "phaserrate"),- ("phasdp", "phaserdepth"),- ("number", "n"),- ("lpq", "resonance"),- ("lpf", "cutoff"),- ("hpq", "hresonance"),- ("hpf", "hcutoff"),- ("gat", "gate"),- ("fadeOutTime", "fadeTime"),- ("dt", "delaytime"),- ("dfb", "delayfeedback"),- ("det", "detune"),- ("delayt", "delaytime"),- ("delayfb", "delayfeedback"),- ("ctf", "cutoff"),- ("bpq", "bandq"),- ("bpf", "bandf"),- ("att", "attack")- ]--addAliases :: [(Text, Text)] -> Map.Map Text AnnotatedExpression -> Map.Map Text AnnotatedExpression-addAliases as x = Map.unions $ map look as ++ [x]- where- look (y, n) = case Map.lookup n x of- Just a -> Map.singleton y a- Nothing -> Map.empty
− src/Zwirn/Language/Builtin/Prelude.hs
@@ -1,616 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# OPTIONS_GHC -Wno-orphans #-}--module Zwirn.Language.Builtin.Prelude where--{-- Builtin.hs - defines builtin functions- Copyright (C) 2023, Martin Gius-- This library is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This library is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this library. If not, see <http://www.gnu.org/licenses/>.--}--import qualified Data.Map as Map-import Data.Text (Text)-import Zwirn.Core.Conditional as Z-import Zwirn.Core.Cord as C-import Zwirn.Core.Core as C-import Zwirn.Core.Map as M-import Zwirn.Core.Modulate-import Zwirn.Core.Number as N-import Zwirn.Core.Random-import Zwirn.Core.Structure as S-import Zwirn.Core.Time-import Zwirn.Language.Builtin.Internal-import Zwirn.Language.Builtin.Parameters-import Zwirn.Language.Environment-import Zwirn.Language.Evaluate hiding (insert)-import Zwirn.Language.TypeCheck.Types--builtinEnvironment :: InterpreterEnv-builtinEnvironment = IEnv builtins instances--instances :: [Instance]-instances =- [ IsIn "Num" numberT,- IsIn "Num" mapT,- IsIn "Eq" numberT,- IsIn "Eq" mapT,- IsIn "Eq" textT- ]--builtinNames :: [Text]-builtinNames = Map.keys builtins--builtins :: Map.Map Text AnnotatedExpression-builtins =- Map.unions- [ coreFunctions,- numberFunctions,- signals,- randomFunctions,- timeFunctions,- structureFunctions,- conditionalFunctions,- cordFunctions,- mapFunctions,- stateFunctions,- builtinParams- ]--coreFunctions :: Map.Map Text AnnotatedExpression-coreFunctions =- Map.unions- [ "id"- === lambda id- <:: "a -> a"- --| "identity function",- "const"- === lambda (lambda . const)- <:: "a -> b -> a"- --| "constant function - ignore second input",- "scomb"- === lambda (\f -> lambda $ \g -> lambda $ \x -> f ! x ! (g ! x))- <:: "(a -> b -> c) -> (a -> b) -> a -> c"- --| "S-combinator",- "."- === lambda (\g -> lambda $ \f -> lambda $ \x -> g ! (f ! x))- <:: "(b -> c) -> (a -> b) -> a -> c"- --| "function composition",- "flip"- === lambda (\f -> lambda $ \y -> lambda $ \x -> f ! x ! y)- <:: "(a -> b -> c) -> b -> a -> c"- --| "flip arguments",- "\'"- === toExp (flip squeezeApply :: Zwirn Expression -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression)- <:: "a -> (a -> b) -> b"- --| "apply argument to function, results are squeezed",- "$"- === toExp (squeezeApply :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)- <:: "(a -> b) -> a -> b"- --| "apply argument to function, results are squeezed",- "|$"- === toExp (outerApply :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)- <:: "(a -> b) -> a -> b"- --| "apply argument to function",- "$|"- === toExp (innerApply :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)- <:: "(a -> b) -> a -> b"- --| "apply argument to function",- "map"- === toExp (mapZ :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)- <:: "(a -> b) -> a -> b"- --| "map a function over the structure of the argument",- "zip"- === toExp (zipApply :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)- <:: "(a -> b) -> a -> b"- --| "map a function over the structure of the argument",- "bus"- === toExp (id :: Zwirn Expression -> Zwirn Expression)- <:: "Number -> Bus"- --| "controlbus",- "recv"- === toExp (toExp recv)- <:: "Text -> Number -> Map"- --| "recieve a value from a bus and send it to the given parameter"- ]--numberFunctions :: Map.Map Text AnnotatedExpression-numberFunctions =- Map.unions- [ "|+"- === toExp ((+) :: Zwirn Expression -> Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a -> a"- --| "addition",- "|-"- === toExp ((-) :: Zwirn Expression -> Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a -> a"- --| "subtraction",- "|*"- === toExp ((*) :: Zwirn Expression -> Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a -> a"- --| "multiplication",- "|/"- === toExp ((/) :: Zwirn Expression -> Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a -> a"- --| "division",- "negate"- === toExp (negate :: Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a"- --| "negate",- "abs"- === toExp (abs :: Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a"- --| "absolute value",- "signum"- === toExp (signum :: Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a"- --| "signum",- "recip"- === toExp (recip :: Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a"- --| "reciprocal value",- "pi"- === toExp (pi :: Zwirn Expression)- <:: "Number"- --| "pi",- "|**"- === toExp ((**) :: Zwirn Expression -> Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a -> a"- --| "exponentiation",- "exp"- === toExp (exp :: Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a"- --| "exponential function",- "log"- === toExp (log :: Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a"- --| "logarithm base 10",- "sqrt"- === toExp (sqrt :: Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a"- --| "square root",- "sin"- === toExp (sin :: Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a"- --| "sine function",- "cos"- === toExp (cos :: Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a"- --| "cosine function",- "tan"- === toExp (tan :: Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a"- --| "tangens",- "asin"- === toExp (asin :: Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a"- --| "arc sine function",- "acos"- === toExp (acos :: Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a"- --| "arc cosine function",- "atan"- === toExp (atan :: Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a"- --| "arc tangens",- "sinh"- === toExp (sinh :: Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a"- --| "hyperbolic sine",- "cosh"- === toExp (cosh :: Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a"- --| "hyperbolic cosine",- "tanh"- === toExp (tan :: Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a"- --| "hyperbolic tangens",- "asinh"- === toExp (asinh :: Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a"- --| "hyperbolic arc sine function",- "acosh"- === toExp (acosh :: Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a"- --| "hyperbolic arc cosine function",- "atanh"- === toExp (atanh :: Zwirn Expression -> Zwirn Expression)- <:: "Num a => a -> a"- --| "hyperbolic arc tangens",- "mod"- === toExp (N.mod :: Zwirn Double -> Zwirn Double -> Zwirn Double)- <:: "Number -> Number -> Number"- --| "modulo",- "frac"- === toExp (N.frac :: Zwirn Double -> Zwirn Double)- <:: "Number -> Number"- --| "fractional part of a number",- "trunc"- === toExp (N.trunc :: Zwirn Double -> Zwirn Int)- <:: "Number -> Number"- --| "truncate",- "ceil"- === toExp (N.ceil :: Zwirn Double -> Zwirn Int)- <:: "Number -> Number"- --| "round up",- "floor"- === toExp (N.floor :: Zwirn Double -> Zwirn Int)- <:: "Number -> Number"- --| "round down",- "round"- === toExp (N.round :: Zwirn Double -> Zwirn Int)- <:: "Number -> Number"- --| "round to closest",- "gcd"- === toExp (N.gcd :: Zwirn Int -> Zwirn Int -> Zwirn Int)- <:: "Number -> Number -> Number"- --| "greatest common divisor",- "lcm"- === toExp (N.lcm :: Zwirn Int -> Zwirn Int -> Zwirn Int)- <:: "Number -> Number -> Number"- --| "least common multiple",- "range"- === toExp (range :: Zwirn Double -> Zwirn Double -> Zwirn Double -> Zwirn Double)- <:: "Number -> Number -> Number -> Number"- --| "range x y l maps number l linearly into interval (x,y), assuming l is between 0 and 1"- ]--signals :: Map.Map Text AnnotatedExpression-signals =- Map.unions- [ "sine"- === toExp (sine :: Zwirn Time)- <:: "Number"- --| "sine signal",- "sine2"- === toExp (sine2 :: Zwirn Time)- <:: "Number"- --| "bipolar sine signal",- "saw"- === toExp (saw :: Zwirn Time)- <:: "Number"- --| "saw signal",- "saw2"- === toExp (saw2 :: Zwirn Time)- <:: "Number"- --| "bipolar saw signal",- "cosine"- === toExp (cosine :: Zwirn Time)- <:: "Number"- --| "cosine signal",- "cosine2"- === toExp (cosine2 :: Zwirn Time)- <:: "Number"- --| "bipolar cosine signal",- "isaw"- === toExp (isaw :: Zwirn Time)- <:: "Number"- --| "inverse saw signal",- "isaw2"- === toExp (isaw2 :: Zwirn Time)- <:: "Number"- --| "bipolar inverse saw signal",- "tri"- === toExp (tri :: Zwirn Time)- <:: "Number"- --| "triangle signal",- "tri2"- === toExp (tri2 :: Zwirn Time)- <:: "Number"- --| "bipolar triangle signal",- "square"- === toExp (square :: Zwirn Time)- <:: "Number"- --| "square signal",- "square2"- === toExp (square2 :: Zwirn Time)- <:: "Number"- --| "bipolar square signal"- ]--randomFunctions :: Map.Map Text AnnotatedExpression-randomFunctions =- Map.unions- [ "noise"- === toExp (noise :: Zwirn Double)- <:: "Number"- --| "random stream of values between 0 and 1",- "irand"- === toExp (irand :: Zwirn Int -> Zwirn Int)- <:: "Number -> Number"- --| "random integer values between 0 and given input",- "chooseFromTo"- === toExp (enumFromToChoice 0 :: Zwirn Double -> Zwirn Double -> Zwirn Double)- <:: "Number -> Number -> Number"- --| "```chooseFromTo x y == [x | .. y]```",- "chooseFromThenTo"- === toExp (enumFromThenToChoice 0 :: Zwirn Double -> Zwirn Double -> Zwirn Double -> Zwirn Double)- <:: "Number -> Number -> Number -> Number"- --| "```chooseFromTo x y z == [x | y .. z]```"- ]--timeFunctions :: Map.Map Text AnnotatedExpression-timeFunctions =- Map.unions- [ "*"- === toExp (flip fast :: Zwirn Expression -> Zwirn Time -> Zwirn Expression)- <:: "a -> Number -> a"- --| "multiply time, making it faster",- "fast"- === toExp (fast :: Zwirn Time -> Zwirn Expression -> Zwirn Expression)- <:: "Number -> a -> a"- --| "multiply time, making it faster",- "/"- === toExp (flip slow :: Zwirn Expression -> Zwirn Time -> Zwirn Expression)- <:: "a -> Number -> a"- --| "divide time, making it slower",- "slow"- === toExp (slow :: Zwirn Time -> Zwirn Expression -> Zwirn Expression)- <:: "Number -> a -> a"- --| "divide time, making it slower",- "+"- === toExp (flip shift :: Zwirn Expression -> Zwirn Time -> Zwirn Expression)- <:: "a -> Number -> a"- --| "shift time to the right",- "-"- === toExp (flip (shift . fmap negate) :: Zwirn Expression -> Zwirn Time -> Zwirn Expression)- <:: "a -> Number -> a"- --| "shift time to the left",- "shift"- === toExp (shift :: Zwirn Time -> Zwirn Expression -> Zwirn Expression)- <:: "Number -> a -> a"- --| "shift time",- "revBy"- === toExp (revBy :: Zwirn Time -> Zwirn Expression -> Zwirn Expression)- <:: "Number -> a -> a"- --| "reverse time, piecewise",- "rev"- === toExp (rev :: Zwirn Expression -> Zwirn Expression)- <:: "a -> a"- --| "reverse time completely",- "ply"- === toExp (ply :: Zwirn Time -> Zwirn Expression -> Zwirn Expression)- <:: "Number -> a -> a"- --| "speed up time inside",- "timeloop"- === toExp (timeloop :: Zwirn Time -> Zwirn Expression -> Zwirn Expression)- <:: "Number -> a -> a"- --| "loop time from 0 to the given number",- "zoom"- === toExp (zoom :: Zwirn Time -> Zwirn Time -> Zwirn Expression -> Zwirn Expression)- <:: "Number -> Number -> a -> a"- --| "zoom and loop a part of a zwirn"- ]--structureFunctions :: Map.Map Text AnnotatedExpression-structureFunctions =- Map.unions- [ "euclidOff"- === toExp (euclidOff :: Zwirn Int -> Zwirn Int -> Zwirn Int -> Zwirn Expression -> Zwirn Expression)- <:: "Number -> Number -> Number -> a -> a"- --| "shifted euclidean rhythm",- "euclid"- === toExp (euclid :: Zwirn Int -> Zwirn Int -> Zwirn Expression -> Zwirn Expression)- <:: "Number -> Number -> a -> a"- --| "euclidean rhythm",- "segment"- === toExp (segment :: Zwirn Int -> Zwirn Expression -> Zwirn Expression)- <:: "Number -> a -> a"- --| "divide structure into equal pieces",- "struct"- === toExp (struct :: Zwirn Expression -> Zwirn Expression -> Zwirn Expression)- <:: "a -> b -> b"- --| "copy the structure from first value",- "run"- === toExp (run :: Zwirn Int -> Zwirn Int)- <:: "Number -> Number"- --| "```run n == [0 .. n-1]```",- "runFromTo"- === toExp (runFromTo :: Zwirn Double -> Zwirn Double -> Zwirn Double)- <:: "Number -> Number -> Number"- --| "```runFromTo x y == [x .. y]```",- "runFromThenTo"- === toExp (runFromThenTo :: Zwirn Double -> Zwirn Double -> Zwirn Double -> Zwirn Double)- <:: "Number -> Number -> Number -> Number"- --| "```runFromTo x y z == [x y .. z]```",- "slowrun"- === toExp (slowrun :: Zwirn Int -> Zwirn Int)- <:: "Number -> Number"- --| "```run n == <0 .. n-1>```",- "slowrunFromTo"- === toExp (slowrunFromTo :: Zwirn Double -> Zwirn Double -> Zwirn Double)- <:: "Number -> Number -> Number"- --| "```slowrunFromTo x y == <x .. y>```",- "slowrunFromThenTo"- === toExp (slowrunFromThenTo :: Zwirn Double -> Zwirn Double -> Zwirn Double -> Zwirn Double)- <:: "Number -> Number -> Number -> Number"- --| "```slowrunFromTo x y z == <x y .. z>```"- ]--conditionalFunctions :: Map.Map Text AnnotatedExpression-conditionalFunctions =- Map.unions- [ "=="- === toExp (eq :: Zwirn Expression -> Zwirn Expression -> Zwirn Bool)- <:: "Eq a => a -> a -> Number"- --| "equality",- ">="- === toExp (geq :: Zwirn Expression -> Zwirn Expression -> Zwirn Bool)- <:: "Number -> Number -> Number"- --| "greater or equal",- "<="- === toExp (leq :: Zwirn Expression -> Zwirn Expression -> Zwirn Bool)- <:: "Number -> Number -> Number"- --| "less or equal",- "<"- === toExp (ge :: Zwirn Expression -> Zwirn Expression -> Zwirn Bool)- <:: "Number -> Number -> Number"- --| "less",- ">"- === toExp (le :: Zwirn Expression -> Zwirn Expression -> Zwirn Bool)- <:: "Number -> Number -> Number"- --| "greater",- "not"- === toExp (Z.not :: Zwirn Bool -> Zwirn Bool)- <:: "Number -> Number"- --| "logical not",- "&&"- === toExp (Z.and :: Zwirn Bool -> Zwirn Bool -> Zwirn Bool)- <:: "Number -> Number -> Number"- --| "logical and",- "||"- === toExp (Z.or :: Zwirn Bool -> Zwirn Bool -> Zwirn Bool)- <:: "Number -> Number -> Number"- --| "logical or",- "ifthen"- === toExp (ifthen :: Zwirn Bool -> Zwirn Expression -> Zwirn Expression -> Zwirn Expression)- <:: "Number -> a -> a -> a"- --| "choose between two expressions based on a condition",- "if"- === toExp (iff :: Zwirn Bool -> Zwirn Expression -> Zwirn Expression)- <:: "Number -> a -> a"- --| "if condition is true produce the value, silence otherwise",- "while"- === toExp (while :: Zwirn Bool -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)- <:: "Number -> (a -> a) -> a -> a"- --| "apply function while condition is true",- "everyFor"- === toExp (everyFor :: Zwirn Time -> Zwirn Time -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)- <:: "Number -> Number -> (a -> a) -> a -> a"- --| "apply function periodically for a given amount of time",- "every"- === toExp (every :: Zwirn Time -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)- <:: "Number -> (a -> a) -> a -> a"- --| "apply function periodically for one cycle"- ]--cordFunctions :: Map.Map Text AnnotatedExpression-cordFunctions =- Map.unions- [ "project"- === toExp (project :: Zwirn Int -> Zwirn Expression -> Zwirn Expression)- <:: "Number -> a -> a"- --| "project to a certain layer of a cord",- "insert"- === toExp (C.insert :: Zwirn Int -> Zwirn Expression -> Zwirn Expression -> Zwirn Expression)- <:: "Number -> a -> a -> a"- --| "insert into a specific layer of a cord",- "remove"- === toExp (remove :: Zwirn Int -> Zwirn Expression -> Zwirn Expression)- <:: "Number -> a -> a"- --| "remove a specific layer of a cord",- "arp"- === toExp (arp :: Zwirn Expression -> Zwirn Expression)- <:: "a -> a"- --| "arpeggiate",- "reverse"- === toExp (reverseC :: Zwirn Expression -> Zwirn Expression)- <:: "a -> a"- --| "reverse order of cord",- "invert"- === toExp (invertC :: Zwirn Expression -> Zwirn Expression)- <:: "Number -> Number"- --| "chord inversion",- "rotate"- === toExp (rotateC :: Zwirn Expression -> Zwirn Expression)- <:: "a -> a"- --| "cord rotation",- "at"- === toExp (at :: Zwirn Int -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)- <:: "Number -> (a -> a) -> a -> a"- --| "apply a function to a specific layer of a cord",- "cordFromTo"- === toExp (enumFromToStack :: Zwirn Double -> Zwirn Double -> Zwirn Double)- <:: "Number -> Number -> Number"- --| "```cordFromTo x y == [x, .. y]```",- "cordFromThenTo"- === toExp (enumFromThenToStack :: Zwirn Double -> Zwirn Double -> Zwirn Double -> Zwirn Double)- <:: "Number -> Number -> Number -> Number"- --| "```cordFromThenTo x y z == [x, y .. z]```"- ]--mapFunctions :: Map.Map Text AnnotatedExpression-mapFunctions =- Map.unions- [ "pN"- === toExp ((\t -> fmap toExp . singleton t) :: Zwirn Text -> Zwirn Double -> Zwirn Expression)- <:: "Text -> Number -> Map"- --| "number singleton with specific key",- "pT"- === toExp ((\t -> fmap toExp . singleton t) :: Zwirn Text -> Zwirn Text -> Zwirn Expression)- <:: "Text -> Text -> Map"- --| "text singleton with specific key",- "#"- === toExp (union :: Zwirn ExpressionMap -> Zwirn ExpressionMap -> Zwirn ExpressionMap)- <:: "Map -> Map -> Map"- --| "union of two maps - structure from the left",- "lookupN"- === toExp (M.lookup :: Zwirn Text -> Zwirn ExpressionMap -> Zwirn Expression)- <:: "Text -> Map -> Number"- --| "retrieve number at given key or silence if key is missing or it's value not a number",- "lookupT"- === toExp (M.lookup :: Zwirn Text -> Zwirn ExpressionMap -> Zwirn Expression)- <:: "Text -> Map -> Text"- --| "retrieve text at given key or silence if key is missing or it's value not a text",- "fix"- === toExp (M.fix :: Zwirn Text -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn ExpressionMap -> Zwirn ExpressionMap)- <:: "Text -> (Map -> Map) -> Map -> Map"- --| "apply a function to a specific key",- "loopAt"- === toExp (loopAt :: Zwirn Time -> Zwirn ExpressionMap -> Zwirn ExpressionMap)- <:: "Number -> Map -> Map"- --| "",- "slice"- === toExp (slice :: Zwirn Int -> Zwirn Int -> Zwirn ExpressionMap -> Zwirn ExpressionMap)- <:: "Number -> Number -> Map -> Map"- --| "slice a sample into equal btis and index into them",- "chop"- === toExp (chop :: Zwirn Int -> Zwirn ExpressionMap -> Zwirn ExpressionMap)- <:: "Number -> Map -> Map"- --| "",- "striate"- === toExp (striate :: Zwirn Int -> Zwirn ExpressionMap -> Zwirn ExpressionMap)- <:: "Number -> Map -> Map"- --| "",- "striateBy"- === toExp (striateBy :: Zwirn Int -> Zwirn Expression -> Zwirn ExpressionMap -> Zwirn ExpressionMap)- <:: "Number -> Number -> Map -> Map"- --| ""- ]--stateFunctions :: Map.Map Text AnnotatedExpression-stateFunctions =- Map.unions- [ "getN"- === toExp getStateN- <:: "Text -> Number"- --| "retrieve number from state at given key or silence if key is missing or it's value not a number",- "getT"- === toExp getStateT- <:: "Text -> Text"- --| "retrieve text from state at given key or silence if key is missing or it's value not a text",- "getM"- === toExp getStateM- <:: "Text -> Map"- --| "retrieve map from state at given key or silence if key is missing or it's value not a map",- "set"- === toExp setState- <:: "Text -> a -> b -> b"- --| "set state at key to given value",- "modify"- === toExp modifyState- <:: "Text -> (a -> a) -> b -> b"- --| "modify state at given key with function"- ]
− src/Zwirn/Language/Compiler.hs
@@ -1,425 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}-{-# OPTIONS_GHC -Wno-unused-top-binds #-}--module Zwirn.Language.Compiler where--{-- Compiler.hs - implementation of a compiler-interpreter for zwirn- Copyright (C) 2023, Martin Gius-- This library is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This library is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this library. If not, see <http://www.gnu.org/licenses/>.--}--import Control.Concurrent (readMVar)-import Control.Exception (SomeException, try)-import Control.Monad-import Control.Monad.Except-import Control.Monad.State-import Data.List (sortOn)-import Data.Text (Text, unpack)-import Data.Text.IO (readFile)-import Text.Read (readMaybe)-import Zwirn.Core.Types (silence)-import Zwirn.Language.Block-import Zwirn.Language.Builtin.Prelude (builtinNames)-import Zwirn.Language.Environment-import Zwirn.Language.Evaluate-import Zwirn.Language.Parser-import Zwirn.Language.Pretty-import qualified Zwirn.Language.Rotate as R-import Zwirn.Language.Simple-import Zwirn.Language.Syntax-import Zwirn.Language.TypeCheck.Constraint (runSolve)-import Zwirn.Language.TypeCheck.Infer-import Zwirn.Language.TypeCheck.Types-import Zwirn.Stream-import Prelude hiding (readFile)--newtype CIMessage- = CIMessage Text- deriving (Show, Eq)--data CurrentBlock- = CurrentBlock Int Int- deriving (Eq, Show)--data ConfigEnv- = ConfigEnv- { cConfigPath :: IO String,- cResetConfig :: IO String- }--data CiConfig = CiConfig- { ciConfigOverwriteBuiltin :: Bool,- ciConfigDynamicTypes :: Bool- }--data Environment- = Environment- { tStream :: Stream,- intEnv :: InterpreterEnv,- confEnv :: Maybe ConfigEnv,- currBlock :: Maybe CurrentBlock,- ciConfig :: CiConfig- }--data CIError- = CIError- { eError :: String,- eEnv :: Environment- }--instance Show CIError where- show (CIError err _) = err--type CI = StateT Environment (ExceptT CIError IO)--runCI :: Environment -> CI a -> IO (Either CIError a)-runCI env m = runExceptT $ evalStateT m env--compilerInterpreterBasic :: Text -> CI String-compilerInterpreterBasic input = do- as <- runParser input- runActions True as--compilerInterpreterBlock :: Int -> Int -> Text -> CI (String, Environment, Int, Int)-compilerInterpreterBlock line editor input = do- blocks <- runBlocks 0 input- (Block strt end content) <- runGetBlock line blocks- setCurrentBlock strt end- as <- runParserWithPos strt editor content- r <- runActions True as- e <- get- return (r, e, strt, end)--compilerInterpreterLine :: Int -> Int -> Text -> CI (String, Environment, Int, Int)-compilerInterpreterLine line editor input = do- setCurrentBlock line line- blocks <- runBlocks 0 input- content <- runGetLine line blocks- as <- runParserWithPos line editor content- r <- runActions True as- e <- get- return (r, e, line, line)--compilerInterpreterWhole :: Int -> Text -> CI (String, Environment, Int, Int)-compilerInterpreterWhole editor input = do- blocks <- runBlocks 0 input- let sorted = sortOn (\(Block x _ _) -> x) blocks- (Block strt _ _) = head sorted- (Block _ end _) = last sorted- liftIO $ print sorted- setCurrentBlock strt end- let parseBlock (Block s _ c) = runParserWithPos s editor c- ass <- mapM parseBlock sorted- rs <- mapM (runActions True) ass- e <- get- return (last rs, e, strt, end)--compilerInterpreterBoot :: [Text] -> CI Environment-compilerInterpreterBoot ps = runActions False (map Load ps) >> get------------------------------------------------------------------------- Throwing Errors ---------------------------------------------------------------------------throw :: String -> CI a-throw err = do- env <- get- throwError $ CIError err env--setCurrentBlock :: Int -> Int -> CI ()-setCurrentBlock st en = modify (\env -> env {currBlock = Just $ CurrentBlock st en})------------------------------------------------------------------------------ Parser -------------------------------------------------------------------------------runParserWithPos :: Int -> Int -> Text -> CI [Action]-runParserWithPos ln ed t = case parseActionsWithPos ln ed t of- Left err -> throw err- Right as -> return as--runParser :: Text -> CI [Action]-runParser t = case parseActions t of- Left err -> throw err- Right as -> return as--runBlocks :: Int -> Text -> CI [Block]-runBlocks ln t = case parseBlocks ln t of- Left err -> throw err- Right bs -> return bs--runGetBlock :: Int -> [Block] -> CI Block-runGetBlock i bs = case getBlock i bs of- Left err -> throw err- Right b -> return b--runGetLine :: Int -> [Block] -> CI Text-runGetLine i bs = case getLn i bs of- Left err -> throw err- Right b -> return b------------------------------------------------------------------------------ Desugar ------------------------------------------------------------------------------runSimplify :: Term -> CI SimpleTerm-runSimplify t = return $ simplify t--runSimplifyDef :: Def -> CI SimpleDef-runSimplifyDef d = return $ simplifyDef d--------------------------------------------------------------------------- AST Rotation ----------------------------------------------------------------------------runRotate :: SimpleTerm -> CI SimpleTerm-runRotate s = case R.runRotate s of- Left err -> throw err- Right t -> return t---------------------------------------------------------------------------- Type Check -----------------------------------------------------------------------------runTypeCheck :: SimpleTerm -> CI Scheme-runTypeCheck s = do- Environment {intEnv = env} <- get- case inferTerm env s of- Left err -> throw $ show err- Right t -> return t---------------------------------------------------------------------------- Interpreter ----------------------------------------------------------------------------interpret :: SimpleTerm -> CI Expression-interpret input = do- env <- gets intEnv- return $ evaluate env input---- if ctx is false, highlighting should be disabled-checkHighlight :: Bool -> Expression -> CI Expression-checkHighlight True x = return x-checkHighlight False x = return $ removePosExp x------------------------------------------------------------------------- Compiling Actions -------------------------------------------------------------------------defAction :: Bool -> Def -> CI ()-defAction ctx d = do- (LetS x st) <- runSimplifyDef d- rot <- runRotate st- ty@(Forall _ (Qual _ typ)) <- runTypeCheck rot- ex <- interpret rot- exCtx <- checkHighlight ctx ex- dynamic <- gets (ciConfigDynamicTypes . ciConfig)-- if dynamic- then checkAndDefine x ty exCtx- else do- mayty <- gets (lookupType x . intEnv)- case mayty of- Just (Forall _ (Qual _ oldType)) -> case runSolve [(oldType, typ)] of- Left _ -> throw "Cannot overwrite definition with new type. Please use DynamicTypes."- Right _ -> checkAndDefine x ty exCtx- Nothing -> checkAndDefine x ty exCtx--checkAndDefine :: Text -> Scheme -> Expression -> CI ()-checkAndDefine x ty exCtx = do- overwrite <- gets (ciConfigOverwriteBuiltin . ciConfig)- if overwrite- then modify (\env -> env {intEnv = extend (x, exCtx, ty) (intEnv env)})- else- if x `elem` builtinNames- then throw "Failed to overwrite builtin function. Please enable OverwriteBuiltin."- else modify (\env -> env {intEnv = extend (x, exCtx, ty) (intEnv env)})--showAction :: Term -> CI String-showAction t = do- s <- runSimplify t- rot <- runRotate s- ty <- runTypeCheck rot- if isBasicType ty- then do- ex <- interpret rot- stmv <- gets (sState . tStream)- st <- liftIO $ readMVar stmv- return $ showWithState st ex- else throw $ "Can not show expressions of type: " ++ ppscheme ty--typeAction :: Term -> CI String-typeAction t = do- s <- runSimplify t- rot <- runRotate s- ty <- runTypeCheck rot- return $ ppTermHasType (t, ty)--loadAction :: Text -> CI ()-loadAction path = do- mayfile <- liftIO ((try $ readFile $ unpack path) :: IO (Either SomeException Text))- case mayfile of- Left _ -> throw "file not found"- Right input -> do- blocks <- runBlocks 0 input- let sorted = sortOn (\(Block x _ _) -> x) blocks- ass <- mapM (runParser . bContent) sorted- mapM_ (runActions False) ass--infoAction :: Text -> CI String-infoAction n = do- env <- gets intEnv- case lookupFull n env of- Just (Annotated _ t (Just d)) -> return $ unpack n ++ " :: " ++ ppscheme t ++ "\n" ++ unpack d- Just (Annotated _ t Nothing) -> return $ unpack n ++ " :: " ++ ppscheme t- Nothing -> throw $ "couldn't find information about " ++ unpack n--streamAction :: Bool -> Text -> Term -> CI ()-streamAction ctx key t = do- s <- runSimplify t- rot <- runRotate s- ty <- runTypeCheck rot- ex <- interpret rot- exCtx <- checkHighlight ctx ex- if isBasicType ty- then- ( do- str <- gets tStream- liftIO $ streamReplace str key (fromExp exCtx)- )- else- if isBus ty- then- ( do- str <- gets tStream- let mayindex = readMaybe $ unpack key- case mayindex of- Just ind -> liftIO $ streamReplaceBus str ind (fromExp exCtx)- Nothing -> throw "Please use an integer as bus index."- )- else throw "Can only stream base types!"--streamSetAction :: Bool -> Text -> Term -> CI ()-streamSetAction ctx x t = do- s <- runSimplify t- rot <- runRotate s- ty@(Forall _ (Qual _ typ)) <- runTypeCheck rot- ex <- interpret rot- exCtx <- checkHighlight ctx ex-- dynamic <- gets (ciConfigDynamicTypes . ciConfig)-- if dynamic- then checkAndSet x ty exCtx- else do- mayty <- gets (lookupType x . intEnv)- case mayty of- Just (Forall _ (Qual _ oldType)) -> case runSolve [(oldType, typ)] of- Left _ -> throw "Cannot overwrite definition with new type. Please use DynamicTypes."- Right _ -> checkAndSet x ty exCtx- Nothing -> checkAndSet x ty exCtx--checkAndSet :: Text -> Scheme -> Expression -> CI ()-checkAndSet x ty exCtx =- if isBasicType ty- then- ( do- overwrite <- gets (ciConfigOverwriteBuiltin . ciConfig)- if overwrite- then setExpression x ty exCtx- else- if x `elem` builtinNames- then throw "Failed to overwrite builtin function. Please enable OverwriteBuiltin."- else setExpression x ty exCtx- )- else throw "Can only set basic types!"--setExpression :: Text -> Scheme -> Expression -> CI ()-setExpression x ty exCtx = do- modify (\env -> env {intEnv = extend (x, newEx, ty) (intEnv env)})- str <- gets tStream- liftIO $ streamSet str x exCtx- where- newEx- | isNumberT ty = EZwirn $ getStateN (pure x)- | isTextT ty = EZwirn $ getStateT (pure x)- | isMapT ty = EZwirn $ getStateM (pure x)- | otherwise = EZwirn silence--streamOnceAction :: Bool -> Term -> CI ()-streamOnceAction ctx t = do- s <- runSimplify t- rot <- runRotate s- ty <- runTypeCheck rot- ex <- interpret rot- exCtx <- checkHighlight ctx ex- if isBasicType ty- then- ( do- str <- gets tStream- liftIO $ streamFirst str (fromExp exCtx)- )- else throw "Can only stream base types!"--streamSetTempoAction :: Tempo -> Text -> CI ()-streamSetTempoAction CPS t = gets tStream >>= \str -> liftIO $ streamSetCPS str (toRational (read $ unpack t :: Double))-streamSetTempoAction BPM t = gets tStream >>= \str -> liftIO $ streamSetBPM str (toRational (read $ unpack t :: Double))--resetConfigAction :: CI String-resetConfigAction = do- (Environment {confEnv = mayEnv}) <- get- case mayEnv of- Nothing -> throw "Configuration not available."- Just (ConfigEnv _ reset) -> liftIO reset--getConfigPathAction :: CI String-getConfigPathAction = do- (Environment {confEnv = mayEnv}) <- get- case mayEnv of- Nothing -> throw "Configuration not available."- Just (ConfigEnv path _) -> liftIO path--runAction :: Bool -> Action -> CI String-runAction b (StreamAction i t) = streamAction b i t >> return ""-runAction b (StreamSet i t) = streamSetAction b i t >> return ""-runAction b (StreamOnce t) = streamOnceAction b t >> return ""-runAction _ (StreamSetTempo mode t) = streamSetTempoAction mode t >> return ""-runAction _ (Show t) = showAction t-runAction b (Def d) = defAction b d >> return ""-runAction _ (Type t) = typeAction t-runAction _ (Load p) = loadAction p >> return ""-runAction _ (Info p) = infoAction p-runAction _ ConfigPath = getConfigPathAction-runAction _ ResetConfig = resetConfigAction--runActions :: Bool -> [Action] -> CI String-runActions b as = last <$> mapM (runAction b) as--isNumberT :: Scheme -> Bool-isNumberT (Forall _ (Qual _ (TypeCon "Number"))) = True-isNumberT _ = False--isTextT :: Scheme -> Bool-isTextT (Forall _ (Qual _ (TypeCon "Text"))) = True-isTextT _ = False--isMapT :: Scheme -> Bool-isMapT (Forall _ (Qual _ (TypeCon "Map"))) = True-isMapT _ = False
− src/Zwirn/Language/Environment.hs
@@ -1,44 +0,0 @@-module Zwirn.Language.Environment where--import qualified Data.Map as Map-import Data.Text (Text)-import Zwirn.Core.Types (silence)-import Zwirn.Language.Evaluate.Expression-import Zwirn.Language.TypeCheck.Types--data AnnotatedExpression- = Annotated- { aExp :: Expression,- aType :: Scheme,- aDesc :: Maybe Text- }--data InterpreterEnv = IEnv- { eExpressions :: Map.Map Text AnnotatedExpression,- eInstances :: [Instance]- }--withExpressions :: (Map.Map Text AnnotatedExpression -> Map.Map Text AnnotatedExpression) -> InterpreterEnv -> InterpreterEnv-withExpressions f (IEnv l i) = IEnv (f l) i--extend :: (Text, Expression, Scheme) -> InterpreterEnv -> InterpreterEnv-extend (n, x, s) = withExpressions (Map.insert n (Annotated x s Nothing))--lookupType :: Text -> InterpreterEnv -> Maybe Scheme-lookupType k (IEnv l _) = aType <$> Map.lookup k l--insertType :: Text -> Scheme -> InterpreterEnv -> InterpreterEnv-insertType t s = withExpressions (Map.alter alt t)- where- dummy = EZwirn silence- alt Nothing = Just $ Annotated dummy s Nothing- alt (Just (Annotated x _ i)) = Just $ Annotated x s i--lookupDescription :: Text -> InterpreterEnv -> Maybe Text-lookupDescription k (IEnv l _) = aDesc =<< Map.lookup k l--lookupExp :: Text -> InterpreterEnv -> Maybe Expression-lookupExp k (IEnv l _) = aExp <$> Map.lookup k l--lookupFull :: Text -> InterpreterEnv -> Maybe AnnotatedExpression-lookupFull k (IEnv l _) = Map.lookup k l
− src/Zwirn/Language/Evaluate.hs
@@ -1,12 +0,0 @@-module Zwirn.Language.Evaluate- ( module Zwirn.Language.Evaluate.Expression,- module Zwirn.Language.Evaluate.Internal,- module Zwirn.Language.Evaluate.Convert,- module Zwirn.Language.Evaluate.SKI,- )-where--import Zwirn.Language.Evaluate.Convert-import Zwirn.Language.Evaluate.Expression-import Zwirn.Language.Evaluate.Internal-import Zwirn.Language.Evaluate.SKI
− src/Zwirn/Language/Evaluate/Convert.hs
@@ -1,179 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}-{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}-{-# OPTIONS_GHC -Wno-orphans #-}-{-# OPTIONS_GHC -Wno-unused-top-binds #-}--module Zwirn.Language.Evaluate.Convert where--{-- Convert.hs - convert from and to Expressions- Copyright (C) 2023, Martin Gius-- This library is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This library is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this library. If not, see <http://www.gnu.org/licenses/>.--}--import qualified Data.Map as Map-import Data.String (IsString, fromString)-import Data.Text (Text, pack)-import Zwirn.Core.Time (Time (..))-import Zwirn.Core.Types-import Zwirn.Language.Evaluate.Expression--fromZwirn :: Zwirn Expression -> Expression-fromZwirn = EZwirn--toZwirn :: Expression -> Zwirn Expression-toZwirn (EZwirn x) = x-toZwirn _ = silence--class FromExpression a where- fromExp :: Expression -> Zwirn a--class ToExpression a where- toExp :: a -> Expression--instance FromExpression Time where- fromExp (EZwirn tz) = fmap (\(ENum t) -> Time (toRational t) 0) tz- fromExp _ = silence--instance FromExpression Double where- fromExp (EZwirn tz) = fmap (\(ENum t) -> t) tz- fromExp _ = silence--instance FromExpression Int where- fromExp (EZwirn tz) = fmap (\(ENum t) -> floor t) tz- fromExp _ = silence--instance FromExpression Expression where- fromExp (EZwirn z) = z- fromExp _ = silence--instance FromExpression Text where- fromExp (EZwirn z) = fmap (\(EText t) -> t) z- fromExp _ = silence--instance FromExpression Bool where- fromExp (EZwirn z) = fmap (\(ENum x) -> x >= 1) z- fromExp _ = silence--instance FromExpression ExpressionMap where- fromExp (EZwirn z) = fmap (\(EMap m) -> m) z- fromExp _ = silence--instance (ToExpression a, FromExpression b) => FromExpression (Zwirn a -> Zwirn b) where- fromExp (EZwirn z) = fmap (\(ELam f) -> fromExp . f . toExp) z- fromExp _ = silence--instance (FromExpression a) => FromExpression (Zwirn a) where- fromExp (EZwirn z) = fmap fromExp z- fromExp _ = silence--instance ToExpression Expression where- toExp = id--instance ToExpression Double where- toExp = ENum--instance ToExpression Time where- toExp (Time t _) = ENum $ fromRational t--instance ToExpression Int where- toExp i = ENum $ fromIntegral i--instance ToExpression Bool where- toExp True = ENum 1- toExp False = ENum 0--instance ToExpression Text where- toExp = EText--instance (ToExpression a) => ToExpression (Map.Map Text a) where- toExp m = EMap $ toExp <$> m--instance (ToExpression a) => ToExpression (Zwirn a) where- toExp a = EZwirn $ fmap toExp a--instance (FromExpression a, ToExpression b) => ToExpression (Zwirn a -> b) where- toExp f = lambda $ \x -> toExp $ f (fromExp x)--instance Num Expression where- (+) = pervasive2 ((+) @Double)- (*) = pervasive2 ((*) @Double)- abs = pervasive (abs @Double)- signum = pervasive (signum @Double)- fromInteger i = ENum $ fromInteger i- negate = pervasive (negate @Double)--instance Fractional Expression where- fromRational r = ENum $ fromRational r- (/) = pervasive2 ((/) @Double)--instance Floating Expression where- pi = EZwirn $ pure $ ENum pi- exp = pervasive (exp :: Double -> Double)- log = pervasive (log :: Double -> Double)- sin = pervasive (sin :: Double -> Double)- cos = pervasive (cos :: Double -> Double)- asin = pervasive (asin :: Double -> Double)- acos = pervasive (acos :: Double -> Double)- atan = pervasive (atan :: Double -> Double)- sinh = pervasive (sinh :: Double -> Double)- cosh = pervasive (cosh :: Double -> Double)- asinh = pervasive (asinh :: Double -> Double)- acosh = pervasive (acosh :: Double -> Double)- atanh = pervasive (atanh :: Double -> Double)--instance IsString Expression where- fromString = EText . pack--class Pervasive a where- pervasive :: (a -> a) -> Expression -> Expression- pervasive2 :: (a -> a -> a) -> Expression -> Expression -> Expression--instance Pervasive Double where- pervasive f (ENum d) = ENum $ f d- pervasive f (EMap m) = EMap $ fmap (pervasive f) m- pervasive _ e = e- pervasive2 f (ENum d) (ENum e) = ENum $ f d e- pervasive2 f (EMap m) (EMap n) = EMap $ Map.unionWith (pervasive2 f) m n- pervasive2 _ e _ = e--instance Pervasive Bool where- pervasive f (ENum d) = toExp $ f (d >= 1)- pervasive f (EMap m) = EMap $ fmap (pervasive f) m- pervasive _ e = e- pervasive2 f (ENum d) (ENum e) = toExp $ f (d >= 1) (e >= 1)- pervasive2 f (EMap m) (EMap n) = EMap $ Map.unionWith (pervasive2 f) m n- pervasive2 _ e _ = e--instance Pervasive Text where- pervasive f (EText d) = EText $ f d- pervasive f (EMap m) = EMap $ fmap (pervasive f) m- pervasive _ e = e- pervasive2 f (EText d) (EText e) = EText $ f d e- pervasive2 f (EMap m) (EMap n) = EMap $ Map.unionWith (pervasive2 f) m n- pervasive2 _ e _ = e--instance Pervasive (Either Double Text) where- pervasive f (EText d) = EText $ (\(Right t) -> t) $ f (Right d)- pervasive f (ENum d) = ENum $ (\(Left t) -> t) $ f (Left d)- pervasive f (EMap m) = EMap $ fmap (pervasive f) m- pervasive _ e = e- pervasive2 f (EText d) (EText e) = EText $ (\(Right t) -> t) $ f (Right d) (Right e)- pervasive2 f (ENum d) (ENum e) = ENum $ (\(Left t) -> t) $ f (Left d) (Left e)- pervasive2 f (EMap m) (EMap n) = EMap $ Map.unionWith (pervasive2 f) m n- pervasive2 _ e _ = e
− src/Zwirn/Language/Evaluate/Expression.hs
@@ -1,74 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}-{-# OPTIONS_GHC -Wno-unused-top-binds #-}--module Zwirn.Language.Evaluate.Expression where--{-- Expression.hs - Abstract Expressions- Copyright (C) 2023, Martin Gius-- This library is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This library is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this library. If not, see <http://www.gnu.org/licenses/>.--}--import Data.List-import qualified Data.Map as Map-import Data.Text (Text, unpack)-import Zwirn.Core.Cord-import Zwirn.Core.Query-import Zwirn.Core.Time (Time (..))-import Zwirn.Language.Syntax-import Zwirn.Language.TypeCheck.Types--type ExpressionMap = Map.Map Text Expression--type Zwirn = Cord ExpressionMap Position--data Expression- = EVar (Maybe Position) Name- | EApp Expression Expression- | ELam (Expression -> Expression)- | ENum Double- | EText Text- | EMap ExpressionMap- | ESeq [Expression]- | EStack [Expression]- | EChoice Int [Expression]- | EZwirn (Zwirn Expression)--showWithState :: ExpressionMap -> Expression -> String-showWithState st (EZwirn x) = intercalate "\n" $ (\(t, y) -> show t ++ ":" ++ showWithState st y) <$> findAllValuesWithTime (Time 0 1, Time 1 1) st x-showWithState _ (ENum x) = take 5 $ show x-showWithState _ (EText x) = unpack x-showWithState st (EMap m) = show $ Map.toList $ showWithState st <$> m-showWithState _ _ = "can't show"--instance Show Expression where- show = showWithState Map.empty--instance Eq Expression where- (==) (ENum n) (ENum m) = n == m- (==) (EText n) (EText m) = n == m- (==) (EMap n) (EMap m) = n == m- (==) _ _ = False--instance Ord Expression where- (<=) (ENum n) (ENum m) = n <= m- (<=) _ _ = False--lambda :: (Expression -> Expression) -> Expression-lambda f = EZwirn $ pure $ ELam f
− src/Zwirn/Language/Evaluate/Internal.hs
@@ -1,75 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}-{-# OPTIONS_GHC -Wno-unused-top-binds #-}--module Zwirn.Language.Evaluate.Internal where--{-- Internal.hs - internal functions, specific to Expressions- Copyright (C) 2023, Martin Gius-- This library is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This library is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this library. If not, see <http://www.gnu.org/licenses/>.--}--import qualified Data.Map as Map-import Data.Text (Text, pack)-import Zwirn.Core.Core (withState, (<$$>))-import Zwirn.Core.Map-import Zwirn.Core.State-import Zwirn.Core.Types-import Zwirn.Language.Evaluate.Convert-import Zwirn.Language.Evaluate.Expression---- helper--insert :: (Text, Expression) -> ExpressionMap -> ExpressionMap-insert (k, x) = Map.insert k x--getStateN :: Zwirn Text -> Zwirn Expression-getStateN xc = innerJoin $ liftA2 (\k l -> fromLookup $ Map.lookup k l) xc (get (pure ()))- where- fromLookup (Just (EZwirn x)) = outerJoin $ fmap fromNum x- fromLookup _ = silence- fromNum (ENum n) = pure $ ENum n- fromNum _ = silence--getStateT :: Zwirn Text -> Zwirn Expression-getStateT xc = innerJoin $ liftA2 (\k l -> fromLookup $ Map.lookup k l) xc (get (pure ()))- where- fromLookup (Just (EZwirn x)) = outerJoin $ fmap fromText x- fromLookup _ = silence- fromText (EText n) = pure $ EText n- fromText _ = silence--getStateM :: Zwirn Text -> Zwirn Expression-getStateM xc = innerJoin $ liftA2 (\k l -> fromLookup $ Map.lookup k l) xc (get (pure ()))- where- fromLookup (Just (EZwirn x)) = outerJoin $ fmap fromMap x- fromLookup _ = silence- fromMap (EMap n) = pure $ EMap n- fromMap _ = silence--modifyState :: Zwirn Text -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression-modifyState kz fz xz = modifyState' <$> kz <*> fz <$$> xz- where- modifyState' :: Text -> (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression- modifyState' key f = withState (Map.update (Just . toExp . f . fromExp) key)--setState :: Zwirn Text -> Zwirn Expression -> Zwirn Expression -> Zwirn Expression-setState t x = setMap t (pure $ EZwirn x)--recv :: Zwirn Text -> Zwirn Int -> Zwirn ExpressionMap-recv t i = singleton t (fmap (toExp . (\x -> pack $ "c" ++ show x)) i)
− src/Zwirn/Language/Evaluate/SKI.hs
@@ -1,102 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}-{-# OPTIONS_GHC -Wno-unused-top-binds #-}--module Zwirn.Language.Evaluate.SKI- ( evaluate,- (!),- removePosExp,- )-where--{-- SKI.hs - evaluate epxressions via the SKI combinator calculus,- code adapted from https://kseo.github.io/posts/2016-12-30-write-you-an-interpreter.html- Copyright (C) 2023, Martin Gius-- This library is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This library is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this library. If not, see <http://www.gnu.org/licenses/>.--}--import Data.Maybe (fromJust)-import Data.Text (unpack)-import Zwirn.Core.Cord-import Zwirn.Core.Core-import Zwirn.Core.Modulate-import Zwirn.Core.Random (chooseWithSeed)-import Zwirn.Core.Types-import Zwirn.Language.Environment-import Zwirn.Language.Evaluate.Convert-import Zwirn.Language.Evaluate.Expression-import Zwirn.Language.Simple-import Zwirn.Language.TypeCheck.Types--compile :: SimpleTerm -> Expression-compile (SVar p n) = EVar p n-compile (SApp fun arg) = EApp (compile fun) (compile arg)-compile (SLambda x body) = abstract x (compile body)-compile (SNum (Just p) x) = EZwirn $ addInfo p $ pure $ ENum $ read $ unpack x-compile (SNum Nothing x) = EZwirn $ pure $ ENum $ read $ unpack x-compile (SText p x) = EZwirn $ addInfo p $ pure $ EText x-compile (SSeq xs) = ESeq $ map compile xs-compile (SStack xs) = EStack $ map compile xs-compile (SChoice i xs) = EChoice i $ map compile xs-compile (SInfix s1 n s2) = EApp (EApp (EVar Nothing n) (compile s1)) (compile s2)-compile (SBracket s) = compile s-compile SRest = EZwirn silence--abstract :: Name -> Expression -> Expression-abstract x (EVar _ n) | x == n = combI-abstract x (EApp fun arg) = combS (abstract x fun) (abstract x arg)-abstract x (ESeq xs) = ESeq $ map (abstract x) xs-abstract x (EStack xs) = EStack $ map (abstract x) xs-abstract x (EChoice i xs) = EChoice i $ map (abstract x) xs-abstract _ k = combK k--combS :: Expression -> Expression -> Expression-combS f = EApp (EApp (EVar Nothing "scomb") f)--combK :: Expression -> Expression-combK = EApp (EVar Nothing "const")--combI :: Expression-combI = EVar Nothing "id"--infixl 0 !--(!) :: Expression -> Expression -> Expression-(EZwirn fp) ! (EZwirn x) = EZwirn $ squeezeApply (fmap (\(ELam f) -> toZwirn . f . fromZwirn) fp) x-_ ! _ = error "Error in (!)"--link :: InterpreterEnv -> Expression -> Expression-link bs (EVar (Just p) n) = addPosExp p $ fromJust (lookupExp n bs)-link bs (EVar Nothing n) = fromJust (lookupExp n bs)-link bs (EApp f x) = link bs f ! link bs x-link bs (ESeq xs) = EZwirn $ fastcat $ map (toZwirn . link bs) xs-link bs (EStack xs) = EZwirn $ stack $ map (toZwirn . link bs) xs-link bs (EChoice i xs) = EZwirn $ chooseWithSeed i $ map (toZwirn . link bs) xs-link _ e = e--evaluate :: InterpreterEnv -> SimpleTerm -> Expression-evaluate bs = link bs . compile--addPosExp :: Position -> Expression -> Expression-addPosExp p (EZwirn x) = EZwirn $ withInfos (p :) x-addPosExp _ x = x--removePosExp :: Expression -> Expression-removePosExp (EZwirn z) = EZwirn $ removeInfo z-removePosExp x = x
− src/Zwirn/Language/Lexer.x
@@ -1,395 +0,0 @@-{-{-# LANGUAGE OverloadedStrings #-}-{-# OPTIONS_GHC -Wno-name-shadowing #-}-module Zwirn.Language.Lexer- ( -- * Invoking Alex- Alex- , AlexPosn (..)- , alexGetInput- , alexError- , runAlex- , alexMonadScan-- , Range (..)- , RangedToken (..)- , Token (..)- , scanMany- , increaseChoice- , setEditorNum- , getEditorNum- , setInitialLineNum- , lineLexer- , typeLexer- ) where--{-- Lexer.hs - lexer for zwirn, code adapted from- https://serokell.io/blog/lexing-with-alex- Copyright (C) 2023, Martin Gius-- This library is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This library is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this library. If not, see <http://www.gnu.org/licenses/>.--}--import Data.Text (Text)-import qualified Data.Text as Text-import Control.Monad (when)-}--%wrapper "monadUserState-strict-text"--$digit = [0-9]-$alphasmall = [a-z]-$alpha = [a-zA-Z]--@id = ($alphasmall) ($alpha | $digit | \_ )*-@singles = ("&" | "$" | "?" | "#" | "." | "^")-@otherops = ("|" | "=" | "~" | "<" | ">" | "%")-@specialop = ("*" | "/" | "'" | "+" | "-")-@op = ((@singles (@singles | @otherops | @specialop)*) | ((@otherops | @specialop) (@singles | @otherops | @specialop)+))-@num = ("-")? ($digit)+ ("." ($digit)+)?-@path = $white ($alpha | "/" | ".")+--tokens :---<0> $white+ ;--<line> (.+ (\n?) | \n) { mkLine }--<ty> $white+ ;-<ty> $alpha+ "." ;-<ty> "=>" { tok Context }-<ty> "->" { tok Arrow }-<ty> "(" { tok LPar }-<ty> ")" { tok RPar }-<ty> "," { tok Comma }-<ty> "Text" { tok TextToken }-<ty> "Number" { tok NumberToken }-<ty> "Map" { tok MapToken }-<ty> "Bus" { tok BusToken }-<ty> @id { tokText VarToken }-<ty> [A-Z] $alphasmall+ { tokText TypeClass }-<ty> @id { tokText Identifier }-<ty> @op { tokText Operator }-<ty> @specialop { tokText SpecialOp }---- Multi Line Comments--<0> "{-" { nestComment `andBegin` comment }-<0> "-}" { \_ _ -> alexError "Error: unexpected closing comment" }-<comment> "{-" { nestComment }-<comment> "-}" { unnestComment }-<comment> . ;-<comment> \n ;---- Single Line Comments--<0> "--" .* ;---- Repeat-<0> "!" { tok Repeat }-<0> "!"($digit+) { tokText (\t -> RepeatNum $ Text.drop 1 t) }---- Parenthesis-<0> "(" { tok LPar }-<0> ")" { tok RPar }---- Sequences-<0> "[" { tok LBrack }-<0> "]" { tok RBrack }---- Stacks-<0> "," { tok Comma }---- Choice-<0> "|" { tok Pipe }---- Enum-<0> ".." { tok Enum }---- Polyrhythm-<0> "%" { tok Poly }---- Euclid-<0> "{" { tok LBraces }-<0> "}" { tok RBraces }---- Lambda-<0> "\" { tok Lambda }-<0> "->" { tok Arrow }---- Actions-<0> ";" { tok Colon }-<0> "<-" { tok StreamA }-<0> ":cps" { tok TempoCps }-<0> ":bpm" { tok TempoBpm }-<0> ":t" { tok TypeA }-<0> "=" { tok Assign }-<0> ":show" { tok ShowA }-<0> ":config" { tok ConfigA }-<0> ":resetconfig" { tok ResetConfigA }-<0> ":info" { tok InfoA }-<0> (":load") @path { tokText (\t -> LoadA $ Text.drop 6 t) }---- Identifiers-<0> @id { tokText Identifier }---- Operator Identifier-<0> \( @op \) { tokText (Identifier . rmFirstLast) }---- Constants-<0> @num { tokText Number }-<0> \"[^\"]*\" { tokText String }-<0> "~" { tok Rest }---- Operators-<0> @op { tokText Operator }-<0> @specialop { tokText SpecialOp }---- Alternations-<0> "<" { tok LAngle }-<0> ">" { tok RAngle }--{-data AlexUserState = AlexUserState- { nestLevel :: Int- , choiceNum :: Int- , editorNum :: Int- }--alexInitUserState :: AlexUserState-alexInitUserState = AlexUserState { nestLevel = 0, choiceNum = 0, editorNum = 0}--get :: Alex AlexUserState-get = Alex $ \s -> Right (s, alex_ust s)--put :: AlexUserState -> Alex ()-put s' = Alex $ \s -> Right (s{alex_ust = s'}, ())--modify :: (AlexUserState -> AlexUserState) -> Alex ()-modify f = Alex $ \s -> Right (s{alex_ust = f (alex_ust s)}, ())--alexEOF :: Alex RangedToken-alexEOF = do- startCode <- alexGetStartCode- when (startCode == comment) $- alexError "Error: unclosed comment"- (pos, _, _, _) <- alexGetInput- pure $ RangedToken EOF (Range pos pos)--data Range = Range- { start :: AlexPosn- , stop :: AlexPosn- } deriving (Eq, Show)--data RangedToken = RangedToken- { rtToken :: Token- , rtRange :: Range- } deriving (Eq, Show)--data Token- -- Identifiers- = Identifier Text- -- Constants- | String Text- | Number Text- | Rest- -- Operators- | Operator Text- | SpecialOp Text- -- Repeat- | Repeat- | RepeatNum Text- -- Parenthesis- | LPar- | RPar- -- Sequences- | LBrack- | RBrack- -- Stacks- | Comma- -- Alternations- | LAngle- | RAngle- -- Choice- | Pipe- -- Polyrhythm- | Poly- -- Euclid- | LBraces- | RBraces- -- Lambda- | Lambda- | Arrow- -- Enum- | Enum- -- Actions- | Colon- | StreamA- | TempoCps- | TempoBpm- | TypeA- | ShowA- | ConfigA- | ResetConfigA- | Assign- | LoadA Text- | InfoA- -- Line & Block Tokens- | LineT Text- | BlockSep- -- Type Tokens- | Context- | TextToken- | NumberToken- | MapToken- | BusToken- | VarToken Text- | TypeClass Text- -- EOF- | EOF- deriving (Eq)--instance Show Token where- show (Identifier s) = show s- show (String s) = show s- show (Number d) = show d- show Rest = quoted "~"- show (Operator o) = show o- show (SpecialOp o) = show o- show Repeat = quoted "!"- show (RepeatNum x) = quoted "!" ++ show x- show LPar = quoted "("- show RPar = quoted ")"- show LBrack = quoted "["- show RBrack = quoted "]"- show Comma = quoted ","- show LAngle = quoted "<"- show RAngle = quoted ">"- show Pipe = quoted "|"- show Poly = quoted "%"- show LBraces = quoted "{"- show RBraces = quoted "}"- show Lambda = quoted "\\"- show Arrow = quoted "->"- show Colon = quoted ";"- show Enum = quoted ".."- show StreamA = quoted "<-"- show TempoCps = ":cps"- show TempoBpm = ":bpm"- show TypeA = quoted ":t"- show ShowA = quoted ":show"- show ConfigA = quoted ":config"- show ResetConfigA = quoted ":resetconfig"- show Assign = quoted "="- show (LoadA x) = ":load " <> show x- show InfoA = quoted ":info"- show (LineT t) = "line " <> show t- show BlockSep = "block"- show Context = "=>"- show TextToken = "Text"- show NumberToken = "Number"- show MapToken = "Map"- show BusToken = "Bus"- show (VarToken t) = show t- show (TypeClass c) = show c- show EOF = "end of file"--quoted :: String -> String-quoted s = "'" ++ s ++ "'"--mkRange :: AlexInput -> Int -> Range-mkRange (st, _, _, str) len = Range{start = st, stop = end}- where- end = Text.foldl' alexMove st $ Text.take len str--mkLine :: AlexAction RangedToken-mkLine inp@(_, _, _, str) len = case Text.all (\c -> elem c ("\n\t " :: String)) (Text.take len str) of- True -> tok BlockSep inp len- False -> pure RangedToken- { rtToken = LineT $ Text.map replaceTab (Text.take len str)- , rtRange = mkRange inp len- }---- | replace all tabs with a single space, since codemirror sees tabs as one column-replaceTab :: Char -> Char-replaceTab '\t' = ' '-replaceTab x = x--rmFirstLast :: Text -> Text-rmFirstLast t = Text.init (Text.tail t)--tok :: Token -> AlexAction RangedToken-tok ctor inp len =- pure RangedToken- { rtToken = ctor- , rtRange = mkRange inp len- }--tokText :: (Text -> Token) -> AlexAction RangedToken-tokText f inp@(_, _, _, str) len =- pure RangedToken- { rtToken = f $ Text.take len str- , rtRange = mkRange inp len- }--nestComment :: AlexAction RangedToken-nestComment input len = do- modify $ \s -> s{nestLevel = nestLevel s + 1}- skip input len--unnestComment :: AlexAction RangedToken-unnestComment input len = do- state <- get- let level = nestLevel state - 1- put state{nestLevel = level}- when (level == 0) $- alexSetStartCode 0- skip input len--increaseChoice :: Alex Int-increaseChoice = do- (AlexUserState c x e) <- get- put $ AlexUserState c (x+1) e- return x--getEditorNum :: Alex Int-getEditorNum = do- (AlexUserState _ _ e) <- get- return e--setEditorNum :: Int -> Alex ()-setEditorNum i = do- (AlexUserState c x _) <- get- put $ AlexUserState c x i--setInitialLineNum :: Int -> Alex ()-setInitialLineNum i = Alex alex- where alex s = Right (s {alex_pos = AlexPn x i c }, ())- where AlexPn x _ c = alex_pos s--lineLexer :: Alex ()-lineLexer = alexSetStartCode line--typeLexer :: Alex ()-typeLexer = alexSetStartCode ty--scanMany :: Text -> Either String [RangedToken]-scanMany input = runAlex input go- where- go = do- output <- lineLexer >> alexMonadScan- if rtToken output == EOF- then pure [output]- else ((output) :) <$> go-}
− src/Zwirn/Language/Parser.y
@@ -1,375 +0,0 @@-{-{-# LANGUAGE OverloadedStrings #-}-module Zwirn.Language.Parser- ( parseActionsWithPos- , parseActions- , parseBlocks- , parseScheme- ) where--import Data.Text (Text)-import qualified Data.Text as Text-import Data.Maybe (fromJust)-import Data.Monoid (First (..))-import Data.List (intercalate, sortOn)--import qualified Zwirn.Language.Lexer as L-import Zwirn.Language.Syntax-import Zwirn.Language.TypeCheck.Types-import Zwirn.Language.TypeCheck.Infer-import Zwirn.Language.Block--{-- Parser.hs - parser for zwirn, code adapted from- https://serokell.io/blog/parsing-with-happy- Copyright (C) 2023, Martin Gius-- This library is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This library is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this library. If not, see <http://www.gnu.org/licenses/>.--}---}--%name parse term-%name pActions actions-%name pBlocks blocks-%name pScheme scheme-%tokentype { L.RangedToken }-%errorhandlertype explist-%error { parseError }-%monad { L.Alex } { >>= } { pure }-%lexer { lexer } { L.RangedToken L.EOF _ }-%expect 0--%token- -- Identifiers- identifier { L.RangedToken (L.Identifier _) _ }- -- Operators- operator { L.RangedToken (L.Operator _) _ }- specop { L.RangedToken (L.SpecialOp _) _ }- -- Constants- string { L.RangedToken (L.String _) _ }- number { L.RangedToken (L.Number _) _ }- line { L.RangedToken (L.LineT _) _ }- bsep { L.RangedToken (L.BlockSep) _ }- '~' { L.RangedToken L.Rest _ }- -- Repeat- '!' { L.RangedToken L.Repeat _ }- repnum { L.RangedToken (L.RepeatNum _) _}- -- Parenthesis- '(' { L.RangedToken L.LPar _ }- ')' { L.RangedToken L.RPar _ }- -- Sequences- '[' { L.RangedToken L.LBrack _ }- ']' { L.RangedToken L.RBrack _ }- -- Stacks- ',' { L.RangedToken L.Comma _ }- -- Alternations- '<' { L.RangedToken L.LAngle _ }- '>' { L.RangedToken L.RAngle _ }- -- Choice- '|' { L.RangedToken L.Pipe _ }- -- Enum- '..' { L.RangedToken L.Enum _ }- -- Polyrhythm- '%' { L.RangedToken L.Poly _ }- -- Lambda- '\\' { L.RangedToken L.Lambda _ }- '->' { L.RangedToken L.Arrow _ }- -- Actions- ';' { L.RangedToken L.Colon _ }- '<-' { L.RangedToken L.StreamA _ }- ':cps' { L.RangedToken L.TempoCps _ }- ':bpm' { L.RangedToken L.TempoBpm _ }- ':t' { L.RangedToken L.TypeA _ }- ':show' { L.RangedToken L.ShowA _ }- ':config' { L.RangedToken L.ConfigA _ }- ':resetconfig' { L.RangedToken L.ResetConfigA _ }- '=' { L.RangedToken L.Assign _ }- ':load' { L.RangedToken (L.LoadA _ ) _}- ':info' { L.RangedToken L.InfoA _ }- -- Type Tokens- '=>' { L.RangedToken L.Context _ }- textT { L.RangedToken L.TextToken _ }- numT { L.RangedToken L.NumberToken _ }- mapT { L.RangedToken L.MapToken _ }- busT { L.RangedToken L.BusToken _ }- varT { L.RangedToken (L.VarToken _) _ }- classT { L.RangedToken (L.TypeClass _) _ }--%%----------------------------------------------------------------------------------------- utilities -----------------------------------------------------------------------------------------optional(p)- : { Nothing }- | p { Just $1 }--many_rev(p)- : { [] }- | many_rev(p) p { $2 : $1 }--many(p)- : many_rev(p) { reverse $1 }--some_rev(p)- : p { [$1] }- | some_rev(p) p { $2 : $1 }--some(p)- : some_rev(p) { reverse $1 }--sepBy_rev(p, sep)- : p { [$1] }- | sepBy_rev(p, sep) sep p { $3 : $1 }--sepBy(p, sep)- : sepBy_rev(p, sep) { reverse $1 }--sepBy_rev2(p, sep)- : p sep p { [$3, $1] }- | sepBy_rev2(p, sep) sep p { $3 : $1 }--sepBy2(p, sep)- : sepBy_rev2(p, sep) { reverse $1 }--------------------------------------------------------------------------------------- parsing terms ---------------------------------------------------------------------------------------atom :: { Term }- : identifier { % (mkAtom TVar) $1 }- | number { % (mkAtom TNum) $1 }- | string { % (mkAtom TText) $1 }- | '~' { TRest }--simpleseq :: { [Term] }- : infix %shift { [$1] }- | infix simpleseq { $1: $2 }--seq :: { Term }- : simpleseq { TSeq $1 }- | infix '..' infix { TEnum Run $1 $3 }- | infix infix '..' infix { TEnumThen Run $1 $2 $4 }- | { TRest }--sequence :: { Term }- : '[' seq ']' { $2 }--choice :: { Term }- : '[' sepBy2(simpleseq, '|') ']' { % L.increaseChoice >>= \x -> return $ TChoice x (map TSeq $2) }- | '[' simpleseq '|' '..' simpleseq ']' { TEnum Choice (TSeq $2) (TSeq $5) }- | '[' simpleseq '|' simpleseq '..' simpleseq ']' { TEnumThen Choice (TSeq $2) (TSeq $4) (TSeq $6) }--lambda :: { Term }- : '\\' some(identifier) '->' term %shift { TLambda (map unTok $2) $4 }--polyrhythm :: { Term }- : simple '%' simple %shift { TPoly $1 $3 }--repeat :: { Term }- : simple repnum { TRepeat $1 (Just $ read $ Text.unpack $ unTok $2) }- | simple '!' { TRepeat $1 Nothing }--stack :: { Term }- : '[' sepBy2(simpleseq, ',') ']' { TStack (map TSeq $2) }- | '[' simpleseq ',' '..' simpleseq ']' { TEnum Cord (TSeq $2) (TSeq $5) }- | '[' simpleseq ',' simpleseq '..' simpleseq ']' { TEnumThen Cord (TSeq $2) (TSeq $4) (TSeq $6) }--alt :: { Term }- : simpleseq { TAlt $1 }- | infix '..' infix { TEnum Alt $1 $3 }- | infix infix '..' infix { TEnumThen Alt $1 $2 $4 }--alternation :: { Term }- : '<' alt '>' { $2 }--bracket :: { Term }- : '(' term ')' { TBracket $2 }--simple :: { Term }- : atom { $1 }- | alternation { $1 }- | sequence { $1 }- | choice { $1 }- | stack { $1 }- | lambda { $1 }- | polyrhythm { $1 }- | repeat { $1 }- | bracket { $1 }---- special operators are left-associative-specialinfix :: { Term }- : specialinfix specop simple %shift { TInfix $1 (unTok $2) $3 }- | simple %shift { $1 }---- all other operators are assumed to be right-associative, AST rotation will fix it--- this definition is for use inside of sequences-infix :: { Term }- : specialinfix operator infix %shift { TInfix $1 (unTok $2) $3 }- | specialinfix %shift { $1 }---- application is left-associative, binds stronger than operators--- outside of sequences-app :: { Term }- : app specialinfix %shift { TApp $1 $2 }- | specialinfix %shift {$1}--sectionR :: { Term }- : operator app %shift { TSectionR (unTok $1) $2 }--sectionL :: { Term }- : app operator %shift { TSectionL $1 (unTok $2) }---- operators outside of sequences have the weakest binding-term :: { Term }- : app operator term %shift { TInfix $1 (unTok $2) $3 }- | app %shift { $1 }- | sectionR %shift { $1 }- | sectionL %shift { $1 }-------------------------------------------------------------------------------------- parsing actions --------------------------------------------------------------------------------------def :: { Def }- : identifier many(identifier) '=' term { Let (unTok $1) (map unTok $2) $4 }--action :: { Action }- : string '<-' term { StreamAction (unTok $1) $3 }- | number '<-' term { StreamAction (unTok $1) $3 }- | identifier '<-' term { StreamSet (unTok $1) $3 }- | ':cps' number { StreamSetTempo CPS (unTok $2) }- | ':bpm' number { StreamSetTempo BPM (unTok $2) }- | '!' term { StreamOnce $2 }- | ':config' { ConfigPath }- | ':resetconfig' { ResetConfig }- | def { Def $1 }- | ':t' term { Type $2 }- | ':show' term { Show $2 }- | ':load' { Load $ unTok $1 }- | ':info' identifier { Info $ unTok $2 }--actionsrecrev :: { [Action] }- : actionsrecrev ';' action { $3:$1 }- | action { [$1] }--actions :: { [Action] }- : actionsrecrev ';' { reverse $1 }- | actionsrecrev { reverse $1 }- | { [] }--------------------------------------------------------------------------------------- parsing blocks --------------------------------------------------------------------------------------block :: { Block }- : some(line) { toBlock $1 }--blocksrec :: { [Block] }- : blocksrec some(bsep) block { $3:$1 }- | block { [$1] }--blocks :: { [Block] }- : some(bsep) blocksrec some(bsep) { $2 }- | some(bsep) blocksrec { $2 }- | blocksrec some(bsep) { $1 }- | blocksrec { $1 }--------------------------------------------------------------------------------------- parsing types ---------------------------------------------------------------------------------------atomType :: { Type }- : textT { TypeCon "Text" }- | numT { TypeCon "Number" }- | mapT { TypeCon "Map" }- | busT { TypeCon "Bus" }- | varT { TypeVar (unTok $1) }--fullType :: { Type }- : atomType { $1 }- | fullType '->' fullType %shift { TypeArr $1 $3 }- | '(' fullType ')' { $2 }--predicate :: { Predicate }- : classT varT { IsIn (unTok $1) (TypeVar (unTok $2))}--predicates :: { [Predicate] }- : predicate '=>' { [$1] }- | { [] }--scheme :: { Scheme }- : predicates fullType %shift { generalize $1 $2 }---{--parseError :: (L.RangedToken, [String]) -> L.Alex a-parseError (L.RangedToken t _,poss) = do- (L.AlexPn _ ln column, _, _, _) <- L.alexGetInput- L.alexError $ "Parse error at line " <> show ln <> ", column " <> show column- <> "\n\tunexpected " <> show t- <> "\n\texpecting " <> (intercalate "," poss)--lexer :: (L.RangedToken -> L.Alex a) -> L.Alex a-lexer = (=<< L.alexMonadScan)--unTok :: L.RangedToken -> Text-unTok (L.RangedToken (L.Identifier x) _) = x-unTok (L.RangedToken (L.Number x) _ ) = x-unTok (L.RangedToken (L.String x) _ )= x-unTok (L.RangedToken (L.Operator x) _) = x-unTok (L.RangedToken (L.SpecialOp x) _) = x-unTok (L.RangedToken (L.LoadA x) _) = x-unTok (L.RangedToken (L.LineT x) _) = x-unTok (L.RangedToken (L.VarToken x) _) = x-unTok (L.RangedToken (L.TypeClass x) _) = x-unTok (L.RangedToken (L.RepeatNum x) _) = x-unTok _ = error "can't untok"---mkAtom :: (Position -> Text -> Term) -> L.RangedToken -> L.Alex Term-mkAtom constr tok@(L.RangedToken _ range) = do- ed <- L.getEditorNum- return $ constr (toPosition ed range) (unTok tok)--toPosition :: Int -> L.Range -> Position-toPosition ed (L.Range (L.AlexPn _ line start) (L.AlexPn _ _ end)) = Pos line start end ed--toBlock :: [L.RangedToken] -> Block-toBlock [] = error "Can't happen"-toBlock xs = Block start end content- where ls = sortOn (\(x,_) -> x) $ map (\r -> (getLn r,unTok r)) xs- (start, _) = head ls- (end, _) = last ls- content = Text.concat $ map snd ls- getLn (L.RangedToken _ (L.Range (L.AlexPn _ l _) _)) = l---parseActionsWithPos :: Int -> Int -> Text -> Either String [Action]-parseActionsWithPos ln ed input = L.runAlex input (L.setEditorNum ed >> L.setInitialLineNum ln >> pActions)--parseActions :: Text -> Either String [Action]-parseActions input = L.runAlex input pActions--parseBlocks :: Int -> Text -> Either String [Block]-parseBlocks line input = L.runAlex input (L.lineLexer >> L.setInitialLineNum line >> pBlocks)--parseScheme :: Text -> Either String Scheme-parseScheme input = L.runAlex input (L.typeLexer >> pScheme)--}
− src/Zwirn/Language/Pretty.hs
@@ -1,121 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# OPTIONS_GHC -Wno-orphans #-}--module Zwirn.Language.Pretty- ( ppterm,- ppscheme,- ppTermHasType,- )-where--{-- Pretty.hs - pretty printer for the AST and the types- Copyright (C) 2023, Martin Gius-- This library is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This library is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this library. If not, see <http://www.gnu.org/licenses/>.--}--import Data.List (intercalate)-import Data.Text (unpack)-import Text.PrettyPrint-import Zwirn.Language.Syntax-import Zwirn.Language.TypeCheck.Constraint-import Zwirn.Language.TypeCheck.Types-import Prelude hiding ((<>))--parensIf :: Bool -> Doc -> Doc-parensIf True = parens-parensIf False = id--class Pretty p where- ppr :: Int -> p -> Doc--instance Pretty Name where- ppr _ x = text $ unpack x--instance Pretty Type where- ppr p (TypeArr a b) = parensIf (isArrow a) (ppr p a) <+> text "->" <+> ppr p b- where- isArrow TypeArr {} = True- isArrow _ = False- ppr p (TypeVar a) = ppr p a- ppr _ (TypeCon a) = text $ unpack a--instance Pretty Predicate where- ppr p (IsIn c t) = text (unpack c) <+> ppr p t--instance Pretty [Predicate] where- ppr p ps = parensIf (length ps > 1) (hcat (punctuate comma (map (ppr p) ps)))--instance Pretty (Qualified Type) where- ppr p (Qual [] t) = ppr p t- ppr p (Qual ps t) = ppr p ps <+> text "=>" <+> ppr p t--instance Pretty Scheme where- ppr p (Forall _ t) = ppr p t--instance Pretty Term where- ppr _ (TVar _ x) = text $ unpack x- ppr _ TRest = text "~"- ppr _ (TText _ x) = text $ unpack x- ppr _ (TNum _ x) = double $ read $ unpack x- ppr p (TRepeat t (Just i)) = ppr p t <> text "!" <> int i- ppr p (TRepeat t Nothing) = ppr p t <> text "!"- ppr p (TSeq [t]) = ppr p t- ppr p (TSeq ts) = brackets (hcat (punctuate space (map (ppr p) ts)))- ppr p (TAlt ts) = text "<" <> hcat (punctuate space (map (ppr p) ts)) <> text ">"- ppr p (TChoice _ ts) = brackets (hcat $ punctuate (text "|") (map (ppr p) ts))- ppr p (TStack ts) = brackets (hcat $ punctuate comma (map (ppr p) ts))- ppr p (TPoly t1 t2) = ppr p t1 <> text "%" <> ppr p t2- ppr p (TApp t1 t2) = parensIf (p > 0) (ppr (p + 1) t1 <+> ppr p t2)- ppr p (TInfix t1 n t2) = ppr p t1 <+> text (unpack n) <+> ppr p t2- ppr p (TBracket t) = parens (ppr p t)- ppr p (TLambda vs t) = text "\\" <> hcat (punctuate space $ map (text . unpack) vs) <+> text "->" <+> ppr p t- ppr p (TSectionL t n) = ppr p t <+> text (unpack n)- ppr p (TSectionR n t) = text (unpack n) <+> ppr p t- ppr p (TEnum Run x y) = brackets (ppr p x <+> text ".." <+> ppr p y)- ppr p (TEnumThen Alt x y z) = text "<" <> (ppr p x <+> ppr p y <+> text ".." <+> ppr p z) <> text ">"- ppr p (TEnum Alt x y) = text "<" <> (ppr p x <+> text ".." <+> ppr p y) <> text ">"- ppr p (TEnumThen Run x y z) = brackets (ppr p x <+> ppr p y <+> text ".." <+> ppr p z)- ppr p (TEnum Cord x y) = brackets (ppr p x <+> text ", .." <+> ppr p y)- ppr p (TEnumThen Cord x y z) = brackets (ppr p x <+> text "," <+> ppr p y <+> text ".." <+> ppr p z)- ppr p (TEnum Choice x y) = brackets (ppr p x <+> text "| .. " <+> ppr p y)- ppr p (TEnumThen Choice x y z) = brackets (ppr p x <+> text "|" <+> ppr p y <+> text ".." <+> ppr p z)--instance Pretty (Term, Scheme) where- ppr p (t, s) = ppr p t <+> text "::" <+> ppr p s--pptype :: Type -> String-pptype = render . ppr 0--ppscheme :: Scheme -> String-ppscheme = render . ppr 0--ppterm :: Term -> String-ppterm = render . ppr 0--ppTermHasType :: (Term, Scheme) -> String-ppTermHasType = render . ppr 0--instance Show TypeError where- show (UnificationFail a b) =- concat ["Cannot unify types: \n\t", pptype a, " ~ ", pptype b]- show (UnificationMismatch as bs) =- concat ["Cannot unify types: \n\t", intercalate "," $ map pptype as, " ~ ", intercalate "," $ map pptype bs]- show (InfiniteType a b) =- concat ["Cannot construct the infinite type: ", unpack a, " = ", pptype b]- show (Ambigious cs) =- concat ["Cannot not match expected type: '" ++ pptype a ++ "' with actual type: '" ++ pptype b ++ "'\n" | (a, b) <- cs]- show (UnboundVariable a) = "Not in scope: " ++ unpack a- show (NoInstance (IsIn c x)) = "No instance for " ++ unpack c ++ " " ++ pptype x
− src/Zwirn/Language/Rotate.hs
@@ -1,126 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Zwirn.Language.Rotate- ( runRotate,- runRotateUnsafe,- RotationError,- )-where--{-- Rotate.hs - syntax tree rotation, code adapted from- https://gist.github.com/heitor-lassarote/b20d6da0a9042d31e439befb8c236a4e- Copyright (C) 2023, Martin Gius-- This library is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This library is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this library. If not, see <http://www.gnu.org/licenses/>.--}--import Control.Monad.Except-import Control.Monad.Identity-import Zwirn.Language.Simple-import Zwirn.Language.Syntax--ops :: [Declaration]-ops =- [ ("*", Fixity LeftA 9),- ("/", Fixity LeftA 9),- ("$", Fixity RightA 0),- ("$|", Fixity RightA 0),- ("|$", Fixity RightA 0),- (".", Fixity RightA 9),- ("#", Fixity RightA 3),- ("++", Fixity RightA 4),- ("+", Fixity LeftA 6),- ("|+", Fixity LeftA 6),- ("+|", Fixity LeftA 6),- ("|*", Fixity LeftA 7),- ("*|", Fixity LeftA 7),- ("//", Fixity LeftA 7),- ("|/", Fixity LeftA 7),- ("/|", Fixity LeftA 7)- ]--defaultFixity :: Fixity-defaultFixity = Fixity LeftA 8--type RotationError = String--type Rotate a = ExceptT RotationError Identity a---- | Describes which action the rotation algorithm should use.-data Rotation- = -- | Fail due to the mixing of incompatible operators.- Fail- | -- | Keep the tree as it is.- Keep- | -- | Balance the tree to the left.- Rotate--runRotate :: SimpleTerm -> Either RotationError SimpleTerm-runRotate t = runIdentity $ runExceptT $ rotate t--runRotateUnsafe :: SimpleTerm -> SimpleTerm-runRotateUnsafe t = case runRotate t of- Left err -> error $ show err- Right r -> r---- | The Happy parser is written in a way so that it will always create a right-balanced AST.--- We compare the operators and indicate how to rotate the tree.-shouldRotate :: Fixity -> Fixity -> Rotation-shouldRotate (Fixity a p) (Fixity a' p') = case compare p p' of- LT -> Keep- EQ -> case (a, a') of- (LeftA, LeftA) -> Rotate- (RightA, RightA) -> Keep- (_, _) -> Fail- GT -> Rotate---- | Rebalances the tree to respect the associativity and precedence of the--- parsed operators.---- Not very efficient, but enough for demonstration purposes.-findOp :: OperatorSymbol -> Rotate Fixity-findOp o = case lookup o ops of- Just d -> return d- Nothing -> return defaultFixity--rotate :: SimpleTerm -> Rotate SimpleTerm-rotate (SInfix l op r) = do- -- Rotating the left side is unneeded since this grammar is very simple.- -- This is because trees are always right-balanced and the left side is- -- always an atom.- lRotated <- rotate l- rRotated <- rotate r- case rRotated of- SInfix l' op' r' -> do- opDec <- findOp op- opDec' <- findOp op'- case shouldRotate opDec opDec' of- Fail -> throwError "can't handle precedence of operators"- Keep -> return $ SInfix lRotated op rRotated- Rotate -> return $ SInfix (SInfix lRotated op l') op' r'- _ -> return $ SInfix lRotated op rRotated-rotate (SApp l r) = do- lRotated <- rotate l- rRotated <- rotate r- return $ SApp lRotated rRotated-rotate e@(SVar _ _) = return e-rotate e@(SText _ _) = return e-rotate e@(SNum _ _) = return e-rotate SRest = return SRest-rotate (SSeq ts) = fmap SSeq (mapM rotate ts)-rotate (SStack ts) = fmap SStack (mapM rotate ts)-rotate (SChoice n ts) = fmap (SChoice n) (mapM rotate ts)-rotate (SLambda vs t) = SLambda vs <$> rotate t-rotate (SBracket t) = fmap SBracket (rotate t)
− src/Zwirn/Language/Simple.hs
@@ -1,104 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Zwirn.Language.Simple- ( simplify,- simplifyDef,- SimpleTerm (..),- SimpleDef (..),- Position (..),- )-where--{-- Simple.hs - desugaring of the zwirn AST- Copyright (C) 2023, Martin Gius-- This library is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This library is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this library. If not, see <http://www.gnu.org/licenses/>.--}--import Data.Text as Text (Text, filter, pack)-import Zwirn.Language.Syntax---- simple representation of patterns-data SimpleTerm- = SVar (Maybe Position) Var- | SText Position Text- | SNum (Maybe Position) Text- | SRest- | SSeq [SimpleTerm]- | SStack [SimpleTerm]- | SChoice Int [SimpleTerm]- | SLambda Var SimpleTerm- | SApp SimpleTerm SimpleTerm- | SInfix SimpleTerm OperatorSymbol SimpleTerm- | SBracket SimpleTerm- deriving (Eq, Show)--data SimpleDef- = LetS Var SimpleTerm- deriving (Eq, Show)--simplify :: Term -> SimpleTerm-simplify (TVar p x) = SVar (Just p) x-simplify (TText p x) = SText p $ stripText x- where- stripText = Text.filter (/= '\"')-simplify (TNum p x) = SNum (Just p) x-simplify TRest = SRest-simplify x@(TRepeat _ _) = SSeq $ map simplify $ resolveRepeat x-simplify (TSeq ts) = SSeq (map simplify $ concatMap resolveRepeat ts)-simplify (TStack ts) = SStack (map simplify ts)-simplify (TChoice i ts) = SChoice i (map simplify ts)-simplify (TAlt ts) = SBracket $ SInfix (SSeq ss) "/" (SNum Nothing (pack $ show $ length ss))- where- ss = map simplify $ concatMap resolveRepeat ts-simplify (TPoly (TSeq ts) n) = SBracket $ SInfix (SInfix (SSeq ss) "/" (SNum Nothing (pack $ show $ length ss))) "*" (simplify n)- where- ss = map simplify $ concatMap resolveRepeat ts-simplify (TPoly x n) = SInfix (simplify x) "*" (simplify n)-simplify (TLambda [] t) = simplify t-simplify (TLambda (x : xs) t) = SLambda x (simplify $ TLambda xs t)-simplify (TApp x y) = SApp (simplify x) (simplify y)-simplify (TInfix x op y) = SInfix (simplify x) op (simplify y)-simplify (TSectionR op y) = SLambda "_x" (SInfix (SVar Nothing "_x") op (simplify y))-simplify (TSectionL x op) = SLambda "_x" (SInfix (simplify x) op (SVar Nothing "_x"))-simplify (TBracket x) = SBracket (simplify x)-simplify (TEnum Run x y) = SApp (SApp (SVar Nothing "runFromTo") (simplify x)) (simplify y)-simplify (TEnumThen Run x y z) = SApp (SApp (SApp (SVar Nothing "runFromThenTo") (simplify x)) (simplify y)) (simplify z)-simplify (TEnum Alt x y) = SApp (SApp (SVar Nothing "slowrunFromTo") (simplify x)) (simplify y)-simplify (TEnumThen Alt x y z) = SApp (SApp (SApp (SVar Nothing "slowrunFromThenTo") (simplify x)) (simplify y)) (simplify z)-simplify (TEnum Cord x y) = SApp (SApp (SVar Nothing "cordFromTo") (simplify x)) (simplify y)-simplify (TEnumThen Cord x y z) = SApp (SApp (SApp (SVar Nothing "cordFromThenTo") (simplify x)) (simplify y)) (simplify z)-simplify (TEnum Choice x y) = SApp (SApp (SVar Nothing "chooseFromTo") (simplify x)) (simplify y)-simplify (TEnumThen Choice x y z) = SApp (SApp (SApp (SVar Nothing "chooseFromThenTo") (simplify x)) (simplify y)) (simplify z)--simplifyDef :: Def -> SimpleDef-simplifyDef (Let x vs t) = LetS x (simplify $ TLambda vs t)--resolveRepeat :: Term -> [Term]-resolveRepeat t = case getTotalRepeat t of- TRepeat x (Just i) -> replicate i x- TRepeat x Nothing -> [x, x]- x -> [x]---- TODO : not completely right when Nothing followed by Just...-getRepeat :: (Term, Int) -> Term-getRepeat (TRepeat x (Just j), k) = getRepeat (x, j * k)-getRepeat (TRepeat x Nothing, k) = getRepeat (x, k + 1)-getRepeat (x, j) = TRepeat x (Just j)--getTotalRepeat :: Term -> Term-getTotalRepeat (TRepeat t (Just i)) = getRepeat (t, i)-getTotalRepeat (TRepeat t Nothing) = getRepeat (t, 2)-getTotalRepeat t = t
− src/Zwirn/Language/Syntax.hs
@@ -1,95 +0,0 @@-module Zwirn.Language.Syntax where--{-- Syntax.hs - definition of the zwirn language,- inspired by tidals mini-notation- Copyright (C) 2023, Martin Gius-- This library is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This library is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this library. If not, see <http://www.gnu.org/licenses/>.--}--import Data.Text (Text)--type Var = Text--type OperatorSymbol = Text--data Position = Pos- { pLine :: Int,- pStart :: Int,- pEnd :: Int,- pEditor :: Int- }- deriving (Eq, Show)--data EnumKind = Cord | Choice | Run | Alt deriving (Eq, Show)---- sugary representation of patterns-data Term- = TVar Position Text- | TText Position Text- | TNum Position Text- | TRest- | TRepeat Term (Maybe Int)- | TSeq [Term]- | TStack [Term]- | TAlt [Term]- | TChoice Int [Term]- | TPoly Term Term- | TLambda [Text] Term- | TApp Term Term- | TInfix Term Text Term- | TSectionR Text Term- | TSectionL Term Text- | TBracket Term- | TEnum EnumKind Term Term- | TEnumThen EnumKind Term Term Term- deriving (Eq, Show)--data Def- = Let Text [Text] Term- deriving (Eq, Show)--data Tempo- = CPS- | BPM- deriving (Eq, Show)--data Action- = StreamAction Text Term- | StreamSet Text Term- | StreamOnce Term- | StreamSetTempo Tempo Text- | ConfigPath- | ResetConfig- | Def Def- | Type Term- | Show Term- | Load Text- | Info Text- deriving (Eq, Show)--data Associativity- = NonA- | LeftA- | RightA- deriving (Eq, Show)--type Precedence = Int--data Fixity- = Fixity Associativity Precedence- deriving (Eq, Show)--type Declaration = (OperatorSymbol, Fixity)
− src/Zwirn/Language/TypeCheck/Constraint.hs
@@ -1,152 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}--module Zwirn.Language.TypeCheck.Constraint- ( Substitutable (..),- Subst (..),- TypeError (..),- Constraint,- runSolve,- )-where--{-- Constraint.hs - unification constraint solver adapted from- https://github.com/sdiehl/write-you-a-haskell/tree/master/chapter7/poly_constraints- Copyright (C) 2023, Martin Gius-- This library is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This library is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this library. If not, see <http://www.gnu.org/licenses/>.--}--import Control.Monad.Except-import Control.Monad.Identity-import qualified Data.Map as Map-import qualified Data.Set as Set-import Data.Text (Text)-import Zwirn.Language.Environment-import Zwirn.Language.TypeCheck.Types--data TypeError- = UnificationFail Type Type- | InfiniteType TypeVar Type- | UnboundVariable Text- | Ambigious [Constraint]- | UnificationMismatch [Type] [Type]- | NoInstance Predicate- deriving (Eq)--type Constraint = (Type, Type)--newtype Subst = Subst (Map.Map TypeVar Type)- deriving (Eq, Ord, Show, Semigroup, Monoid)--type Unifier = (Subst, [Constraint])---- | Constraint solver monad-type Solve a = ExceptT TypeError Identity a--class Substitutable a where- apply :: Subst -> a -> a- ftv :: a -> Set.Set TypeVar--instance Substitutable Type where- apply _ (TypeCon a) = TypeCon a- apply (Subst s) t@(TypeVar a) = Map.findWithDefault t a s- apply s (t1 `TypeArr` t2) = apply s t1 `TypeArr` apply s t2-- ftv TypeCon {} = Set.empty- ftv (TypeVar a) = Set.singleton a- ftv (t1 `TypeArr` t2) = ftv t1 `Set.union` ftv t2--instance Substitutable Scheme where- apply (Subst s) (Forall as t) = Forall as $ apply s' t- where- s' = Subst $ foldr Map.delete s as- ftv (Forall as t) = ftv t `Set.difference` Set.fromList as--instance Substitutable Constraint where- apply s (t1, t2) = (apply s t1, apply s t2)- ftv (t1, t2) = ftv t1 `Set.union` ftv t2--instance Substitutable AnnotatedExpression where- apply s (Annotated x sc d) = Annotated x (apply s sc) d- ftv (Annotated _ s _) = ftv s--instance (Substitutable a) => Substitutable [a] where- apply = map . apply- ftv = foldr (Set.union . ftv) Set.empty--instance Substitutable InterpreterEnv where- apply s (IEnv ty cl) = IEnv (Map.map (apply s) ty) (apply s cl)- ftv (IEnv ty cl) = ftv (Map.elems ty) `Set.union` ftv cl--instance Substitutable Predicate where- apply s (IsIn x t) = IsIn x (apply s t)- ftv (IsIn _ t) = ftv t--instance (Substitutable t) => Substitutable (Qualified t) where- apply s (Qual ps t) = Qual (apply s ps) (apply s t)- ftv (Qual ps t) = ftv ps `Set.union` ftv t------------------------------------------------------------------------------------ Constraint Solver------------------------------------------------------------------------------------ | The empty substitution-emptySubst :: Subst-emptySubst = mempty---- | Compose substitutions-compose :: Subst -> Subst -> Subst-(Subst s1) `compose` (Subst s2) = Subst $ Map.map (apply (Subst s1)) s2 `Map.union` s1---- | Run the constraint solver-runSolve :: [Constraint] -> Either TypeError Subst-runSolve cs = runIdentity $ runExceptT $ solver st- where- st = (emptySubst, cs)--unifyMany :: [Type] -> [Type] -> Solve Subst-unifyMany [] [] = return emptySubst-unifyMany (t1 : ts1) (t2 : ts2) =- do- su1 <- unifies t1 t2- su2 <- unifyMany (apply su1 ts1) (apply su1 ts2)- return (su2 `compose` su1)-unifyMany t1 t2 = throwError $ UnificationMismatch t1 t2--unifies :: Type -> Type -> Solve Subst-unifies t1 t2 | t1 == t2 = return emptySubst-unifies (TypeVar v) t = v `bind` t-unifies t (TypeVar v) = v `bind` t-unifies (TypeArr t1 t2) (TypeArr t3 t4) = unifyMany [t1, t2] [t3, t4]-unifies t1 t2 = throwError $ UnificationFail t1 t2---- Unification solver-solver :: Unifier -> Solve Subst-solver (su, cs) =- case cs of- [] -> return su- ((t1, t2) : cs0) -> do- su1 <- unifies t1 t2- solver (su1 `compose` su, apply su1 cs0)--bind :: TypeVar -> Type -> Solve Subst-bind a t- | t == TypeVar a = return emptySubst- | occursCheck a t = throwError $ InfiniteType a t- | otherwise = return (Subst $ Map.singleton a t)--occursCheck :: (Substitutable a) => TypeVar -> a -> Bool-occursCheck a t = a `Set.member` ftv t
− src/Zwirn/Language/TypeCheck/Infer.hs
@@ -1,199 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Zwirn.Language.TypeCheck.Infer- ( inferTerm,- generalize,- )-where--{-- Infer.hs - type inference algorithm adapted from- https://github.com/sdiehl/write-you-a-haskell/tree/master/chapter7/poly_constraints- Copyright (C) 2023, Martin Gius-- This library is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This library is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this library. If not, see <http://www.gnu.org/licenses/>.--}--import Control.Monad (replicateM)-import Control.Monad.Except-import Control.Monad.Reader-import Control.Monad.State-import Data.List (nub)-import qualified Data.Map as Map-import qualified Data.Set as Set-import Data.Text (Text, pack)--- import Zwirn.Language.TypeCheck.Env as Env--import Zwirn.Language.Environment-import Zwirn.Language.Simple-import Zwirn.Language.TypeCheck.Constraint-import Zwirn.Language.TypeCheck.Types---- | Inference monad-type Infer a =- ( ReaderT- InterpreterEnv -- Typing environment- ( StateT -- Inference state- InferState- ( Except -- Inference errors- TypeError- )- )- a -- Result- )---- | Inference state-newtype InferState = InferState {count :: Int}---- | Initial inference state-initInfer :: InferState-initInfer = InferState {count = 0}------------------------------------------------------------------------------------ Inference------------------------------------------------------------------------------------ | Run the inference monad-runInfer :: InterpreterEnv -> Infer a -> Either TypeError a-runInfer env m = runExcept $ evalStateT (runReaderT m env) initInfer---- | Solve for the toplevel type of an expression in a given environment-inferTerm :: InterpreterEnv -> SimpleTerm -> Either TypeError Scheme-inferTerm env ex = case runInfer env (infer ex) of- Left err -> Left err- Right (ty, ps, cs) -> case runSolve cs of- Left err -> Left err- Right subst -> case runInfer env (filterAndCheck (apply subst ps) (apply subst ty)) of- Left err -> Left err- Right xs -> Right $ closeOver xs $ apply subst ty---- | Return the internal constraints used in solving for the type of an expression--- constraintsTerm :: Env -> SimpleTerm -> Either TypeError ([Constraint], Subst, Type, Scheme)--- constraintsTerm env ex = case runInfer env (infer ex) of--- Left err -> Left err--- Right (ty, cs) -> case runSolve cs of--- Left err -> Left err--- Right subst -> Right (cs, subst, ty, sc)--- where--- sc = closeOver $ apply subst ty---- | Canonicalize and return the polymorphic toplevel type.-closeOver :: [Predicate] -> Type -> Scheme-closeOver ps t = normalize $ generalize ps t---- | modified environment where x :: sc-inEnv :: (Name, Scheme) -> Infer a -> Infer a-inEnv (x, sc) m = do- let scope = insertType x sc- local scope m---- | Lookup type in the environment-lookupEnv :: Name -> Infer (Type, [Predicate])-lookupEnv x = do- env <- ask- case lookupType x env of- Nothing -> throwError $ UnboundVariable x- Just s -> instantiate s--letters :: [Text]-letters = map pack $ [1 ..] >>= flip replicateM ['a' .. 'z']--fresh :: Infer Type-fresh = do- s <- get- put s {count = count s + 1}- return $ TypeVar (letters !! count s)--instantiate :: Scheme -> Infer (Type, [Predicate])-instantiate (Forall as (Qual ps t)) = do- as' <- mapM (const fresh) as- let s = Subst $ Map.fromList $ zip as as'- return $ (apply s t, apply s ps)--generalize :: [Predicate] -> Type -> Scheme-generalize ps t = Forall as (Qual ps t)- where- as = Set.toList $ ftv t--filterAndCheck :: [Predicate] -> Type -> Infer [Predicate]-filterAndCheck [] _ = return []-filterAndCheck (p@(IsIn _ (TypeVar _)) : ps) t =- if or $ Set.map (\x -> elem x $ ftv p) (ftv t)- then (p :) <$> filterAndCheck ps t- else filterAndCheck ps t-filterAndCheck (p : ps) t = checkInstance p >> filterAndCheck ps t--checkInstance :: Predicate -> Infer ()-checkInstance p = do- (IEnv _ is) <- ask- (if p `elem` is then return () else throwError $ NoInstance p)--infer :: SimpleTerm -> Infer (Type, [Predicate], [Constraint])-infer expr = case expr of- SVar _ x -> do- (t, ps) <- lookupEnv x- return (t, ps, [])- SText _ _ -> return (textT, [], [])- SNum _ _ -> return (numberT, [], [])- SBracket s -> infer s- SRest -> do- tv <- fresh- return (tv, [], [])- SLambda x e -> do- tv <- fresh- (t, ps, c) <- inEnv (x, Forall [] (Qual [] tv)) (infer e)- return (tv `TypeArr` t, ps, c)- SApp e1 e2 -> do- (t1, ps1, c1) <- infer e1- (t2, ps2, c2) <- infer e2- tv <- fresh- return (tv, ps1 ++ ps2, c1 ++ c2 ++ [(t1, t2 `TypeArr` tv)])- SInfix e1 op e2 -> do- (t1, ps1, c1) <- infer e1- (t2, ps2, c2) <- infer e2- tv <- fresh- let u1 = t1 `TypeArr` (t2 `TypeArr` tv)- (u2, p3) <- lookupEnv op- return (tv, ps1 ++ ps2 ++ p3, c1 ++ c2 ++ [(u1, u2)])- SSeq (x : xs) -> do- (t, ps, cs) <- infer x- infs <- mapM infer xs- return (t, ps, cs ++ concatMap (\(_, _, y) -> y) infs ++ [(t, t') | t' <- map (\(y, _, _) -> y) infs])- SStack (x : xs) -> do- (t, ps, cs) <- infer x- infs <- mapM infer xs- return (t, ps, cs ++ concatMap (\(_, _, y) -> y) infs ++ [(t, t') | t' <- map (\(y, _, _) -> y) infs])- SChoice _ (x : xs) -> do- (t, ps, cs) <- infer x- infs <- mapM infer xs- return (t, ps, cs ++ concatMap (\(_, _, y) -> y) infs ++ [(t, t') | t' <- map (\(y, _, _) -> y) infs])- _ -> error "Can't happen"--normalize :: Scheme -> Scheme-normalize (Forall _ (Qual ps body)) = Forall (map snd ord) (Qual (map normpred ps) $ normtype body)- where- ord = zip (nub $ fv body) letters-- fv (TypeVar a) = [a]- fv (TypeArr a b) = fv a ++ fv b- fv (TypeCon _) = []-- normtype (TypeArr a b) = TypeArr (normtype a) (normtype b)- normtype (TypeCon a) = TypeCon a- normtype (TypeVar a) =- case Prelude.lookup a ord of- Just x -> TypeVar x- Nothing -> error "type variable not in signature"-- normpred (IsIn n t) = IsIn n (normtype t)
− src/Zwirn/Language/TypeCheck/Types.hs
@@ -1,88 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Zwirn.Language.TypeCheck.Types where--{-- Types.hs - defintion of types adapted from- https://github.com/sdiehl/write-you-a-haskell/tree/master/chapter7/poly_constraints- Copyright (C) 2023, Martin Gius-- This library is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This library is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this library. If not, see <http://www.gnu.org/licenses/>.--}--import Data.Text (Text)--type Name = Text--type TypeVar = Text--data Type- = TypeVar TypeVar- | TypeCon Text- | TypeArr Type Type- deriving (Show, Eq, Ord)--data Predicate- = IsIn Name Type- deriving (Show, Eq, Ord)--data Qualified t- = Qual [Predicate] t- deriving (Show, Eq, Ord)--data Scheme- = Forall [TypeVar] (Qualified Type)- deriving (Show, Eq)--type Instance = Predicate--numberT :: Type-numberT = TypeCon "Number"--textT :: Type-textT = TypeCon "Text"--mapT :: Type-mapT = TypeCon "Map"--busT :: Type-busT = TypeCon "Bus"--varA :: Type-varA = TypeVar "a"--varB :: Type-varB = TypeVar "b"--varC :: Type-varC = TypeVar "c"--isBasicType :: Scheme -> Bool-isBasicType (Forall [] (Qual [] (TypeCon "Bus"))) = False-isBasicType (Forall [] (Qual [] (TypeCon _))) = True-isBasicType (Forall _ (Qual [] (TypeVar _))) = True-isBasicType _ = False--isBus :: Scheme -> Bool-isBus (Forall [] (Qual [] (TypeCon "Bus"))) = True-isBus (Forall _ (Qual [] (TypeVar _))) = True-isBus _ = False--infixr 1 -->--(-->) :: Type -> Type -> Type-(-->) = TypeArr--unqual :: Type -> Qualified Type-unqual = Qual []
− src/Zwirn/Stream.hs
@@ -1,229 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}-{-# OPTIONS_GHC -Wno-type-defaults #-}--module Zwirn.Stream where--{-- Stream.hs - query and send messages, code adapted from- https://github.com/tidalcycles/Tidal/tree/dev/src/Sound/Tidal/Stream- Copyright (C) 2023, Martin Gius-- This library is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This library is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this library. If not, see <http://www.gnu.org/licenses/>.--}--import Control.Concurrent (forkIO)-import Control.Concurrent.MVar (MVar, modifyMVar_, newMVar, readMVar, swapMVar)-import Control.Monad (when)-import Data.Bifunctor (second)-import qualified Data.Map as Map-import Data.Maybe (catMaybes, isJust)-import Data.Text (Text, pack)-import qualified Data.Text as T-import GHC.Generics (Generic)-import qualified Network.Socket as N-import qualified Sound.Osc as O-import Sound.Osc.Time.Timeout (recvPacketTimeout)-import qualified Sound.Osc.Transport.Fd.Udp as O-import Sound.Tidal.Clock-import qualified Sound.Tidal.Clock as Clock-import Sound.Tidal.Link-import Zwirn.Core.Cord (stack)-import Zwirn.Core.Query-import qualified Zwirn.Core.Time as Z-import Zwirn.Language.Evaluate--type PlayMap = Map.Map Text (Zwirn Expression)--type BusMap = Map.Map Int (Zwirn Expression)--data StreamConfig = StreamConfig- { streamConfigPort :: Int,- streamConfigBusPort :: Int,- streamConfigAddress :: String- }- deriving (Generic)--data Stream = Stream- { sPlayMap :: MVar PlayMap,- sBusMap :: MVar BusMap,- sState :: MVar ExpressionMap,- sBusses :: MVar [Int],- sAddress :: RemoteAddress,- sBusAddress :: RemoteAddress,- sLocal :: O.Udp,- sClockRef :: ClockRef,- sClockConfig :: ClockConfig- }--type RemoteAddress = N.SockAddr--streamReplace :: Stream -> Text -> Zwirn Expression -> IO ()-streamReplace str key p = modifyMVar_ (sPlayMap str) (return . Map.insert key p)--streamReplaceBus :: Stream -> Int -> Zwirn Expression -> IO ()-streamReplaceBus str key p = modifyMVar_ (sBusMap str) (return . Map.insert key p)--streamSet :: Stream -> T.Text -> Expression -> IO ()-streamSet str x ex = modifyMVar_ (sState str) (return . Map.insert x ex)--streamSetCPS :: Stream -> Time -> IO ()-streamSetCPS s = Clock.setCPS (sClockConfig s) (sClockRef s)--streamSetBPM :: Stream -> Time -> IO ()-streamSetBPM s = Clock.setBPM (sClockRef s)--streamFirst :: Stream -> Zwirn Expression -> IO ()-streamFirst str z = do- dummy <- newMVar $ Map.singleton (pack "_streamOnceDummy_") z- Clock.clockOnce (tickAction dummy (sBusMap str) (sState str) (sBusses str) (sAddress str) (sBusAddress str) (sLocal str)) (sClockConfig str) (sClockRef str)--startStream :: StreamConfig -> MVar PlayMap -> MVar ExpressionMap -> ClockConfig -> IO Stream-startStream config zMV stMV conf = do- let target_address = streamConfigAddress config- target_port = streamConfigPort config- target_bus_port = streamConfigBusPort config- remote <- resolve target_address target_port- remoteBus <- resolve target_address target_bus_port- local <- O.udp_server 2323-- busMapMV <- newMVar Map.empty- bussesMV <- newMVar []-- _ <- forkIO $ handshake (N.addrAddress remote) local bussesMV-- cref <- clocked conf (tickAction zMV busMapMV stMV bussesMV (N.addrAddress remote) (N.addrAddress remoteBus) local)- return $ Stream zMV busMapMV stMV bussesMV (N.addrAddress remote) (N.addrAddress remoteBus) local cref conf--tickAction :: MVar PlayMap -> MVar BusMap -> MVar ExpressionMap -> MVar [Int] -> RemoteAddress -> RemoteAddress -> O.Udp -> (Time, Time) -> Double -> ClockConfig -> ClockRef -> (SessionState, SessionState) -> IO ()-tickAction zMV busMapMV stMV bussesMV remote remoteBus local (star, end) nudge cconf cref (ss, _) = do- cps <- Clock.getCPS cconf cref- vs <- processPlayMap (star, end) cps zMV stMV- bs <- processBusMap (star, end) busMapMV stMV bussesMV- mapM_ (processAndSend remote local nudge cconf cref ss) vs- mapM_ (processAndSend remoteBus local nudge cconf cref ss) bs--processPlayMap :: (Time, Time) -> Time -> MVar PlayMap -> MVar ExpressionMap -> IO [(Z.Time, O.Message)]-processPlayMap (star, end) cps zMV stMV = do- pm <- readMVar zMV- let p = playMapToCord pm- st <- readMVar stMV- let qs = findAllValuesWithTimeState (Z.Time (align star) 1, Z.Time (align end) 1) st p- vs = map (\(t, v, _) -> (t, v)) qs- sts = map (\(_, _, x) -> x) qs-- -- TODO: what about race conditions?- updateState stMV sts-- return $ (\(t, ex) -> (t, expressionToMessage (fromIntegral $ floor t) (realToFrac cps) ex)) <$> vs--processBusMap :: (Time, Time) -> MVar BusMap -> MVar ExpressionMap -> MVar [Int] -> IO [(Z.Time, O.Message)]-processBusMap (star, end) busMV stMV bussesMV = do- bm <- readMVar busMV- let bs = Map.toList bm- busses <- readMVar bussesMV- st <- readMVar stMV- return $ concatMap (\(i, p) -> second (busExpressionToMessage $ toBus busses i) <$> findAllValuesWithTime (Z.Time (align star) 1, Z.Time (align end) 1) st p) bs--toBus :: [Int] -> Int -> Int-toBus [] i = i-toBus xs i = xs !! (i `mod` length xs)--resolve :: String -> Int -> IO N.AddrInfo-resolve host port = do- let hints = N.defaultHints {N.addrSocketType = N.Stream}- addr : _ <- N.getAddrInfo (Just hints) (Just host) (Just $ show port)- return addr--playMapToCord :: PlayMap -> Zwirn Expression-playMapToCord = stack . Map.elems--align :: Time -> Time-align t = fromIntegral (floor $ t / 0.001) * 0.001--expressionToOSC :: Expression -> [O.Datum]-expressionToOSC (ENum n) = [O.float n]-expressionToOSC (EText n) = [O.string $ T.unpack n]-expressionToOSC (EMap m) = concatMap (\(k, v) -> O.string (T.unpack k) : expressionToOSC v) $ Map.toList m-expressionToOSC _ = []--additionalData :: Double -> Double -> [O.Datum]-additionalData cyc cps = [O.string "cps", O.float cps, O.string "cycle", O.float cyc]--expressionToMessage :: Double -> Double -> Expression -> O.Message-expressionToMessage cyc cps ex = O.message "/dirt/play" (additionalData cyc cps ++ expressionToOSC ex)--busExpressionToMessage :: Int -> Expression -> O.Message-busExpressionToMessage bus ex = O.message "/c_set" (O.int32 bus : expressionToOSC ex)--sendMessage :: RemoteAddress -> O.Udp -> Double -> Double -> (Double, O.Message) -> IO ()-sendMessage remote local latency extraLatency (time, m) = sendBndl remote local $ O.Bundle timeWithLatency [m]- where- timeWithLatency = time - latency + extraLatency--sendBndl :: RemoteAddress -> O.Udp -> O.Bundle -> IO ()-sendBndl remote local bndl = O.sendTo local (O.Packet_Bundle bndl) remote--defaultLatency :: Double-defaultLatency = 0.2--processAndSend :: RemoteAddress -> O.Udp -> Double -> ClockConfig -> ClockRef -> SessionState -> (Z.Time, O.Message) -> IO ()-processAndSend remote local nudge cconf cref ss (t, msg) = do- let onBeat = Clock.cyclesToBeat cconf (realToFrac ((\(Z.Time r _) -> fromRational r) t))-- on <- Clock.timeAtBeat cconf ss onBeat- onOSC <- Clock.linkToOscTime cref on-- sendMessage remote local defaultLatency nudge (onOSC, msg)--updateState :: MVar ExpressionMap -> [ExpressionMap] -> IO ()-updateState _ [] = return ()-updateState stmv (st : _) = modifyMVar_ stmv (const $ return st)--handshake :: RemoteAddress -> O.Udp -> MVar [Int] -> IO ()-handshake addr udp bussesMV = sendHandshake >> listen 0- where- sendHandshake :: IO ()- sendHandshake = O.sendTo udp (O.Packet_Message $ O.Message "/dirt/handshake" []) addr- listen :: Int -> IO ()- listen waits = do- ms <- recvMessagesTimeout 2 udp- if null ms- then do- checkHandshake waits -- there was a timeout, check handshake- listen (waits + 1)- else do- mapM_ respond ms- listen 0- checkHandshake :: Int -> IO ()- checkHandshake waits = do- busses <- readMVar bussesMV- when (null busses) $ do- -- when (waits == 0) $ print "Waiting for SuperDirt (v.1.7.2 or higher).."- sendHandshake- respond :: O.Message -> IO ()- respond (O.Message "/dirt/hello" _) = sendHandshake- respond (O.Message "/dirt/handshake/reply" xs) = do- prev <- swapMVar bussesMV $ bufferIndices xs- return ()- -- Only report the first time..- -- when (null prev) $ print "Connected to SuperDirt."- respond _ = return ()- bufferIndices :: [O.Datum] -> [Int]- bufferIndices [] = []- bufferIndices (x : xs')- | x == O.AsciiString (O.ascii "&controlBusIndices") = catMaybes $ takeWhile isJust $ map O.datum_integral xs'- | otherwise = bufferIndices xs'--recvMessagesTimeout :: Double -> O.Udp -> IO [O.Message]-recvMessagesTimeout n sock = maybe [] O.packetMessages <$> recvPacketTimeout n sock
+ src/zwirn-core/Zwirn/Core/Cord.hs view
@@ -0,0 +1,67 @@+{-# LANGUAGE FlexibleInstances #-}+{-# OPTIONS_GHC -Wno-orphans #-}++module Zwirn.Core.Cord where++{-+ Cord.hs - functions on parallel signals+ Copyright (C) 2025, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Zwirn.Core.Core+import Zwirn.Core.Time+import Zwirn.Core.Tree+import Zwirn.Core.Types++type Cord = ZwirnT Tree++liftList :: ([Tree (Value i a, st)] -> [Tree (Value i b, st)]) -> Cord st i a -> Cord st i b+liftList f = withInner g+ where+ g (Leaf x) = Branch $ f [Leaf x]+ g (Branch xs) = Branch $ f xs++liftListWithTimeState :: (Time -> st -> [Tree (Value i a, st)] -> [Tree (Value i b, st)]) -> Cord st i a -> Cord st i b+liftListWithTimeState f = withInnerTimeState g+ where+ g t st (Leaf x) = Branch $ f t st [Leaf x]+ g t st (Branch xs) = Branch $ f t st xs++instance HasSilence Tree where+ silence = zwirn $ const $ const $ Branch []++-- | group a list of cords+stack :: [Cord st i a] -> Cord st i a+stack zs = zwirn $ \t st -> Branch $ map (\x -> unzwirn x t st) zs++-- | get the current depth of the cord+depth :: Cord st i a -> Cord st i Int+depth c = zwirn z+ where+ z t st = pure (Value l t [], st)+ where+ ts = unzwirn c t st+ l = topLength ts++-- | pick a certain layer out of a cord, wrapping around+_pick :: Int -> Cord st i a -> Cord st i a+_pick i = withInner (look i)++collect :: Cord st i a -> Cord st i [Cord st i a]+collect c = map (`_pick` c) . enumFromTo 0 . (\x -> x - 1) <$> depth c++_cordmap :: (Cord st i a -> Cord st i b) -> Cord st i a -> Cord st i b+_cordmap f x = (stack . map f) =<< collect x
+ src/zwirn-core/Zwirn/Core/Core.hs view
@@ -0,0 +1,133 @@+{-# OPTIONS_GHC -Wno-orphans #-}++module Zwirn.Core.Core where++{-+ Core.hs - core functions and instances+ Copyright (C) 2025, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Data.Bifunctor+import Zwirn.Core.Time+import Zwirn.Core.Types++-- | indicates the current time+now :: (Applicative k) => ZwirnT k st i Time+now = zwirn $ \t st -> pure (Value t t [], st)++modulateTime :: (a -> Time -> st -> Time) -> a -> ZwirnT k st i b -> ZwirnT k st i b+modulateTime f x b = zwirn (\t st -> unzwirn b (f x t st) st)++withInner :: (k (Value i a, st) -> k (Value i b, st)) -> ZwirnT k st i a -> ZwirnT k st i b+withInner f x = zwirn $ \t st -> f $ unzwirn x t st++withInnerAndTime :: (Time -> k (Value i a, st) -> k (Value i b, st)) -> ZwirnT k st i a -> ZwirnT k st i b+withInnerAndTime f x = zwirn $ \t st -> f t (unzwirn x t st)++withInnerTimeState :: (Time -> st -> k (Value i a, st) -> k (Value i b, st)) -> ZwirnT k st i a -> ZwirnT k st i b+withInnerTimeState f x = zwirn $ \t st -> f t st (unzwirn x t st)++withInner2 :: (k (Value i a, st) -> k (Value i b, st) -> k (Value i c, st)) -> ZwirnT k st i a -> ZwirnT k st i b -> ZwirnT k st i c+withInner2 f x y = zwirn $ \t st -> f (unzwirn x t st) (unzwirn y t st)++withValueState :: (Functor k) => ((Value i a, st) -> (Value i b, st)) -> ZwirnT k st i a -> ZwirnT k st i b+withValueState f = withInner (fmap f)++withValue :: (Functor k) => (Value i a -> Value i b) -> ZwirnT k st i a -> ZwirnT k st i b+withValue f = withValueState (first f)++withA :: (Functor k) => (a -> a) -> ZwirnT k st i a -> ZwirnT k st i a+withA f = withValue (\v -> v {value = f $ value v})++withTime :: (Functor k) => (Time -> Time) -> ZwirnT k st i a -> ZwirnT k st i a+withTime f = withValue (\v -> v {time = f $ time v})++withInfo :: (Functor k) => (i -> i) -> ZwirnT k st i a -> ZwirnT k st i a+withInfo f = withValue (\v -> v {info = f <$> info v})++withInfos :: (Functor k) => ([i] -> [i]) -> ZwirnT k st i a -> ZwirnT k st i a+withInfos f = withValue (\v -> v {info = f $ info v})++addInfo :: (Functor k) => i -> ZwirnT k st i a -> ZwirnT k st i a+addInfo i = withInfos (const [i])++removeInfo :: (Functor k) => ZwirnT k st i a -> ZwirnT k st i a+removeInfo = withInfos (const [])++withState :: (Functor k) => (st -> st) -> ZwirnT k st i a -> ZwirnT k st i a+withState f = withValueState (second f)++fromSignal :: (Applicative k) => (Time -> Time) -> ZwirnT k st i Time+fromSignal f = f <$> now++instance (Semigroup a, Applicative k) => Semigroup (ZwirnT k st i a) where+ (<>) = liftA2 (<>)++instance (Monoid a, Applicative k) => Monoid (ZwirnT k st i a) where+ mempty = pure mempty++instance (Functor k) => Functor (ZwirnT k st i) where+ fmap f = withInner (fmap $ first (fmap f))++instance (Applicative k) => Applicative (ZwirnT k st i) where+ pure x = zwirn $ \t st -> pure (Value x t [], st)+ liftA2 f = withInner2 (liftA2 (\(v1, st1) (v2, _) -> (liftA2 f v1 v2, st1)))++instance (MultiApplicative k) => MultiApplicative (ZwirnT k st i) where+ liftA2Left f = withInner2 (liftA2Left (\(v1, st1) (v2, _) -> (liftA2Left f v1 v2, st1)))+ liftA2Right f = withInner2 (liftA2Right (\(v1, st1) (v2, _) -> (liftA2Right f v1 v2, st1)))++instance (Monad k) => Monad (ZwirnT k st i) where+ (>>=) x f = _innerJoin $ fmap f x+ where+ _innerJoin pp = zwirn q+ where+ q t st = (\(z, st') -> first (mergeInfo (info z)) <$> unzwirn (value z) t st') =<< outer+ where+ outer = unzwirn pp t st+ mergeInfo i v = v {info = info v ++ i}++instance (MultiMonad k) => MultiMonad (ZwirnT k st i) where+ outerJoin pp = zwirn q+ where+ q t st = outerJoin $ (\(z, st') -> first (\v -> v {time = time z, info = info v ++ info z}) <$> unzwirn (value z) t st') <$> outer+ where+ outer = unzwirn pp t st++ squeezeJoin pp = zwirn q+ where+ q t st = squeezeJoin $ (\(z, st') -> first (mergeInfo (info z)) <$> unzwirn (value z) (time z) st') <$> outer+ where+ outer = unzwirn pp t st+ mergeInfo i v = v {info = info v ++ i}++squeezeMap :: (MultiMonad m) => (m a -> m b) -> m a -> m b+squeezeMap f x = squeezeJoin $ fmap (f . pure) x++enumerateFromByTo :: (Ord a, Num a) => a -> a -> a -> [a]+enumerateFromByTo x y z+ | y <= 0 = []+ | x == z = []+ | x < z = if z < x + y then [x] else x : enumerateFromByTo (x + y) y z+ | otherwise = if z > x - y then [x] else x : enumerateFromByTo (x - y) y z++enumerateFromThenTo :: (Ord a, Num a) => a -> a -> a -> [a]+enumerateFromThenTo x y+ | x <= y = enumerateFromByTo x (y - x)+ | otherwise = enumerateFromByTo x (x - y)++enumerateFromTo :: (Ord a, Num a) => a -> a -> [a]+enumerateFromTo x = enumerateFromByTo x 1
+ src/zwirn-core/Zwirn/Core/Lib/Conditional.hs view
@@ -0,0 +1,91 @@+module Zwirn.Core.Lib.Conditional where++{-+ Conditional.hs - conditional functions+ Copyright (C) 2025, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Data.Bifunctor (first)+import Data.Fixed (mod')+import Zwirn.Core.Lib.Core+import Zwirn.Core.Time+import Zwirn.Core.Types++ifthen :: (MultiMonad k) => ZwirnT k st i Bool -> ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a+ifthen bz xz yz = innerJoin $ zwirn q+ where+ q t st = first (fmap f) <$> unzwirn bz t st+ where+ f True = xz+ f False = yz++iff :: (MultiMonad k, HasSilence k) => ZwirnT k st i Bool -> ZwirnT k st i a -> ZwirnT k st i a+iff b x = ifthen b x silence++or :: (Applicative k) => ZwirnT k st i Bool -> ZwirnT k st i Bool -> ZwirnT k st i Bool+or = liftA2 (||)++and :: (Applicative k) => ZwirnT k st i Bool -> ZwirnT k st i Bool -> ZwirnT k st i Bool+and = liftA2 (&&)++not :: (Functor k) => ZwirnT k st i Bool -> ZwirnT k st i Bool+not = fmap Prelude.not++eq :: (Eq a, Applicative k) => ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i Bool+eq = liftA2 (==)++leq :: (Ord a, Applicative k) => ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i Bool+leq = liftA2 (<=)++geq :: (Ord a, Applicative k) => ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i Bool+geq = liftA2 (>=)++le :: (Ord a, Applicative k) => ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i Bool+le = liftA2 (<)++ge :: (Ord a, Applicative k) => ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i Bool+ge = liftA2 (>)++while :: (MultiMonad k) => ZwirnT k st i Bool -> ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a+while b f x = ifthen b (apply f x) x++-- | the first value controls the period the second the length of applying the function in that period+everyFor :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i Time -> ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a+everyFor t1 t2 f x = (everyFor' <$> t1 <*> t2 <*> f) `innerApply` x+ where+ everyFor' 0 _ _ y = y+ everyFor' per for g y = zwirn $ \t st -> if mod' t per <= for then unzwirn (g y) t st else unzwirn y t st++-- | applies function every period for one cycle+every :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a+every x = everyFor x (pure 1)++everyBeatShiftWith :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i Time -> ZwirnT k st i Time -> ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a+everyBeatShiftWith bpcz shz nz fz xz = (everyBeatWith' <$> bpcz <*> shz <*> nz <*> fz) `innerApply` xz+ where+ everyBeatWith' :: Time -> Time -> Time -> (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a+ everyBeatWith' _ _ 0 _ x = x+ everyBeatWith' 0 _ _ _ x = x+ everyBeatWith' bpc sh n f x = zwirn $ \t st -> if mod' (t + sh) (n / bpc) < (1 / bpc) then unzwirn (f x) t st else unzwirn x t st++everyBeatWith :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i Time -> ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a+everyBeatWith x = everyBeatShiftWith x (pure 0)++everyBeatShift :: (Monad k, State k st i) => ZwirnT k st i Time -> ZwirnT k st i Time -> ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a+everyBeatShift = everyBeatShiftWith (realToFrac <$> beatsPerCycle)++everyBeat :: (Monad k, State k st i) => ZwirnT k st i Time -> ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a+everyBeat = everyBeatShift (pure 0)
+ src/zwirn-core/Zwirn/Core/Lib/Cord.hs view
@@ -0,0 +1,188 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeApplications #-}+{-# OPTIONS_GHC -Wno-orphans #-}++module Zwirn.Core.Lib.Cord where++{-+ Cord.hs - functions on parallel signals+ Copyright (C) 2025, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Control.Monad (join)+import Data.Bifunctor (first)+import Zwirn.Core.Cord as C+import Zwirn.Core.Core+import Zwirn.Core.Lib.Conditional (iff)+import Zwirn.Core.Lib.Core+import Zwirn.Core.Lib.Modulate (bump, fastcat, fastcyclecatpat, shift)+import Zwirn.Core.Lib.Number+import Zwirn.Core.Time+import Zwirn.Core.Tree+import qualified Zwirn.Core.Tree as Tree+import Zwirn.Core.Types++-- | get the current depth of the cord+depth :: Cord st i a -> Cord st i Int+depth = C.depth++superimpose :: Cord st i (Cord st i a -> Cord st i a) -> Cord st i a -> Cord st i a+superimpose f x = stack [apply f x, x]++ghostWith :: Cord st i Time -> Cord st i (Cord st i a -> Cord st i a) -> Cord st i a -> Cord st i a+ghostWith t f x = stack [shift (t * 2.5) $ apply f x, shift (t * 1.5) $ apply f x, x]++-- | pick a certain layer out of a cord, wrapping around+pick :: Cord st i Int -> Cord st i a -> Cord st i a+pick iz x = (_pick <$> iz) `innerApply` x++inhabit :: Cord st i Int -> Cord st i a -> Cord st i a+inhabit iz xz = zwirn q+ where+ q t st = innerJoin $ (\v -> look (value v) $ unzwirn xz (time v) st) . fst <$> ik+ where+ ik = unzwirn iz t st++-- | pick a certain layer out of a cord, silence when out of bounds+pick' :: Cord st i Int -> Cord st i a -> Cord st i a+pick' i x = (withInner . look' <$> i) `innerApply` x++-- | pick a certain layer out of a cord, wrapping around+select :: Cord st i Double -> Cord st i a -> Cord st i a+select d x = Zwirn.Core.Lib.Cord.pick i x+ where+ i = join $ liftA2 (\l k -> if k > 0 && abs l <= 1 then pure $ Prelude.floor $ l * fromIntegral k else silence) d (C.depth x)++-- | insert cord a specific index+insert :: Cord st i Int -> Cord st i a -> Cord st i a -> Cord st i a+insert ic x y = flip ($ x) y . insert' =<< ic+ where+ insert' :: Int -> Cord st i a -> Cord st i a -> Cord st i a+ insert' i z ys = zwirn $ \t st -> insertT i (unzwirn z t st) (unzwirn ys t st)++-- | push to the top of the cord+push :: Cord st i a -> Cord st i a -> Cord st i a+push = withInner2 Tree.push++concat :: Cord st i a -> Cord st i a -> Cord st i a+concat = withInner2 Tree.concat++-- | remove the top of the cord+pop :: Cord st i a -> Cord st i a+pop = withInner Tree.pop++-- | remove cord at specific index+remove :: Cord st i Int -> Cord st i a -> Cord st i a+remove i x = (withInner . removeT <$> i) `innerApply` x++-- | apply function to specific index+at :: Cord st i Int -> Cord st i (Cord st i a -> Cord st i a) -> Cord st i a -> Cord st i a+at i f x = insert i (innerApply f $ pick i x) (remove i x)++applyCord :: (Num a) => Cord st i a -> Cord st i a -> Cord st i a+applyCord = liftA2Left (+)++reverse :: Cord st i a -> Cord st i a+reverse = liftList Prelude.reverse++rotate :: Cord st i Int -> Cord st i a -> Cord st i a+rotate iz xz = flip liftList xz . rotateList =<< iz+ where+ rotateList n xs+ | n > 0 = Prelude.take (length xs) (Prelude.drop n (cycle $ Prelude.reverse xs))+ | otherwise = Prelude.take (length xs) (Prelude.drop n (cycle xs))++take :: Cord st i Int -> Cord st i a -> Cord st i a+take iz xz = flip liftList xz . Prelude.take =<< iz++drop :: Cord st i Int -> Cord st i a -> Cord st i a+drop iz xz = flip liftList xz . Prelude.drop =<< iz++filter :: Cord st i (Cord st i a -> Cord st i Bool) -> Cord st i a -> Cord st i a+filter fz xz = iff bz xz+ where+ bz = cordmap fz xz++invert :: (Num a) => Cord st i a -> Cord st i a+invert = liftList invertList+ where+ invertList [] = []+ invertList (x : xs) = xs ++ [fmap (first $ fmap (+ 12)) x]++open :: (Num a) => Cord st i a -> Cord st i a+open = liftList openList+ where+ openList ds = case ds of+ (d : _ : _ : _) -> [fmap (first $ fmap (+ (-12))) d, fmap (first $ fmap (+ (-12))) (ds !! 2), ds !! 1] ++ Prelude.reverse (Prelude.take (length ds - 3) (Prelude.reverse ds))+ _ -> ds++expand :: (Num a) => Cord st i Int -> Cord st i a -> Cord st i a+expand iz xz = flip liftList xz . expandList =<< iz+ where+ expandList i ds = Prelude.take i $ concatMap (\x -> map (fmap $ first $ fmap (+ fromIntegral x)) ds) [0 :: Int, 12 ..]++enumFromToStack :: (Ord a, Num a) => Cord st i a -> Cord st i a -> Cord st i a+enumFromToStack xz yz = join $ en <$> xz <*> yz+ where+ en x y = stack $ map pure $ enumerateFromTo x y++enumFromThenToStack :: (Ord a, Num a) => Cord st i a -> Cord st i a -> Cord st i a -> Cord st i a+enumFromThenToStack xz yz zz = join $ en <$> xz <*> yz <*> zz+ where+ en x y z = stack $ map pure $ enumerateFromThenTo x y z++replicate :: Cord st i Int -> Cord st i a -> Cord st i a+replicate i x = (stack . flip Prelude.replicate x) =<< i++fold :: Cord st i (Cord st i a -> Cord st i (Cord st i a -> Cord st i a)) -> Cord st i a -> Cord st i a+fold fz xz = fold' fz =<< collect xz+ where+ fold' _ [] = silence+ fold' _ [x] = x+ fold' f (x : xs) = foldl' (\a b -> f `apply` a `apply` b) x xs++cordcat :: Cord st i a -> Cord st i a+cordcat x = fastcat =<< collect x++timerun :: Cord st i Time -> Cord st i Int+timerun tz = (fastcyclecatpat . flip zip (map pure [0 :: Int ..])) =<< collect tz++interpol :: Cord st i Time -> Cord st i Time+interpol x = _interpol =<< collect x++cordmap :: Cord st i (Cord st i a -> Cord st i b) -> Cord st i a -> Cord st i b+cordmap f x = (stack . map (apply f)) =<< collect x++layer :: Cord st i (Cord st i a -> Cord st i b) -> Cord st i a -> Cord st i b+layer f x = (stack . map (`apply` x)) =<< collect f++arp :: Cord st i a -> Cord st i a+arp x = apply (squeezeMap (pure . bump) ds) x+ where+ d = C.depth x+ ds = enumFromThenToStack @Time 0 ((\dp -> if dp == 0 then 0 else 1 / fromIntegral dp) <$> d) 1++echoWith :: Cord st i Int -> Cord st i Time -> Cord st i (Cord st i a -> Cord st i a) -> Cord st i a -> Cord st i a+echoWith cz tz fz xz = echoWith' =<< cz+ where+ echoWith' c = if c < 1 then silence else stack $ scanl (\ !prev _ -> apply fz $ shift tz prev) xz [0 .. c - 1]++followWith :: Cord st i Int -> Cord st i Time -> Cord st i (Cord st i a -> Cord st i a) -> Cord st i a -> Cord st i a+followWith cz tz fz xz = followWith' =<< cz+ where+ followWith' c = if c < 1 then silence else stack $ scanl (\ !prev _ -> apply fz $ bump tz prev) xz [0 .. c - 1]
+ src/zwirn-core/Zwirn/Core/Lib/Core.hs view
@@ -0,0 +1,81 @@+{-# OPTIONS_GHC -Wno-orphans #-}++module Zwirn.Core.Lib.Core where++{-+ Core.hs - core functions and instances+ Copyright (C) 2025, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Control.Monad (join)+import Zwirn.Core.Core as C+import Zwirn.Core.Query+import Zwirn.Core.Time+import Zwirn.Core.Types++-- | indicates the current time+now :: (Applicative k) => ZwirnT k st i Time+now = C.now++-- | indicates the current cycle+cyc :: (Applicative k) => ZwirnT k st i Int+cyc = fmap floor C.now++trig :: (Functor k) => ZwirnT k st i a -> ZwirnT k st i Bool+trig = withValue (\(Value _ t i) -> Value (breakpointCondition 0.005 t) t i)++getInnerTime :: (Functor k) => ZwirnT k st i a -> ZwirnT k st i Double+getInnerTime = withValue (\v -> v {value = realToFrac $ tTime $ time v})++getSpeed :: (Functor k) => ZwirnT k st i a -> ZwirnT k st i Double+getSpeed = withValue (\v -> v {value = realToFrac $ tDiff $ time v})++apply :: (MultiMonad k) => ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i b) -> ZwirnT k st i a -> ZwirnT k st i b+apply = zipSqueezeApply++outerApply :: (MultiMonad m) => m (m a -> m b) -> m a -> m b+outerApply f x = outerJoin $ f <*> pure x++innerApply :: (Monad m) => m (m a -> m b) -> m a -> m b+innerApply f x = join $ f <*> pure x++squeezeApply :: (MultiMonad m) => m (m a -> m b) -> m a -> m b+squeezeApply f x = squeezeJoin $ f <*> pure x++zipApply :: (MultiMonad k) => ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i b) -> ZwirnT k st i a -> ZwirnT k st i b+zipApply fs x = zwirn q+ where+ q t st = innerJoin $ (\c -> unzwirn c t st) . ($ x) . value . fst <$> unzwirn fs t st++zipSqueezeApply :: (MultiMonad k) => ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i b) -> ZwirnT k st i a -> ZwirnT k st i b+zipSqueezeApply fs x = zwirn q+ where+ q t st = innerJoin $ func <$> unzwirn fs t st+ where+ func f = (\v -> unzwirn (value v x) (time v) st) $ fst f++matchApply :: (MultiMonad k) => ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i b) -> ZwirnT k st i a -> ZwirnT k st i b+matchApply fs x = innerJoin $ apply fs . pure <$> x++iterate :: (MultiMonad k, HasSilence k) => ZwirnT k st i Int -> ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a+iterate nz fz xz = iterate' =<< nz+ where+ iterate' n = if n < 0 then silence else is !! n+ where+ is = Prelude.iterate (apply fz) xz++squeeze :: (MultiMonad m) => m (m a -> m b) -> m a -> m b+squeeze fp xp = squeezeJoin $ fmap (squeezeApply fp . pure) xp
+ src/zwirn-core/Zwirn/Core/Lib/Map.hs view
@@ -0,0 +1,132 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}++module Zwirn.Core.Lib.Map where++{-+ Map.hs - lifting functions on maps to signals, some adapted+ from https://github.com/tidalcycles/Tidal/blob/dev/src/Sound/Tidal/Control.hs+ Copyright (C) 2025, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Data.Map (Map)+import qualified Data.Map as Map+import Data.Maybe (fromMaybe)+import Data.String (IsString)+import Zwirn.Core.Cord (Cord, stack)+import Zwirn.Core.Core+import Zwirn.Core.Lib.Cord (echoWith)+import Zwirn.Core.Lib.Core+import Zwirn.Core.Lib.Modulate (fastcat, slow)+import Zwirn.Core.Lib.Structure (run)+import Zwirn.Core.Time (Time)+import Zwirn.Core.Types+import Prelude hiding ((*>))++-- | create a singleton map with specific key+singleton :: (MultiApplicative m) => ZwirnT m st i k -> ZwirnT m st i a -> ZwirnT m st i (Map k a)+singleton = liftA2Right Map.singleton++-- | union of two maps, if a key exists in both, the value will come from the right+union :: (Applicative m, Ord k) => ZwirnT m st i (Map k a) -> ZwirnT m st i (Map k a) -> ZwirnT m st i (Map k a)+union = liftA2 (flip Map.union)++-- | lookup a value via key+lookup :: (HasSilence m, MultiMonad m, Ord k) => ZwirnT m st i k -> ZwirnT m st i (Map k a) -> ZwirnT m st i a+lookup tz xz = outerJoin $ liftA2Right (\t x -> fromLookup $ Map.lookup t x) tz xz+ where+ fromLookup (Just x) = pure x+ fromLookup _ = silence++insert :: (Applicative m, Ord k) => ZwirnT m st i k -> ZwirnT m st i a -> ZwirnT m st i (Map k a) -> ZwirnT m st i (Map k a)+insert k a m = Map.insert <$> k <*> a <*> m++-- | apply a function to a specific key, if key is absent, return the original map+fix :: (MultiMonad m, Ord k) => ZwirnT m st i k -> ZwirnT m st i (ZwirnT m st i a -> ZwirnT m st i a) -> ZwirnT m st i (Map k a) -> ZwirnT m st i (Map k a)+fix kz fz mz = outerJoin $ fromLookup <$> lookupMaybe kz mz+ where+ fromLookup (Just x) = insert kz (apply fz (pure x)) mz+ fromLookup Nothing = mz+ lookupMaybe = liftA2Right Map.lookup++chop :: (Fractional a, MultiMonad m, HasSilence m, Ord k, IsString k) => ZwirnT m st i Int -> ZwirnT m st i (Map k a) -> ZwirnT m st i (Map k a)+chop nz = squeezeMap (quickslice nz (run nz))++quickslice :: (Fractional a, MultiMonad m, Ord k, IsString k) => ZwirnT m st i Int -> ZwirnT m st i Int -> ZwirnT m st i (Map k a) -> ZwirnT m st i (Map k a)+quickslice nz iz = squeezeMap (slice nz iz)++loopAt :: (Fractional a, IsString a, HasSilence m, Monad m, Ord k, IsString k) => ZwirnT m st i Time -> ZwirnT m st i (Map k a) -> ZwirnT m st i (Map k a)+loopAt zt zx = (_loopAt <$> zt) `innerApply` zx+ where+ _loopAt 0 _ = silence+ _loopAt t x = Map.alter a "speed" . Map.insert "unit" "c" <$> slow (pure t) x+ where+ a (Just s) = Just (s / realToFrac t)+ a Nothing = Just (1 / realToFrac t)++slice :: (Fractional a, MultiApplicative m, Ord k, IsString k) => ZwirnT m st i Int -> ZwirnT m st i Int -> ZwirnT m st i (Map k a) -> ZwirnT m st i (Map k a)+slice nz iz zm = _slice <$> nz *> iz <*> zm+ where+ _slice 0 _ m = m+ _slice n i m = Map.unions [Map.singleton "begin" newb, Map.singleton "end" newe, m]+ where+ b = fromMaybe 0 $ Map.lookup "begin" m+ e = fromMaybe 1 $ Map.lookup "end" m+ newrange x = e * x + (1 - x) * b+ newb = newrange $ div' i n+ newe = newrange $ div' i n + if n == 1 then 1 else div' 1 n+ div' num den = fromIntegral (num `mod` den) / fromIntegral den++striateBy :: (Fractional a, Monad m, HasSilence m, Ord k, IsString k) => ZwirnT m st i Int -> ZwirnT m st i a -> ZwirnT m st i (Map k a) -> ZwirnT m st i (Map k a)+striateBy i f x = (_striateBy <$> i <*> f) `innerApply` x+ where+ _striateBy n g mz = fastcat $ map (offset . fromIntegral) [0 .. n - 1]+ where+ offset k = _mergePlayRange (slot * k, (slot * k) + g) <$> mz+ slot = (1 - g) / fromIntegral (n - 1)++striate :: (Fractional a, Monad m, HasSilence m, Ord k, IsString k) => ZwirnT m st i Int -> ZwirnT m st i (Map k a) -> ZwirnT m st i (Map k a)+striate i x = (_striate <$> i) `innerApply` x+ where+ _striate n z = fastcat $ map offset [0 .. n - 1]+ where+ offset k = _mergePlayRange (fromIntegral k / fromIntegral n, fromIntegral (k + 1) / fromIntegral n) <$> z++_mergePlayRange :: (Fractional a, Ord k, IsString k) => (a, a) -> Map k a -> Map k a+_mergePlayRange (b, e) cm = Map.insert "begin" ((b * d') + b') $ Map.insert "end" ((e * d') + b') cm+ where+ b' = fromMaybe 0 $ Map.lookup "begin" cm+ e' = fromMaybe 1 $ Map.lookup "end" cm+ d' = e' - b'++juxBy :: (Fractional a, Ord k, IsString k) => Cord st i a -> Cord st i (Cord st i (Map k a) -> Cord st i (Map k a)) -> Cord st i (Map k a) -> Cord st i (Map k a)+juxBy nz fz xz = stack [modifyPanL xz nz, modifyPanR (apply fz xz) nz]+ where+ modifyPanL = liftA2 (\x n -> Map.alter (pannerL n) "pan" x)+ modifyPanR = liftA2 (\x n -> Map.alter (pannerR n) "pan" x)+ pannerL n (Just p) = Just $ p + 0.5 + n / 2+ pannerL n Nothing = Just $ 0.5 + n / 2+ pannerR n (Just p) = Just $ p + 0.5 - n / 2+ pannerR n Nothing = Just $ 0.5 - n / 2++jux :: (Fractional a, Ord k, IsString k) => Cord st i (Cord st i (Map k a) -> Cord st i (Map k a)) -> Cord st i (Map k a) -> Cord st i (Map k a)+jux = juxBy (pure 1)++echo :: (Fractional a, Ord k, IsString k) => Cord st i Int -> Cord st i Time -> Cord st i a -> Cord st i (Map k a) -> Cord st i (Map k a)+echo cz tz ez = echoWith cz tz (pure (liftA2 (\e m -> Map.alter (modGain e) "gain" m) ez))+ where+ modGain e (Just g) = Just $ g * e+ modGain e Nothing = Just e
+ src/zwirn-core/Zwirn/Core/Lib/Modulate.hs view
@@ -0,0 +1,169 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE TypeApplications #-}++module Zwirn.Core.Lib.Modulate where++{-+ Modulate.hs - functions modulating time+ Copyright (C) 2025, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Data.Fixed (mod')+import Zwirn.Core.Core+import Zwirn.Core.Lib.Core+import Zwirn.Core.Time+import Zwirn.Core.Tree+import Zwirn.Core.Types++rev :: ZwirnT k st i a -> ZwirnT k st i a+rev = modulateTime (\_ t _ -> -t) ()++revBy :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a+revBy tz x = (modulateTime (\y t _ -> fromIntegral @Int (floor y) + t - frac y) <$> tz) `innerApply` x++sini :: ZwirnT k st i a -> ZwirnT k st i a+sini = modulateTime (\_ t _ -> sin (2 * pi * t)) ()++fast :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a+fast tz x = (modulateTime (\y t _ -> t * y) <$> tz) `innerApply` x++slow :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a+slow tz x = (modulateTime timefunc <$> tz) `innerApply` x+ where+ timefunc y t _+ | y == 0 = 0+ | otherwise = t / y++shift :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a+shift tz x = (modulateTime (\y t _ -> t - y) <$> tz) `innerApply` x++ply :: (MultiMonad k) => ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a+ply tz = apply (matchApply ply' tz)+ where+ ply' = pure $ \t -> pure $ \x -> squeezeMap (fast t) x++bump :: (MultiMonad k) => ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a+bump tz = apply (matchApply bump' tz)+ where+ bump' = pure $ \t -> pure $ \x -> squeezeMap (shift t) x++-- zoom into a specific region of time, while preserving the speed+zoom :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a+zoom t1 t2 x = (modulateTime timefunc <$> tup) `innerApply` x+ where+ tup = liftA2 (,) t1 t2+ timefunc (st, en) t _+ | en > st = mod' (t * (en - st)) (en - st) + st+ | en == st = st+ | otherwise = st - mod' (t * (st - en)) (st - en)++-- loop a specific region of time, the shorter the region, the faster it gets+loop :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a+loop t1 t2 x = (modulateTime timefunc <$> tup) `innerApply` x+ where+ tup = liftA2 (,) t1 t2+ timefunc (st, en) t _+ | en > st = mod' t (en - st) + st+ | en == st = st+ | otherwise = st - mod' t (st - en)++timeloop :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a+timeloop = loop (pure 0)++loopfirst :: (Monad k) => ZwirnT k st i a -> ZwirnT k st i a+loopfirst = timeloop (pure 1)++ribbon :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a+ribbon off len = loop off (liftA2 (+) off len)++swingBy :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a+swingBy t1 t2 x = (modulateTime timefunc <$> tup) `innerApply` x+ where+ tup = liftA2 (,) t1 t2+ timefunc (sh, segs) t _+ | odd (floor $ frac t * segs * 2 :: Int) = t - (sh / (segs * 2))+ | segs == 0 = 0+ | otherwise = t++----------------------------------------------+------------------ cats ----------------------+----------------------------------------------++fastcat :: (HasSilence k) => [ZwirnT k st i a] -> ZwirnT k st i a+fastcat [] = silence+fastcat obj = zwirn q+ where+ q t = unzwirn item phase+ where+ metre = fromIntegral $ length obj+ scaledPhase = t * metre+ item = nth scaledPhase obj+ cy = floor t+ phase = frac scaledPhase + fromIntegral @Int cy++slowcat :: (HasSilence k, Monad k) => [ZwirnT k st i a] -> ZwirnT k st i a+slowcat zs = slow (pure $ fromIntegral $ length zs) $ fastcat zs++-- | each (t,p) indicates the amount of time t for pattern p relative+-- | to the other lengths in the list, squeezed within one cycle+timecat :: (HasSilence k, Monad k) => [(Time, ZwirnT k st i a)] -> ZwirnT k st i a+timecat tps = if total == 0 then silence else cyclecat normalised+ where+ total = sum $ map fst tps+ normalised = map (\(t, p) -> (t / total, slow (pure $ t / total) p)) tps++-- | each (t,p) indicates the amount of time t the pattern p is queried for+-- | the patterns in the list will be queried in order by their respective amounts+-- | Example: cyclecat [(1,pure 10), (2, slow 2 $ pure 20)] == < 10 20 ~ >+-- | Note: also works with rational numbers+cyclecat :: (HasSilence k) => [(Time, ZwirnT k st i a)] -> ZwirnT k st i a+cyclecat [] = silence+cyclecat xs = cyclecatrec xs (sum $ map fst xs)+ where+ -- len = sum $ map fst xs+ cyclecatrec [] _ = silence+ cyclecatrec [(_, p)] _ = p+ cyclecatrec (x : ys) !tot = cat x (tot - fst x, cyclecatrec ys (tot - fst x))++cat :: (HasSilence k) => (Time, ZwirnT k st i a) -> (Time, ZwirnT k st i a) -> ZwirnT k st i a+cat (t1, p1) (t2, p2) = if total == 0 then silence else zwirn q+ where+ total = t1 + t2+ q t = unzwirn item phase+ where+ cy = t / total+ first = frac cy < t1 / total+ item = if first then p1 else p2+ phase = if first then t - fromIntegral @Int (floor cy) * t2 else t - (fromIntegral @Int (floor cy) + 1) * t1++fastcyclecat :: (HasSilence k, Monad k) => [(Time, ZwirnT k st i a)] -> ZwirnT k st i a+fastcyclecat xs = cyclecat $ map (\(t, x) -> (t, slow (pure t) x)) xs++catpat :: (MultiMonad k, HasSilence k) => (ZwirnT k st i Time, ZwirnT k st i a) -> (ZwirnT k st i Time, ZwirnT k st i a) -> ZwirnT k st i a+catpat (t1z, p1) (t2z, p2) = innerJoin $ liftA2 (\t1 t2 -> cat (t1, p1) (t2, p2)) t1z t2z++cyclecatpat :: (MultiMonad k, HasSilence k) => [(ZwirnT k st i Time, ZwirnT k st i a)] -> ZwirnT k st i a+cyclecatpat [] = silence+cyclecatpat xs = cyclecatrec xs (foldl' (liftA2 (+)) (pure 0) $ map fst xs)+ where+ cyclecatrec [] _ = silence+ cyclecatrec [(_, p)] _ = p+ cyclecatrec (x : ys) !tot = catpat x (newTot, cyclecatrec ys newTot)+ where+ newTot = liftA2 (-) tot (fst x)++fastcyclecatpat :: (MultiMonad k, HasSilence k) => [(ZwirnT k st i Time, ZwirnT k st i a)] -> ZwirnT k st i a+fastcyclecatpat xs = cyclecatpat $ map (\(t, x) -> (t, slow t x)) xs
+ src/zwirn-core/Zwirn/Core/Lib/Number.hs view
@@ -0,0 +1,143 @@+{-# LANGUAGE TypeApplications #-}+{-# OPTIONS_GHC -Wno-orphans #-}++module Zwirn.Core.Lib.Number where++{-+ Number.hs - lifting functions on numbers to signals+ Copyright (C) 2025, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Data.Fixed (mod')+import Zwirn.Core.Core+import Zwirn.Core.Lib.Modulate (cat, fastcat)+import Zwirn.Core.Time (Time)+import Zwirn.Core.Types++instance (Num a, Applicative k) => Num (ZwirnT k st i a) where+ (+) = liftA2 (+)+ (-) = liftA2 (-)+ (*) = liftA2 (*)+ negate = fmap negate+ abs = fmap abs+ signum = fmap signum+ fromInteger = pure . fromInteger++instance (Eq a, Fractional a, MultiMonad k, HasSilence k) => Fractional (ZwirnT k st i a) where+ fromRational = pure . fromRational+ recip xz = innerJoin $ fmap (\x -> if x == 0 then silence else pure $ recip x) xz++instance (Ord a, Floating a, MultiMonad k, HasSilence k) => Floating (ZwirnT k st i a) where+ pi = pure pi+ exp = fmap exp+ log xz = innerJoin $ fmap (\x -> if x <= 0 then silence else pure $ log x) xz+ sqrt xz = innerJoin $ fmap (\x -> if x < 0 then silence else pure $ sqrt x) xz+ (**) xz yz = innerJoin $ liftA2 (\x y -> if x <= 0 && abs y < 1 then silence else pure $ x ** y) xz yz+ logBase bz xz = innerJoin $ liftA2 (\b x -> if b < 0 || x <= 0 then silence else pure $ logBase b x) bz xz+ sin = fmap sin+ cos = fmap cos+ tan = fmap tan+ asin = fmap asin+ acos = fmap acos+ atan = fmap atan+ sinh = fmap sinh+ cosh = fmap cosh+ tanh = fmap tanh+ asinh = fmap asinh+ acosh = fmap acosh+ atanh = fmap atanh++mod :: (Real a, HasSilence k, MultiMonad k) => ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a+mod xz yz = innerJoin $ liftA2 (\x y -> if y == 0 then silence else pure $ mod' x y) xz yz++frac :: (Real a, MultiMonad k) => ZwirnT k st i a -> ZwirnT k st i a+frac = fmap (`mod'` 1)++trunc :: (RealFrac a, Integral b, Functor k) => ZwirnT k st i a -> ZwirnT k st i b+trunc = fmap truncate++ceil :: (RealFrac a, Integral b, Functor k) => ZwirnT k st i a -> ZwirnT k st i b+ceil = fmap ceiling++floor :: (RealFrac a, Integral b, Functor k) => ZwirnT k st i a -> ZwirnT k st i b+floor = fmap Prelude.floor++round :: (RealFrac a, Integral b, Functor k) => ZwirnT k st i a -> ZwirnT k st i b+round = fmap Prelude.round++gcd :: (Integral a, Applicative k) => ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a+gcd = liftA2 Prelude.gcd++lcm :: (Integral a, Applicative k) => ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a+lcm = liftA2 Prelude.lcm++range :: (Num a, Applicative k) => ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a+range lx lu lv = (\l u v -> (1 - v) * l + v * u) <$> lx <*> lu <*> lv++sine :: (Applicative k) => ZwirnT k st i Time+sine = fromSignal (\t -> (sin (2 * pi * t) + 1) / 2)++sine2 :: (Applicative k) => ZwirnT k st i Time+sine2 = fromSignal (\t -> sin (2 * pi * t))++cosine :: (Applicative k) => ZwirnT k st i Time+cosine = fromSignal (\t -> (cos (2 * pi * t) + 1) / 2)++cosine2 :: (Applicative k) => ZwirnT k st i Time+cosine2 = fromSignal (\t -> cos (2 * pi * t))++saw :: (Applicative k) => ZwirnT k st i Time+saw = fromSignal (`mod'` 1)++saw2 :: (Applicative k) => ZwirnT k st i Time+saw2 = fromSignal (\t -> (mod' t 1 * 2) - 1)++isaw :: (Applicative k) => ZwirnT k st i Time+isaw = fromSignal (\t -> 1 - mod' t 1)++isaw2 :: (Applicative k) => ZwirnT k st i Time+isaw2 = fromSignal (\t -> 1 - (mod' t 1 * 2))++square :: (Applicative k) => ZwirnT k st i Time+square = fromSignal (\t -> fromIntegral @Int $ Prelude.floor $ mod' t 1 * 2)++square2 :: (Applicative k) => ZwirnT k st i Time+square2 = fromSignal (\t -> fromIntegral @Int $ Prelude.floor (mod' t 1 * 2) - 1)++tri :: (Applicative k, HasSilence k) => ZwirnT k st i Time+tri = fastcat [saw, isaw]++tri2 :: (Applicative k, HasSilence k) => ZwirnT k st i Time+tri2 = fastcat [saw2, isaw2]++firstCyclesThen :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a+firstCyclesThen t1 dz xz = func =<< t1+ where+ func l = zwirn q+ where+ q t st = if t <= l then unzwirn xz t st else unzwirn dz t st++linear :: (Applicative k) => ZwirnT k st i Time -> ZwirnT k st i Time -> ZwirnT k st i Time+linear x y = range x y saw++_interpol :: (Applicative k, HasSilence k) => [ZwirnT k st i Time] -> ZwirnT k st i Time+_interpol [] = silence+_interpol [x] = x+_interpol (x : y : zs) = if null zs then linear x y else cat (delta, l) (1 - delta, ls)+ where+ delta = 1 / (fromIntegral (length zs) + 1)+ l = linear x y+ ls = _interpol (y : zs)
+ src/zwirn-core/Zwirn/Core/Lib/Random.hs view
@@ -0,0 +1,153 @@+module Zwirn.Core.Lib.Random where++{-+ Random.hs - simple random signals and related functions+ Copyright (C) 2025, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Control.Monad (join)+import qualified Numeric.Noise as Noise+import System.Random+import Zwirn.Core.Core+import Zwirn.Core.Lib.Core+import Zwirn.Core.Lib.Modulate+import Zwirn.Core.Time (Time)+import Zwirn.Core.Types++precision :: Time+precision = 0.005++randR :: (Random a, Applicative k) => ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a+randR l r = zwirn q+ where+ q t = unzwirn (fmap fst $ liftA2 randomR zipp $ pure $ mkStdGen $ floor (t / precision)) t+ where+ zipp = liftA2 (,) l r++rand :: (Random a, Applicative k) => ZwirnT k st i a+rand = zwirn $ \t st -> pure (Value (fst $ random (mkStdGen $ floor (t / precision))) t [], st)++noise :: (Applicative k) => ZwirnT k st i Double+noise = rand++irand :: (Applicative k) => ZwirnT k st i Int -> ZwirnT k st i Int+irand = randR (pure 0)++brandBy :: (Applicative k) => ZwirnT k st i Double -> ZwirnT k st i Bool+brandBy prob = liftA2 (>) prob rand++sometimesBy :: (MultiMonad k) => ZwirnT k st i Double -> ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a+sometimesBy prob f x = innerJoin $ fmap cho (brandBy prob)+ where+ cho True = apply f x+ cho False = x++sometimes :: (MultiMonad k) => ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a+sometimes = sometimesBy (pure 0.5)++often :: (MultiMonad k) => ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a+often = sometimesBy (pure 0.75)++rarely :: (MultiMonad k) => ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a+rarely = sometimesBy (pure 0.25)++almostNever :: (MultiMonad k) => ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a+almostNever = sometimesBy (pure 0.1)++almostAlways :: (MultiMonad k) => ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a+almostAlways = sometimesBy (pure 0.9)++-- TODO: FIX+degradeBy :: (MultiMonad k, HasSilence k) => ZwirnT k st i Double -> ZwirnT k st i a -> ZwirnT k st i a+degradeBy prob = sometimesBy prob (pure $ const silence)++degrade :: (MultiMonad k, HasSilence k) => ZwirnT k st i a -> ZwirnT k st i a+degrade = sometimes (pure $ const silence)++-- these versions take the cycle number as seed++randR' :: (Random a, Applicative k) => ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a+randR' r l = zwirn q+ where+ q t = unzwirn (fmap fst $ liftA2 randomR zipp $ pure $ mkStdGen $ floor t) t+ where+ zipp = liftA2 (,) l r++rand' :: (Random a, Applicative k) => ZwirnT k st i a+rand' = zwirn $ \t st -> pure (Value (fst $ random (mkStdGen $ floor t)) t [], st)++irand' :: (Applicative k) => ZwirnT k st i Int -> ZwirnT k st i Int+irand' = randR' (pure 0)++brandBy' :: (Applicative k) => ZwirnT k st i Double -> ZwirnT k st i Bool+brandBy' prob = liftA2 (>) prob rand'++somecyclesBy :: (MultiMonad k) => ZwirnT k st i Double -> ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a+somecyclesBy prob f x = innerJoin $ fmap cho (brandBy' prob)+ where+ cho True = apply f x+ cho False = x++somecycles :: (MultiMonad k) => ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a+somecycles = somecyclesBy (pure 0.5)++------++chooseWithSeed :: (Monad k) => Int -> [ZwirnT k st i a] -> ZwirnT k st i a+chooseWithSeed i ps = (ps !!) =<< shift (pure $ fromIntegral i / precision) (irand' $ pure $ length ps - 1)++chooseList :: (Monad k) => [ZwirnT k st i a] -> ZwirnT k st i a+chooseList = chooseWithSeed 0++enumFromToChoice :: (Ord a, Num a, Monad k) => Int -> ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a+enumFromToChoice i xz yz = join $ en <$> xz <*> yz+ where+ en x y = chooseWithSeed i $ map pure $ enumerateFromTo x y++enumFromThenToChoice :: (Ord a, Num a, Monad k) => Int -> ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a+enumFromThenToChoice i xz yz zz = join $ en <$> xz <*> yz <*> zz+ where+ en x y z = chooseWithSeed i $ map pure $ enumerateFromThenTo x y z++perlinSeed :: (Applicative k) => ZwirnT k st i Int -> ZwirnT k st i Double+perlinSeed seed = zwirn $ \t st -> unzwirn ((\i -> (Noise.noise2At Noise.perlin2 (fromIntegral i) 0 (realToFrac t) + 1) / 2) <$> seed) t st++perlin :: (Applicative k) => ZwirnT k st i Double+perlin = perlinSeed (pure 0)++simplexSeed :: (Applicative k) => ZwirnT k st i Int -> ZwirnT k st i Double+simplexSeed seed = zwirn $ \t st -> unzwirn ((\i -> (Noise.noise2At Noise.openSimplex2 (fromIntegral i) 0 (realToFrac t) + 1) / 2) <$> seed) t st++simplex :: (Applicative k) => ZwirnT k st i Double+simplex = simplexSeed (pure 0)++ssimplexSeed :: (Applicative k) => ZwirnT k st i Int -> ZwirnT k st i Double+ssimplexSeed seed = zwirn $ \t st -> unzwirn ((\i -> (Noise.noise2At Noise.superSimplex2 (fromIntegral i) 0 (realToFrac t) + 1) / 2) <$> seed) t st++ssimplex :: (Applicative k) => ZwirnT k st i Double+ssimplex = ssimplexSeed (pure 0)++valueSeed :: (Applicative k) => ZwirnT k st i Int -> ZwirnT k st i Double+valueSeed seed = zwirn $ \t st -> unzwirn ((\i -> (Noise.noise2At Noise.value2 (fromIntegral i) 0 (realToFrac t) + 1) / 2) <$> seed) t st++valueN :: (Applicative k) => ZwirnT k st i Double+valueN = valueSeed (pure 0)++cubicSeed :: (Applicative k) => ZwirnT k st i Int -> ZwirnT k st i Double+cubicSeed seed = zwirn $ \t st -> unzwirn ((\i -> (Noise.noise2At Noise.valueCubic2 (fromIntegral i) 0 (realToFrac t) + 1) / 2) <$> seed) t st++cubic :: (Applicative k) => ZwirnT k st i Double+cubic = cubicSeed (pure 0)
+ src/zwirn-core/Zwirn/Core/Lib/State.hs view
@@ -0,0 +1,52 @@+module Zwirn.Core.Lib.State where++{-+ State.hs - functions manipulating the underlying state of signals+ Copyright (C) 2025, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import qualified Data.Map as Map+import Zwirn.Core.Core+import Zwirn.Core.Lib.Core+import Zwirn.Core.Types++--- functions modifying the state++modify' :: (st -> st) -> ZwirnT k st i a -> ZwirnT k st i a+modify' f x = zwirn $ \t st -> unzwirn x t (f st)++modify :: (MultiMonad k) => (ZwirnT k st i st -> ZwirnT k st i st) -> ZwirnT k st i a -> ZwirnT k st i a+modify f x = set (f (get x)) x++get :: (Applicative k) => ZwirnT k st i a -> ZwirnT k st i st+get = withValueState (\(v, st) -> (fmap (const st) v, st))++set :: (Monad k) => ZwirnT k st i st -> ZwirnT k st i a -> ZwirnT k st i a+set st a = (withState . const <$> st) `innerApply` a++-- functions to act on state that is a map++-- | get value of specific key, providing a function in case key is not found+getMap :: (MultiMonad k, Ord key) => (Maybe b -> ZwirnT k (Map.Map key b) i b) -> ZwirnT k (Map.Map key b) i key -> ZwirnT k (Map.Map key b) i b+getMap fromLookup xc = innerJoin $ liftA2 (\k l -> fromLookup $ Map.lookup k l) xc (get (pure ()))++-- | set value of given key+setMap :: (Monad k, Ord key) => ZwirnT k (Map.Map key b) i key -> ZwirnT k (Map.Map key b) i b -> ZwirnT k (Map.Map key b) i a -> ZwirnT k (Map.Map key b) i a+setMap key b = set (liftA2 Map.insert key b <*> get (pure ()))++-- | modify+modifyMap :: (MultiMonad k, Ord key) => (Maybe b -> ZwirnT k (Map.Map key b) i b) -> ZwirnT k (Map.Map key b) i key -> (ZwirnT k (Map.Map key b) i b -> ZwirnT k (Map.Map key b) i b) -> ZwirnT k (Map.Map key b) i a -> ZwirnT k (Map.Map key b) i a+modifyMap fromLookup key f = setMap key (f (getMap fromLookup key))
+ src/zwirn-core/Zwirn/Core/Lib/Structure.hs view
@@ -0,0 +1,228 @@+{-# OPTIONS_GHC -Wno-type-defaults #-}+{-# HLINT ignore "Use tuple-section" #-}+{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}++module Zwirn.Core.Lib.Structure where++{-+ Structure.hs - functions manipulating the 'structure' of signals+ Copyright (C) 2025, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Control.Applicative+import Control.Monad (join)+import Data.List (mapAccumL)+import Music.Theory.Bjorklund (bjorklund, iseq)+import Numeric (showIntAtBase)+import Text.ParserCombinators.ReadP+import Zwirn.Core.Core+import Zwirn.Core.Lib.Core+import Zwirn.Core.Lib.Modulate+import Zwirn.Core.Time+import Zwirn.Core.Tree hiding (concat)+import Zwirn.Core.Types+import Prelude hiding (enumFromTo)++runFromTo :: (Ord a, Num a, Monad k, HasSilence k) => ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a+runFromTo xz yz = join $ en <$> xz <*> yz+ where+ en x y = fastcat $ map pure $ enumerateFromTo x y++runFromThenTo :: (Ord a, Num a, Monad k, HasSilence k) => ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a+runFromThenTo xz yz zz = join $ en <$> xz <*> yz <*> zz+ where+ en x y z = fastcat $ map pure $ enumerateFromThenTo x y z++slowrunFromTo :: (Ord a, Num a, Monad k, HasSilence k) => ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a+slowrunFromTo xz yz = join $ en <$> xz <*> yz+ where+ en x y = slowcat $ map pure $ enumerateFromTo x y++slowrunFromThenTo :: (Ord a, Num a, Monad k, HasSilence k) => ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a+slowrunFromThenTo xz yz zz = join $ en <$> xz <*> yz <*> zz+ where+ en x y z = slowcat $ map pure $ enumerateFromThenTo x y z++run :: (Monad k, HasSilence k) => ZwirnT k st i Int -> ZwirnT k st i Int+run = runFromTo (pure 0)++slowrun :: (Monad k, HasSilence k) => ZwirnT k st i Int -> ZwirnT k st i Int+slowrun = slowrunFromTo (pure 0)++sampleAndHold :: (MultiMonad k, HasSilence k) => ZwirnT k st i Int -> ZwirnT k st i a -> ZwirnT k st i a+sampleAndHold iz az = segment iz $ innerJoin $ sampleAndHold' <$> iz+ where+ sampleAndHold' i+ | i <= 0 = silence+ | otherwise = zwirn q+ where+ q t = unzwirn az (fromIntegral (floor $ tTime t * fromIntegral i) / fromIntegral i)++-- struct combines the inner time (structure) of the first argument with the values of the second one+-- this function does not 'sample and hold' like in tidal/strudel+struct :: (MultiMonad k) => ZwirnT k st i a -> ZwirnT k st i b -> ZwirnT k st i b+struct = withInner2 (liftA2Both f)+ where+ f (v1, st) (v2, _) = (Value (value v2) (time v1) (info v1), st)++segment :: (MultiMonad k, HasSilence k) => ZwirnT k st i Int -> ZwirnT k st i a -> ZwirnT k st i a+segment = struct . run++euclidOff :: (HasSilence k, Monad k) => ZwirnT k st i Int -> ZwirnT k st i Int -> ZwirnT k st i Int -> ZwirnT k st i a -> ZwirnT k st i a+euclidOff i1 i2 i3 x = (euclidOff' <$> i1 <*> i2 <*> i3) `innerApply` x+ where+ euclidOff' a b off y = timecat $ map (\i -> (fromIntegral i :: Time, y)) ts+ where+ ts = rot off $ iseq $ bjorklund (a, b)+ rot n xs = take lxs . drop ((-n) `mod` lxs) . cycle $ xs where lxs = length xs++euclid :: (HasSilence k, Monad k) => ZwirnT k st i Int -> ZwirnT k st i Int -> ZwirnT k st i a -> ZwirnT k st i a+euclid x y = euclidOff x y (pure 0)++left :: (MultiMonad k) => (ZwirnT k st i a -> ZwirnT k st i b -> ZwirnT k st i c) -> ZwirnT k st i a -> ZwirnT k st i b -> ZwirnT k st i c+left f x y = struct x $ f x y++right :: (MultiMonad k) => (ZwirnT k st i a -> ZwirnT k st i b -> ZwirnT k st i c) -> ZwirnT k st i a -> ZwirnT k st i b -> ZwirnT k st i c+right f x y = struct y $ f x y++euclidean :: (Applicative k) => ZwirnT k st i Int -> ZwirnT k st i Int -> ZwirnT k st i Int -> ZwirnT k st i String+euclidean i1 i2 i3 = euclidean' <$> i1 <*> i2 <*> i3+ where+ euclidean' off a b = map toChar $ rotateList off $ bjorklund (a, b)+ toChar True = '1'+ toChar False = '0'++-------------------------------------------+----------- chunking notation -------------+-------------------------------------------++-- see Computational Models of Rhythm and Meter by Georg Boenn+-- nested chunks via [...]+-- they will occupy one unit (i.e. one eighth by default) with their duration subdived by the amount of steps within+-- example: [i] == eight triplet, [~!] == rhythm in quintuplets etc.++type Sequence = Tree Bool++singleChunk :: Char -> [Bool]+singleChunk '~' = [False]+singleChunk '.' = [True]+singleChunk '0' = [False]+singleChunk '1' = [True]+singleChunk 'I' = [True, False]+singleChunk ':' = [True, True]+singleChunk 'v' = [False, True]+singleChunk '-' = [True, False, False]+singleChunk '<' = [False, True, False]+singleChunk 'w' = [False, False, True]+singleChunk 'X' = [True, True, False]+singleChunk '>' = [True, False, True]+singleChunk '+' = [False, True, True]+singleChunk 'i' = [True, True, True]+singleChunk 'H' = [True, False, False, False]+singleChunk '!' = [True, True, False, False]+singleChunk _ = []++pChunk :: ReadP [Sequence]+pChunk = do+ c <- get+ if c == '[' then pfail else return $ map Leaf $ singleChunk c++pChunks :: ReadP [Sequence]+pChunks = do+ _ <- char '['+ xs <- manyTill (pChunks +++ pChunk) (char ']')+ return [Branch $ concat xs]++pOuterChunks :: ReadP [Sequence]+pOuterChunks = concat <$> many1 (pChunks +++ pChunk)++runChunk :: String -> Maybe [Sequence]+runChunk s = case map fst $ filter (\(_, x) -> null x) $ readP_to_S pOuterChunks s of+ (x : _) -> Just x+ _ -> Nothing++seqToZwirn :: (HasSilence k, Monad k) => Sequence -> Int -> ZwirnT k st i Int+seqToZwirn t shif = seqToZwirnNum shif $ numberedTreeBool t+ where+ seqToZwirnNum sh (Leaf (i, True)) = pure $ i + sh+ seqToZwirnNum _ (Leaf (_, False)) = silence+ seqToZwirnNum _ (Branch []) = silence+ seqToZwirnNum sh (Branch xs) = fastcyclecat $ map (\x -> (1 / fromIntegral (length xs), seqToZwirnNum sh x)) xs++chunkWith :: (HasSilence k, MultiMonad k) => ZwirnT k st i Double -> ZwirnT k st i String -> ZwirnT k st i Int+chunkWith m s = innerJoin $ fullChunk' <$> m <*> s+ where+ fullChunk' d i = case runChunk i of+ Nothing -> silence+ Just ss -> if d == 0 then silence else fastcyclecat $ snd $ mapAccumL (\k se -> (k + countTrue se, (1 / realToFrac d, seqToZwirn se k))) 0 ss+ where+ countTrue (Leaf False) = 0+ countTrue (Leaf True) = 1+ countTrue (Branch xs) = sum (map countTrue xs)++chunk :: (HasSilence k, MultiMonad k, State k st i) => ZwirnT k st i String -> ZwirnT k st i Int+chunk = chunkWith beatsPerCycle++chunked :: (HasSilence k, MultiMonad k, State k st i) => ZwirnT k st i String -> ZwirnT k st i a -> ZwirnT k st i a+chunked = struct . chunk++binary :: (Applicative k) => ZwirnT k st i Int -> ZwirnT k st i String+binary = fmap (\i -> showIntAtBase 2 sel i "")+ where+ sel 0 = '0'+ sel _ = '1'++christoffelWord :: Int -> Int -> String+christoffelWord m n = snd $ foldl (christoffelWord' m n) (0, "") [1 .. n]+ where+ christoffelWord' :: Int -> Int -> (Int, String) -> Int -> (Int, String)+ christoffelWord' k l (prev, out) i = (y, out ++ "1" ++ bs)+ where+ y = floor $ fromIntegral i * fromIntegral k / fromIntegral l+ test = y - prev+ bs = replicate test '0'++christoffel :: (Applicative k) => ZwirnT k st i Int -> ZwirnT k st i Int -> ZwirnT k st i String+christoffel m n = christoffelWord <$> m <*> n++rotate :: (Applicative k) => ZwirnT k st i Int -> ZwirnT k st i [a] -> ZwirnT k st i [a]+rotate i xs = rotateList <$> i <*> xs++rotateList :: Int -> [a] -> [a]+rotateList n xs = take lxs . drop ((-n) `mod` lxs) . cycle $ xs+ where+ lxs = length xs++neg :: (Applicative k) => ZwirnT k st i String -> ZwirnT k st i String+neg = fmap neg'+ where+ neg' ('0' : xs) = '1' : neg' xs+ neg' ('1' : xs) = '0' : neg' xs+ neg' ('~' : xs) = '.' : neg' xs+ neg' ('.' : xs) = '~' : neg' xs+ neg' ('I' : xs) = 'v' : neg' xs+ neg' ('v' : xs) = 'I' : neg' xs+ neg' (':' : xs) = '~' : '~' : neg' xs+ neg' ('-' : xs) = '+' : neg' xs+ neg' ('+' : xs) = '-' : neg' xs+ neg' ('>' : xs) = '<' : neg' xs+ neg' ('<' : xs) = '>' : neg' xs+ neg' ('w' : xs) = 'X' : neg' xs+ neg' ('X' : xs) = 'w' : neg' xs+ neg' ('i' : xs) = '~' : '~' : '~' : neg' xs+ neg' ('H' : xs) = '~' : 'i' : neg' xs+ neg' ('!' : xs) = '~' : '+' : neg' xs+ neg' ys = ys
+ src/zwirn-core/Zwirn/Core/Lib/Text.hs view
@@ -0,0 +1,29 @@+module Zwirn.Core.Lib.Text where++{-+ Text.hs - lifting functions on text to signals+ Copyright (C) 2025, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import qualified Data.Text as T+import Zwirn.Core.Core ()+import Zwirn.Core.Types++append :: (MultiApplicative k) => ZwirnT k st i T.Text -> ZwirnT k st i T.Text -> ZwirnT k st i T.Text+append = liftA2Left T.append++length :: (Functor k) => ZwirnT k st i T.Text -> ZwirnT k st i Int+length = fmap T.length
+ src/zwirn-core/Zwirn/Core/Query.hs view
@@ -0,0 +1,75 @@+{-# LANGUAGE BangPatterns #-}+{-# OPTIONS_GHC -Wno-orphans #-}+{-# OPTIONS_GHC -Wno-type-defaults #-}++module Zwirn.Core.Query where++{-+ Query.hs - querying signals for breakpoints+ (i.e. the zeroes of the fractional part of the inner time of a signal)+ Copyright (C) 2025, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Data.List (uncons)+import Zwirn.Core.Time+import Zwirn.Core.Tree hiding (concat)+import Zwirn.Core.Types++type Breakpoint st i a = (Time, Value i a, st)++instance (Show a, Num st, ToList k) => Show (ZwirnT k st i a) where+ show = show . findAllValuesWithTime (Time 0 1, Time 1 1) 0++findAllValuesWithTime :: (ToList k) => (Time, Time) -> st -> ZwirnT k st i a -> [(Time, a)]+findAllValuesWithTime = findAllValuesWithTimePrec 0.005++findAllValuesWithTimeState :: (ToList k) => (Time, Time) -> st -> ZwirnT k st i a -> ([(Time, a)], st)+findAllValuesWithTimeState = findAllValuesWithTimeStatePrec 0.005++findAllValuesWithTimePrec :: (ToList k) => Time -> (Time, Time) -> st -> ZwirnT k st i a -> [(Time, a)]+findAllValuesWithTimePrec prec (start, end) st z = map (\(t, v, _) -> (t, value v)) xs+ where+ (xs, _) = findAllBreakpoints prec start end st z++findAllValuesWithTimeStatePrec :: (ToList k) => Time -> (Time, Time) -> st -> ZwirnT k st i a -> ([(Time, a)], st)+findAllValuesWithTimeStatePrec prec (start, end) st z = (map (\(t, v, _) -> (t, value v)) xs, st')+ where+ (xs, st') = findAllBreakpoints prec start end st z++checkBreakpoint :: (ToList k) => Time -> Time -> st -> ZwirnT k st i a -> ([Breakpoint st i a], st)+checkBreakpoint !prec !now st z = (concatMap func vs, newst)+ where+ vs = toList $ unzwirn z now st+ func (v, _) = [(now, v, st) | breakpointCondition prec (time v)]+ newst = case uncons vs of+ (Just ((_, st'), _)) -> st'+ Nothing -> st++breakpointCondition :: Time -> Time -> Bool+breakpointCondition prec (Time !t !diff)+ | diff > 0 = frac t / diff < tTime prec+ | diff == 0 = False+ | otherwise = frac (abs t) / abs diff < tTime prec++findAllBreakpoints :: (ToList k) => Time -> Time -> Time -> st -> ZwirnT k st i a -> ([Breakpoint st i a], st)+findAllBreakpoints prec !now end initialSt z = go [] now initialSt+ where+ !limit = end - prec+ go acc !t !st+ | t > limit = (reverse acc, st)+ | otherwise = case checkBreakpoint prec t st z of+ ([], st') -> go acc (t + prec) st'+ (bps, newst) -> go (reverse bps ++ acc) (t + prec) newst
+ src/zwirn-core/Zwirn/Core/Time.hs view
@@ -0,0 +1,89 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# OPTIONS_GHC -Wno-orphans #-}+{-# OPTIONS_GHC -Wno-type-defaults #-}++module Zwirn.Core.Time where++{-+ Time.hs - automated differentiation for time+ Copyright (C) 2025, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++data Time+ = Time {tTime :: !Rational, tDiff :: !Rational}+ deriving (Eq, Ord)++instance Show Time where+ show (Time x _) = show x++showAll :: Time -> String+showAll (Time x y) = "(" ++ show x ++ "," ++ show y ++ ")"++instance Num Time where+ Time x x' + Time y y' = Time (x + y) (x' + y')+ Time x x' * Time y y' = Time (x * y) (y' * x + x' * y)+ fromInteger x = Time (fromInteger x) 0+ negate (Time x x') = Time (negate x) (negate x')+ signum (Time x _) = Time (signum x) 0+ abs (Time x x') = Time (abs x) (x' * signum x)++instance Enum Time where+ toEnum i = Time (fromIntegral i) 0+ fromEnum (Time i _) = fromEnum i++instance Fractional Time where+ fromRational x = Time x 0+ recip (Time x x') = Time (recip x) (-(x' / x * x))++instance Real Time where+ toRational (Time x _) = x++instance RealFrac Time where+ properFraction (Time x x') = (i, Time p x')+ where+ (i, p) = properFraction x++instance Floating Rational where+ pi = toRational pi+ exp = toRational . exp . fromRational+ log = toRational . log . fromRational+ sin = toRational . sin . fromRational+ cos = toRational . cos . fromRational+ asin = toRational . asin . fromRational+ acos = toRational . acos . fromRational+ atan = toRational . atan . fromRational+ sinh = toRational . sinh . fromRational+ cosh = toRational . cosh . fromRational+ asinh = toRational . asinh . fromRational+ acosh = toRational . acosh . fromRational+ atanh = toRational . atanh . fromRational++instance Floating Time where+ pi = Time pi 0+ exp (Time x x') = Time (exp x) (x' * exp x)+ log (Time x x') = Time (log x) (x' / x)+ sqrt (Time x x') = Time (sqrt x) (x' / (2 * sqrt x))+ sin (Time x x') = Time (sin x) (x' * cos x)+ cos (Time x x') = Time (cos x) (x' * (-sin x))+ asin (Time x x') = Time (asin x) (x' / sqrt (1 - x * x))+ acos (Time x x') = Time (acos x) (x' / (-sqrt (1 - x * x)))+ atan (Time x x') = Time (atan x) (1 / ((x' * x') + 1))+ sinh (Time x x') = Time (sinh x) (cosh x')+ cosh (Time x x') = Time (cosh x) (sinh x')+ asinh (Time x x') = Time (asinh x) (1 / sqrt ((x' * x') + 1))+ acosh (Time x x') = Time (acosh x) (1 / sqrt (x' - 1) * sqrt (x' + 1))+ atanh (Time x x') = Time (atanh x) (1 / (1 - (x' * x')))
+ src/zwirn-core/Zwirn/Core/Tree.hs view
@@ -0,0 +1,221 @@+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE TypeApplications #-}+{-# OPTIONS_GHC -Wno-orphans #-}+{-# OPTIONS_GHC -Wno-type-defaults #-}++module Zwirn.Core.Tree where++{-+ Tree.hs - a structure for parallel signals+ Copyright (C) 2025, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Data.Fixed (mod')+import Zwirn.Core.Types++nth :: (RealFrac r) => r -> [a] -> a+nth = wrapAt++wrapAt :: (RealFrac r) => r -> [a] -> a+wrapAt t ls = ls !! phase+ where+ phase = mod (floor t) (length ls)++frac :: (Real r) => r -> r+frac d = mod' d 1++data Tree a+ = Leaf a+ | Branch [Tree a]+ deriving (Show, Eq, Functor)++instance ToList Tree where+ toList (Leaf a) = [a]+ toList (Branch as) = concatMap toList as++(!!!) :: (RealFrac b) => [a] -> b -> a+(!!!) as r = nth r as++empty :: Tree a+empty = Branch []++isEmpty :: Tree a -> Bool+isEmpty (Leaf _) = False+isEmpty (Branch ts) = all isEmpty ts++singleton :: a -> Tree a+singleton = Leaf++fromList :: [a] -> Tree a+fromList as = Branch $ map singleton as++look :: Int -> Tree a -> Tree a+look _ (Leaf x) = Leaf x+look _ (Branch []) = empty+look i (Branch xs) = xs !!! fromIntegral i++look' :: Int -> Tree a -> Tree a+look' 0 (Leaf x) = Leaf x+look' _ (Leaf _) = empty+look' i (Branch xs) = if length xs > i && i >= 0 then xs !! i else empty++lookup :: [Int] -> Tree a -> Tree a+lookup is x = foldl (flip look) x is++concatMapTree :: (a -> Tree b) -> Tree a -> Tree b+concatMapTree f x = squeezeJoin $ fmap f x++topLength :: Tree a -> Int+topLength (Leaf _) = 1+topLength (Branch xs) = length xs++push :: Tree a -> Tree a -> Tree a+push x l@(Leaf _) = Branch [l, x]+push x (Branch xs) = Branch (xs ++ [x])++concat :: Tree a -> Tree a -> Tree a+concat l1@(Leaf _) l2@(Leaf _) = Branch [l1, l2]+concat l@(Leaf _) (Branch xs) = Branch (l : xs)+concat (Branch xs) l@(Leaf _) = Branch (xs ++ [l])+concat (Branch xs) (Branch ys) = Branch (xs ++ ys)++pop :: Tree a -> Tree a+pop (Leaf _) = empty+pop (Branch []) = empty+pop (Branch xs) = Branch (take (length xs - 1) xs)++insertT :: Int -> Tree a -> Tree a -> Tree a+insertT 0 x (Leaf y) = Branch [x, Leaf y]+insertT _ x (Leaf y) = Branch [Leaf y, x]+insertT i x (Branch ys) = Branch (ys1 ++ [x] ++ ys2)+ where+ (ys1, ys2) = splitAt i ys++removeT :: Int -> Tree a -> Tree a+removeT _ (Leaf _) = empty+removeT i (Branch xs) = case splitAt i xs of+ (xs1, []) -> Branch xs1+ (xs1, _ : xs2) -> Branch $ xs1 ++ xs2++numberedTree :: Tree a -> Tree (Int, a)+numberedTree = snd . numberedTreeRec 0+ where+ numberedTreeRec :: Int -> Tree a -> (Int, Tree (Int, a))+ numberedTreeRec n (Leaf x) = (n + 1, Leaf (n, x))+ numberedTreeRec n (Branch []) = (n, Branch [])+ numberedTreeRec n (Branch (x : xs)) = (fst $ last fs, Branch $ map snd fs)+ where+ l = numberedTreeRec n x+ fs = foldl' folder [l] xs+ folder is t = is ++ [numberedTreeRec m t]+ where+ m = fst $ last is++numberedTreeBool :: Tree Bool -> Tree (Int, Bool)+numberedTreeBool = snd . numberedTreeRec 0+ where+ numberedTreeRec :: Int -> Tree Bool -> (Int, Tree (Int, Bool))+ numberedTreeRec n (Leaf True) = (n + 1, Leaf (n, True))+ numberedTreeRec n (Leaf False) = (n, Leaf (n, False))+ numberedTreeRec n (Branch []) = (n, Branch [])+ numberedTreeRec n (Branch (x : xs)) = (fst $ last fs, Branch $ map snd fs)+ where+ l = numberedTreeRec n x+ fs = foldl' folder [l] xs+ folder is t = is ++ [numberedTreeRec m t]+ where+ m = fst $ last is++-------------------------------------------------------+------------------- APPLICATIVE STUFF -----------------+-------------------------------------------------------++instance MultiApplicative [] where+ liftA2Left _ [] _ = []+ liftA2Left _ _ [] = []+ liftA2Left f as bs = map (\i -> f (as !! i) (bs !! floor @Double ((fromIntegral i / fromIntegral n) * fromIntegral m))) [0 .. n - 1]+ where+ n = length as+ m = length bs+ liftA2Right f as bs = liftA2Left (flip f) bs as++instance Applicative Tree where+ pure = Leaf+ liftA2 f (Leaf x) (Leaf y) = Leaf $ f x y+ liftA2 f l@(Leaf _) (Branch ys) = Branch $ map (liftA2 f l) ys+ liftA2 f (Branch xs) l@(Leaf _) = Branch $ map (\x -> liftA2 f x l) xs+ liftA2 f (Branch xs) (Branch ys) = Branch $ lift2Both (liftA2 f) xs ys++instance MultiApplicative Tree where+ liftA2Left f (Leaf x) (Leaf y) = Leaf $ f x y+ liftA2Left f l@(Leaf _) (Branch ys) = Branch $ map (liftA2Left f l) ys+ liftA2Left f (Branch xs) l@(Leaf _) = Branch $ map (\x -> liftA2Left f x l) xs+ liftA2Left f (Branch xs) (Branch ys) = Branch $ liftA2Left (liftA2Left f) xs ys+ liftA2Right f (Leaf x) (Leaf y) = Leaf $ f x y+ liftA2Right f l@(Leaf _) (Branch ys) = Branch $ map (liftA2Right f l) ys+ liftA2Right f (Branch xs) l@(Leaf _) = Branch $ map (\x -> liftA2Right f x l) xs+ liftA2Right f (Branch xs) (Branch ys) = Branch $ liftA2Right (liftA2Right f) xs ys++lift2Both :: (a -> b -> c) -> [a] -> [b] -> [c]+lift2Both f as bs =+ if n < m+ then liftA2Right f as bs+ else liftA2Left f as bs+ where+ n = length as+ m = length bs++--------------------------------------------------+------------------- MONAD STUFF ------------------+--------------------------------------------------++instance MultiMonad [] where+ innerJoin = Prelude.concat+ outerJoin = Prelude.concat+ squeezeJoin = Prelude.concat++instance Monad Tree where+ (>>=) x f = innerJoin $ f <$> x++instance MultiMonad Tree where+ innerJoin t = squeezeJoin $ mapWithPos reduceNested t+ outerJoin = innerJoin+ squeezeJoin (Leaf x) = x+ squeezeJoin (Branch xs) = Branch $ map squeezeJoin xs++reduce :: (Int, Int) -> Tree a -> [Tree a]+reduce _ (Leaf x) = [Leaf x]+reduce (i, n) (Branch xs) =+ if count <= 0+ then []+ else take count $ drop (start `mod` m) (cycle xs)+ where+ m = length xs+ start = floor (fromIntegral (m * i) / fromIntegral n)+ end = ceiling (fromIntegral (m * (i + 1)) / fromIntegral n)+ count = end - start++reduceNested :: [(Int, Int)] -> Tree a -> Tree a+reduceNested [] x = x+reduceNested (i : is) x = Branch $ map (reduceNested is) (reduce i x)++mapWithPos :: ([(Int, Int)] -> a -> b) -> Tree a -> Tree b+mapWithPos f = go []+ where+ go path (Leaf x) = Leaf (f (reverse path) x)+ go path (Branch ts) = Branch [go ((i, length ts) : path) t | (i, t) <- zip [0 ..] ts]
+ src/zwirn-core/Zwirn/Core/Types.hs view
@@ -0,0 +1,91 @@+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE MultiParamTypeClasses #-}++module Zwirn.Core.Types where++{-+ Types.hs - defines all core types and classes+ Copyright (C) 2025, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Control.Monad (join)+import Control.Monad.Identity+import Zwirn.Core.Time++data Value i a+ = Value {value :: a, time :: !Time, info :: [i]}+ deriving (Eq, Show, Ord, Functor)++newtype ZwirnT k st i a = ZwirnT {unZwirnT :: Time -> st -> k (Value i a, st)}++unzwirn :: ZwirnT k st i a -> Time -> st -> k (Value i a, st)+unzwirn = unZwirnT++zwirn :: (Time -> st -> k (Value i a, st)) -> ZwirnT k st i a+zwirn = ZwirnT++-- | represents instances of k that allow for a special zwirn with no values+class HasSilence k where+ silence :: ZwirnT k st i a++class State k st i where+ beatsPerCycle :: ZwirnT k st i Double++class ToList k where+ toList :: k a -> [a]++infixl 4 *>++infixl 4 <*++class (Applicative f) => MultiApplicative f where+ liftA2Left :: (a -> b -> c) -> f a -> f b -> f c+ liftA2Right :: (a -> b -> c) -> f a -> f b -> f c+ liftA2Both :: (a -> b -> c) -> f a -> f b -> f c+ liftA2Both = liftA2+ (*>) :: f (a -> b) -> f a -> f b+ (*>) = liftA2Right id+ (<*) :: f (a -> b) -> f a -> f b+ (<*) = liftA2Left id++class (MultiApplicative m, Monad m) => MultiMonad m where+ innerJoin :: m (m a) -> m a+ innerJoin = join+ outerJoin :: m (m a) -> m a+ squeezeJoin :: m (m a) -> m a++instance ToList [] where+ toList = id++instance ToList Identity where+ toList (Identity x) = pure x++instance MultiApplicative Identity where+ liftA2Left f x y = Identity $ f (runIdentity x) (runIdentity y)+ liftA2Right f x y = Identity $ f (runIdentity x) (runIdentity y)++instance MultiMonad Identity where+ innerJoin (Identity x) = x+ outerJoin (Identity x) = x+ squeezeJoin (Identity x) = x++instance Applicative (Value i) where+ pure x = Value x 0 []+ liftA2 f (Value x t1 i1) (Value y _ i2) = Value (f x y) t1 (i1 ++ i2)++instance MultiApplicative (Value i) where+ liftA2Left = liftA2+ liftA2Right f (Value x _ i1) (Value y t2 i2) = Value (f x y) t2 (i1 ++ i2)
+ src/zwirn-lang/Zwirn/Language.hs view
@@ -0,0 +1,44 @@+module Zwirn.Language+ ( module Zwirn.Language.Block,+ module Zwirn.Language.Compiler,+ module Zwirn.Language.Lexer,+ module Zwirn.Language.Parser,+ module Zwirn.Language.Pretty,+ module Zwirn.Language.Simple,+ module Zwirn.Language.Syntax,+ module Zwirn.Language.TypeCheck.Constraint,+ module Zwirn.Language.Environment,+ module Zwirn.Language.TypeCheck.Infer,+ module Zwirn.Language.TypeCheck.Types,+ )+where++import Zwirn.Language.Block+import Zwirn.Language.Compiler+import Zwirn.Language.Environment+import Zwirn.Language.Lexer+import Zwirn.Language.Parser+import Zwirn.Language.Pretty+import Zwirn.Language.Simple+import Zwirn.Language.Syntax+import Zwirn.Language.TypeCheck.Constraint+import Zwirn.Language.TypeCheck.Infer+import Zwirn.Language.TypeCheck.Types++{-+ Language.hs - re-exports of all zwirn language modules+ Copyright (C) 2023, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}
+ src/zwirn-lang/Zwirn/Language/Block.hs view
@@ -0,0 +1,109 @@+{-# LANGUAGE OverloadedStrings #-}++module Zwirn.Language.Block+ ( Block (..),+ BlockError,+ Line (..),+ getBlock,+ structureLines,+ getBlockContent,+ getLineIndent,+ getSingleLine,+ getBlockStart,+ getBlockEnd,+ )+where++{-+ Block.hs - layout sensitive representation of blocks of code+ Copyright (C) 2023, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import qualified Data.List.NonEmpty as NE+import qualified Data.Text as T++-- | A Block of code is a non-empty list of lines of code+newtype Block = Block+ { bContent :: NE.NonEmpty Line+ }+ deriving (Show, Eq)++-- | A Line of code has a start line and an end line, if start /= end, it represents a multiline+data Line = Line+ { lStart :: Int,+ lEnd :: Int,+ lContent :: T.Text+ }+ deriving (Show, Eq)++type BlockError = String++-- | Get the start line of a block, assumes that the lines are ordered.+getBlockStart :: Block -> Int+getBlockStart (Block ls) = lStart $ NE.head ls++-- | Get the end line of a block, assumes that the lines are ordered.+getBlockEnd :: Block -> Int+getBlockEnd (Block ls) = lEnd $ NE.last ls++-- | Get the contents of a block by merging the contents of its lines+getBlockContent :: Block -> [T.Text]+getBlockContent (Block ls) = NE.toList $ NE.map lContent ls++-- | Given a line number and a list of blocks, get the block that starts before and ends after the line.+getBlock :: Int -> [Block] -> Either BlockError Block+getBlock _ [] = Left "no block of code at current line"+getBlock num (block : bs) =+ if start <= num && num <= end+ then Right block+ else getBlock num bs+ where+ start = getBlockStart block+ end = getBlockEnd block++-- | Given a line number and a non-empty list of lines, get the line that starts before and ends after the line number.+getLine' :: Int -> NE.NonEmpty Line -> Either BlockError Line+getLine' num (l@(Line st en _) NE.:| ls) =+ if st <= num && num <= en+ then Right l+ else case NE.nonEmpty ls of+ Just xs -> getLine' num xs+ Nothing -> Left "no line at current position"++getSingleLine :: Int -> [Block] -> Either BlockError Line+getSingleLine i bs = do+ (Block ls) <- getBlock i bs+ getLine' i ls++-- | Parsing Blocks of code is layout sensitive - this function merges lines of code into a single multiline+-- | whenever the indent of the first line is smaller than the indent of the following lines+structureLines :: NE.NonEmpty Line -> NE.NonEmpty Line+structureLines (l NE.:| ls) = mergeLines vs NE.:| rs+ where+ vs = l NE.:| takeWhile (\x -> getLineIndent x > getLineIndent l) ls+ rs = case NE.nonEmpty $ drop (length vs - 1) ls of+ Just xs -> NE.toList $ structureLines xs+ Nothing -> []++-- | checks how many spaces or tabs are at the start of the line content+getLineIndent :: Line -> Int+getLineIndent (Line _ _ lc) = T.length $ T.takeWhile (\c -> c `elem` (" \t" :: String)) lc++-- | combines lines to a single multiline by merging their content and adjusting the end line+mergeLines :: NE.NonEmpty Line -> Line+mergeLines ((Line lst len c) NE.:| ls) = Line lst end (T.concat $ c : map lContent ls)+ where+ end = if null ls then len else lEnd $ last ls
+ src/zwirn-lang/Zwirn/Language/Builtin/Internal.hs view
@@ -0,0 +1,29 @@+{-# OPTIONS_GHC -Wno-orphans #-}++module Zwirn.Language.Builtin.Internal where++import qualified Data.Map as Map+import Data.String+import Data.Text (Text, pack)+import Zwirn.Language.Environment+import Zwirn.Language.Evaluate hiding (insert)+import Zwirn.Language.Parser (parseScheme)+import Zwirn.Language.TypeCheck.Types++instance IsString Scheme where+ fromString s = fromEither $ parseScheme (pack s)+ where+ fromEither (Right r) = r+ fromEither (Left e) = error e++(===) :: Text -> Expression -> Map.Map Text Expression+(===) = Map.singleton++(<::) :: Map.Map Text Expression -> Scheme -> Map.Map Text (Expression, Scheme)+(<::) x s = fmap (\l -> (l, s)) x++(--|) :: Map.Map Text (Expression, Scheme) -> Text -> Map.Map Text AnnotatedExpression+(--|) n t = fmap (\(x, s) -> Annotated x s (Just t)) n++noDesc :: Map.Map Text (Expression, Scheme) -> Map.Map Text AnnotatedExpression+noDesc = fmap (\(x, s) -> Annotated x s Nothing)
+ src/zwirn-lang/Zwirn/Language/Builtin/Parameters.hs view
@@ -0,0 +1,470 @@+{-# LANGUAGE OverloadedStrings #-}++module Zwirn.Language.Builtin.Parameters where++import qualified Data.Map as Map+import Data.Text (Text, append, uncons)+import Zwirn.Core.Cord (stack)+import Zwirn.Core.Lib.Cord (applyCord)+import Zwirn.Core.Lib.Map+import Zwirn.Language.Builtin.Internal+import Zwirn.Language.Environment+import Zwirn.Language.Evaluate (Expression, Zwirn, toExp)++builtinParams :: Map.Map Text AnnotatedExpression+builtinParams = addAliases aliases $ Map.unions [builtinTextParams, builtinNumberParams, builtinIntParams]++builtinTextParams :: Map.Map Text AnnotatedExpression+builtinTextParams = Map.unions $ map (\t -> noDesc $ t === toExp ((fmap toExp . singleton (pure t)) :: Zwirn Expression -> Zwirn Expression) <:: "Text -> Map") textParams++builtinNumberParams :: Map.Map Text AnnotatedExpression+builtinNumberParams = Map.unions $ map (\t -> noDesc $ t === toExp ((fmap toExp . singleton (pure t)) :: Zwirn Double -> Zwirn Expression) <:: "Number -> Map") numberParams++builtinIntParams :: Map.Map Text AnnotatedExpression+builtinIntParams = Map.unions $ map (\t -> noDesc $ t === toExp ((fmap toExp . singleton (pure t)) :: Zwirn Int -> Zwirn Expression) <:: "Number -> Map") intParams++textParams :: [Text]+textParams = ["s", "unit", "vowel", "toArg"]++intParams :: [Text]+intParams = ["cut", "orbit"]++numberParams :: [Text]+numberParams =+ [ "accelerate",+ "amp",+ "attack",+ "bandf",+ "bandq",+ "begin",+ "binshift",+ "ccn",+ "ccv",+ "channel",+ "coarse",+ "comb",+ "crush",+ "cutoff",+ "decay",+ "delay",+ "delaytime",+ "detune",+ "distort",+ "djf",+ "dry",+ "dur",+ "end",+ "enhance",+ "expression",+ "fadeInTime",+ "fadeTime",+ "freeze",+ "freq",+ "from",+ "fshift",+ "gain",+ "gate",+ "harmonic",+ "hbrick",+ "hcutoff",+ "hold",+ "hresonance",+ "imag",+ "krush",+ "lagogo",+ "lbrick",+ "legato",+ "leslie",+ "lock",+ "midibend",+ "miditouch",+ "modwheel",+ "n",+ "note",+ "nudge",+ "octave",+ "octer",+ "octersub",+ "octersubsub",+ "offset",+ "overgain",+ "overshape",+ "pan",+ "panorient",+ "panspan",+ "pansplay",+ "panwidth",+ "partials",+ "phaserdepth",+ "phaserrate",+ "rate",+ "real",+ "release",+ "resonance",+ "ring",+ "ringdf",+ "ringf",+ "room",+ "sagogo",+ "scram",+ "shape",+ "size",+ "slide",+ "smear",+ "speed",+ "squiz",+ "sustain",+ "sustainpedal",+ "timescale",+ "timescalewin",+ "to",+ "tremolodepth",+ "tremolorate",+ "triode",+ "tsdelay",+ "velocity",+ "voice",+ "waveloss",+ "xsdelay"+ ]++aliases :: [(Text, Text)]+aliases =+ [ ("sound", "s"),+ ("voi", "voice"),+ ("up", "n"),+ ("tremr", "tremolorate"),+ ("tremdp", "tremolodepth"),+ ("sz", "size"),+ ("sus", "sustain"),+ ("sld", "slide"),+ ("scr", "scrash"),+ ("rel", "release"),+ ("por", "portamento"),+ ("phasr", "phaserrate"),+ ("phasdp", "phaserdepth"),+ ("number", "n"),+ ("lpq", "resonance"),+ ("lpf", "cutoff"),+ ("hpq", "hresonance"),+ ("hpf", "hcutoff"),+ ("gat", "gate"),+ ("fadeOutTime", "fadeTime"),+ ("dt", "delaytime"),+ ("dfb", "delayfeedback"),+ ("det", "detune"),+ ("delayt", "delaytime"),+ ("delayfb", "delayfeedback"),+ ("ctf", "cutoff"),+ ("bpq", "bandq"),+ ("bpf", "bandf"),+ ("att", "attack")+ ]++addAliases :: [(Text, Text)] -> Map.Map Text AnnotatedExpression -> Map.Map Text AnnotatedExpression+addAliases as x = Map.unions $ map look as ++ [x]+ where+ look (y, n) = case Map.lookup n x of+ Just a -> Map.singleton y a+ Nothing -> Map.empty++----------------------------------------------------------+---------------- defining note names ---------------------+----------------------------------------------------------++noteExpressions :: Map.Map Text AnnotatedExpression+noteExpressions = Map.unions $ map (\n -> noDesc $ n === toExp ((pure $ toNote n) :: Zwirn Int) <:: "Number") notes++chordExpressions :: Map.Map Text AnnotatedExpression+chordExpressions = Map.unions $ map (\(n, cs) -> noDesc $ n === toExp ((\z -> applyCord z (stack $ map pure cs)) :: Zwirn Int -> Zwirn Int) <:: "Number -> Number") chordTable++noteNames :: [Text]+noteNames = ["c", "d", "e", "f", "g", "a", "b"]++noteMods :: [Text]+noteMods = ["", "f", "s"]++noteOctaves :: [Text]+noteOctaves = ["", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]++notes :: [Text]+notes = [n `append` m `append` o | n <- noteNames, m <- noteMods, o <- noteOctaves]++modVal :: Char -> Int+modVal 'f' = -1+modVal 's' = 1+modVal _ = 0++nameVal :: Char -> Int+nameVal 'c' = 0+nameVal 'd' = 2+nameVal 'e' = 4+nameVal 'f' = 5+nameVal 'g' = 7+nameVal 'a' = 9+nameVal 'b' = 11+nameVal _ = 0++octVal :: Char -> Int+octVal '0' = -60+octVal '1' = -48+octVal '2' = -36+octVal '3' = -24+octVal '4' = -12+octVal '5' = 0+octVal '6' = 12+octVal '7' = 24+octVal '8' = 36+octVal '9' = 48+octVal _ = 0++toNote :: Text -> Int+toNote t = case uncons t of+ Just (x, xs) -> case uncons xs of+ Just (y, ys) -> case uncons ys of+ Just (z, _) -> nameVal x + modVal y + octVal z+ Nothing -> nameVal x + modVal y + octVal y+ Nothing -> nameVal x+ Nothing -> 0++-- the following is taken from https://hackage.haskell.org/package/tidal-1.9.5/docs/src/Sound.Tidal.Chords.html+chordTable :: (Num a) => [(Text, [a])]+chordTable =+ [ ("major", major),+ ("maj", major),+ ("M", major),+ ("aug", aug),+ ("plus", aug),+ ("sharp5", aug),+ ("six", six),+ ("sixNine", sixNine),+ ("six9", sixNine),+ ("sixby9", sixNine),+ ("6by9", sixNine),+ ("major7", major7),+ ("maj7", major7),+ ("major9", major9),+ ("maj9", major9),+ ("add9", add9),+ ("major11", major11),+ ("maj11", major11),+ ("add11", add11),+ ("major13", major13),+ ("maj13", major13),+ ("add13", add13),+ ("dom7", dom7),+ ("dom9", dom9),+ ("dom11", dom11),+ ("dom13", dom13),+ ("sevenFlat5", sevenFlat5),+ ("7f5", sevenFlat5),+ ("sevenSharp5", sevenSharp5),+ ("7s5", sevenSharp5),+ ("sevenFlat9", sevenFlat9),+ ("7f9", sevenFlat9),+ ("nine", nine),+ ("eleven", eleven),+ ("thirteen", thirteen),+ ("minor", minor),+ ("min", minor),+ ("m", minor),+ ("diminished", diminished),+ ("dim", diminished),+ ("minorSharp5", minorSharp5),+ ("msharp5", minorSharp5),+ ("mS5", minorSharp5),+ ("minor6", minor6),+ ("min6", minor6),+ ("m6", minor6),+ ("minorSixNine", minorSixNine),+ ("minor69", minorSixNine),+ ("min69", minorSixNine),+ ("minSixNine", minorSixNine),+ ("m69", minorSixNine),+ ("mSixNine", minorSixNine),+ ("m6by9", minorSixNine),+ ("minor7flat5", minor7flat5),+ ("minor7f5", minor7flat5),+ ("min7flat5", minor7flat5),+ ("min7f5", minor7flat5),+ ("m7flat5", minor7flat5),+ ("m7f5", minor7flat5),+ ("minor7", minor7),+ ("min7", minor7),+ ("m7", minor7),+ ("minor7sharp5", minor7sharp5),+ ("minor7s5", minor7sharp5),+ ("min7sharp5", minor7sharp5),+ ("min7s5", minor7sharp5),+ ("m7sharp5", minor7sharp5),+ ("m7s5", minor7sharp5),+ ("minor7flat9", minor7flat9),+ ("minor7f9", minor7flat9),+ ("min7flat9", minor7flat9),+ ("min7f9", minor7flat9),+ ("m7flat9", minor7flat9),+ ("m7f9", minor7flat9),+ ("minor7sharp9", minor7sharp9),+ ("minor7s9", minor7sharp9),+ ("min7sharp9", minor7sharp9),+ ("min7s9", minor7sharp9),+ ("m7sharp9", minor7sharp9),+ ("m7s9", minor7sharp9),+ ("diminished7", diminished7),+ ("dim7", diminished7),+ ("minor9", minor9),+ ("min9", minor9),+ ("m9", minor9),+ ("minor11", minor11),+ ("min11", minor11),+ ("m11", minor11),+ ("minor13", minor13),+ ("min13", minor13),+ ("m13", minor13),+ ("minorMajor7", minorMajor7),+ ("minMaj7", minorMajor7),+ ("mmaj7", minorMajor7),+ ("one", one),+ ("five", five),+ ("sus2", sus2),+ ("sus4", sus4),+ ("sevenSus2", sevenSus2),+ ("7sus2", sevenSus2),+ ("sevenSus4", sevenSus4),+ ("7sus4", sevenSus4),+ ("nineSus4", nineSus4),+ ("ninesus4", nineSus4),+ ("9sus4", nineSus4),+ ("sevenFlat10", sevenFlat10),+ ("7f10", sevenFlat10),+ ("nineSharp5", nineSharp5),+ ("9sharp5", nineSharp5),+ ("9s5", nineSharp5),+ ("minor9sharp5", minor9sharp5),+ ("minor9s5", minor9sharp5),+ ("min9sharp5", minor9sharp5),+ ("min9s5", minor9sharp5),+ ("m9sharp5", minor9sharp5),+ ("m9s5", minor9sharp5),+ ("sevenSharp5flat9", sevenSharp5flat9),+ ("7s5f9", sevenSharp5flat9),+ ("minor7sharp5flat9", minor7sharp5flat9),+ ("m7sharp5flat9", minor7sharp5flat9),+ ("elevenSharp", elevenSharp),+ ("minor11sharp", minor11sharp),+ ("m11sharp", minor11sharp),+ ("m11s", minor11sharp)+ ]+ where+ major :: (Num a) => [a]+ major = [0, 4, 7]+ aug :: (Num a) => [a]+ aug = [0, 4, 8]+ six :: (Num a) => [a]+ six = [0, 4, 7, 9]+ sixNine :: (Num a) => [a]+ sixNine = [0, 4, 7, 9, 14]+ major7 :: (Num a) => [a]+ major7 = [0, 4, 7, 11]+ major9 :: (Num a) => [a]+ major9 = [0, 4, 7, 11, 14]+ add9 :: (Num a) => [a]+ add9 = [0, 4, 7, 14]+ major11 :: (Num a) => [a]+ major11 = [0, 4, 7, 11, 14, 17]+ add11 :: (Num a) => [a]+ add11 = [0, 4, 7, 17]+ major13 :: (Num a) => [a]+ major13 = [0, 4, 7, 11, 14, 21]+ add13 :: (Num a) => [a]+ add13 = [0, 4, 7, 21]++ -- \** Dominant chords++ dom7 :: (Num a) => [a]+ dom7 = [0, 4, 7, 10]+ dom9 :: (Num a) => [a]+ dom9 = [0, 4, 7, 14]+ dom11 :: (Num a) => [a]+ dom11 = [0, 4, 7, 17]+ dom13 :: (Num a) => [a]+ dom13 = [0, 4, 7, 21]+ sevenFlat5 :: (Num a) => [a]+ sevenFlat5 = [0, 4, 6, 10]+ sevenSharp5 :: (Num a) => [a]+ sevenSharp5 = [0, 4, 8, 10]+ sevenFlat9 :: (Num a) => [a]+ sevenFlat9 = [0, 4, 7, 10, 13]+ nine :: (Num a) => [a]+ nine = [0, 4, 7, 10, 14]+ eleven :: (Num a) => [a]+ eleven = [0, 4, 7, 10, 14, 17]+ thirteen :: (Num a) => [a]+ thirteen = [0, 4, 7, 10, 14, 17, 21]++ -- \** Minor chords++ minor :: (Num a) => [a]+ minor = [0, 3, 7]+ diminished :: (Num a) => [a]+ diminished = [0, 3, 6]+ minorSharp5 :: (Num a) => [a]+ minorSharp5 = [0, 3, 8]+ minor6 :: (Num a) => [a]+ minor6 = [0, 3, 7, 9]+ minorSixNine :: (Num a) => [a]+ minorSixNine = [0, 3, 9, 7, 14]+ minor7flat5 :: (Num a) => [a]+ minor7flat5 = [0, 3, 6, 10]+ minor7 :: (Num a) => [a]+ minor7 = [0, 3, 7, 10]+ minor7sharp5 :: (Num a) => [a]+ minor7sharp5 = [0, 3, 8, 10]+ minor7flat9 :: (Num a) => [a]+ minor7flat9 = [0, 3, 7, 10, 13]+ minor7sharp9 :: (Num a) => [a]+ minor7sharp9 = [0, 3, 7, 10, 15]+ diminished7 :: (Num a) => [a]+ diminished7 = [0, 3, 6, 9]+ minor9 :: (Num a) => [a]+ minor9 = [0, 3, 7, 10, 14]+ minor11 :: (Num a) => [a]+ minor11 = [0, 3, 7, 10, 14, 17]+ minor13 :: (Num a) => [a]+ minor13 = [0, 3, 7, 10, 14, 17, 21]+ minorMajor7 :: (Num a) => [a]+ minorMajor7 = [0, 3, 7, 11]++ -- \** Other chords++ one :: (Num a) => [a]+ one = [0]+ five :: (Num a) => [a]+ five = [0, 7]+ sus2 :: (Num a) => [a]+ sus2 = [0, 2, 7]+ sus4 :: (Num a) => [a]+ sus4 = [0, 5, 7]+ sevenSus2 :: (Num a) => [a]+ sevenSus2 = [0, 2, 7, 10]+ sevenSus4 :: (Num a) => [a]+ sevenSus4 = [0, 5, 7, 10]+ nineSus4 :: (Num a) => [a]+ nineSus4 = [0, 5, 7, 10, 14]++ -- \** Questionable chords++ sevenFlat10 :: (Num a) => [a]+ sevenFlat10 = [0, 4, 7, 10, 15]+ nineSharp5 :: (Num a) => [a]+ nineSharp5 = [0, 1, 13]+ minor9sharp5 :: (Num a) => [a]+ minor9sharp5 = [0, 1, 14]+ sevenSharp5flat9 :: (Num a) => [a]+ sevenSharp5flat9 = [0, 4, 8, 10, 13]+ minor7sharp5flat9 :: (Num a) => [a]+ minor7sharp5flat9 = [0, 3, 8, 10, 13]+ elevenSharp :: (Num a) => [a]+ elevenSharp = [0, 4, 7, 10, 14, 18]+ minor11sharp :: (Num a) => [a]+ minor11sharp = [0, 3, 7, 10, 14, 18]
+ src/zwirn-lang/Zwirn/Language/Builtin/Prelude.hs view
@@ -0,0 +1,1032 @@+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -Wno-orphans #-}++module Zwirn.Language.Builtin.Prelude where++{-+ Builtin.hs - defines builtin functions+ Copyright (C) 2023, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import qualified Data.Map as Map+import Data.Text (Text)+import Zwirn.Core.Lib.Conditional as Z+import Zwirn.Core.Lib.Cord as C+import Zwirn.Core.Lib.Core as C+import Zwirn.Core.Lib.Map as M+import Zwirn.Core.Lib.Modulate+import Zwirn.Core.Lib.Number as N+import Zwirn.Core.Lib.Random+import Zwirn.Core.Lib.Structure as S+import Zwirn.Core.Time+import Zwirn.Language.Builtin.Internal+import Zwirn.Language.Builtin.Parameters+import Zwirn.Language.Environment+import Zwirn.Language.Evaluate hiding (insert)+import Zwirn.Language.TypeCheck.Types+import Zwirn.Stream.Types (Stream)++builtinEnvironment :: InterpreterEnv+builtinEnvironment = IEnv builtins instances++builtinEnvironmentWithStream :: Stream -> InterpreterEnv+builtinEnvironmentWithStream str = IEnv (Map.unions [builtins, streamFunctions str]) instances++instances :: [Instance]+instances =+ [ IsIn "Num" numberT,+ IsIn "Num" mapT,+ IsIn "Eq" numberT,+ IsIn "Eq" mapT,+ IsIn "Eq" textT,+ IsIn "Id" numberT,+ IsIn "Id" textT,+ IsIn "Id" mapT+ ]++builtinNames :: [Text]+builtinNames = Map.keys builtins++builtins :: Map.Map Text AnnotatedExpression+builtins =+ Map.unions+ [ coreFunctions,+ numberFunctions,+ signals,+ randomFunctions,+ timeFunctions,+ structureFunctions,+ conditionalFunctions,+ cordFunctions,+ mapFunctions,+ builtinParams,+ noteExpressions,+ chordExpressions+ ]++coreFunctions :: Map.Map Text AnnotatedExpression+coreFunctions =+ Map.unions+ [ "id"+ === lambda id+ <:: "a -> a"+ --| "identity function",+ "const"+ === lambda (lambda . const)+ <:: "a -> b -> a"+ --| "constant function - ignore second input",+ "scomb"+ === lambda (\f -> lambda $ \g -> lambda $ \x -> f ! x ! (g ! x))+ <:: "(a -> b -> c) -> (a -> b) -> a -> c"+ --| "S-combinator",+ "(.)"+ === lambda (\g -> lambda $ \f -> lambda $ \x -> g ! (f ! x))+ <:: "(b -> c) -> (a -> b) -> a -> c"+ --| "function composition",+ "flip"+ === lambda (\f -> lambda $ \y -> lambda $ \x -> f ! x ! y)+ <:: "(a -> b -> c) -> b -> a -> c"+ --| "flip arguments",+ "(\')"+ === toExp (flip apply :: Zwirn Expression -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression)+ <:: "a -> (a -> b) -> b"+ --| "apply argument to function, results are squeezed and zipped",+ "($)"+ === toExp (apply :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "(a -> b) -> a -> b"+ --| "apply function to argument, results are squeezed and zipped",+ "match"+ === toExp (matchApply :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "(a -> b) -> a -> b"+ --| "",+ "iterate"+ === toExp (C.iterate :: Zwirn Int -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> (a -> a) -> a -> a"+ --| "apply function to argument, n times",+ "(|$)"+ === toExp (outerApply :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "(a -> b) -> a -> b"+ --| "apply function to argument",+ "($|)"+ === toExp (innerApply :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "(a -> b) -> a -> b"+ --| "apply function to argument",+ "squeeze"+ === toExp (squeeze :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "(a -> b) -> a -> b"+ --| "map a function over the structure of the argument",+ "inner"+ === toExp (getInnerTime :: Zwirn Expression -> Zwirn Double)+ <:: "a -> Number"+ --| "get the inner time of a zwirn",+ "diff"+ === toExp (getSpeed :: Zwirn Expression -> Zwirn Double)+ <:: "a -> Number"+ --| "get the speed of a zwirn",+ "trig"+ === toExp (trig :: Zwirn Expression -> Zwirn Bool)+ <:: "a -> Number"+ --| "returns true on the trigger points, false otherwise",+ "recv"+ === toExp recv+ <:: "(Number -> Map) -> Number -> Map"+ --| "recieve a value from a bus and send it to the given parameter",+ "recvT"+ === toExp recvT+ <:: "Text -> Number -> Map"+ --| "like recv but takes the parameter name as text"+ ]++numberFunctions :: Map.Map Text AnnotatedExpression+numberFunctions =+ Map.unions+ [ "(|+)"+ === toExp ((+) :: Zwirn Expression -> Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a -> a"+ --| "addition",+ "(|-)"+ === toExp ((-) :: Zwirn Expression -> Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a -> a"+ --| "subtraction",+ "(|*)"+ === toExp ((*) :: Zwirn Expression -> Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a -> a"+ --| "multiplication",+ "(|/)"+ === toExp ((/) :: Zwirn Expression -> Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a -> a"+ --| "division",+ "negate"+ === toExp (negate :: Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a"+ --| "negate",+ "abs"+ === toExp (abs :: Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a"+ --| "absolute value",+ "signum"+ === toExp (signum :: Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a"+ --| "signum",+ "recip"+ === toExp (recip :: Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a"+ --| "reciprocal value",+ "pi"+ === toExp (pi :: Zwirn Expression)+ <:: "Number"+ --| "pi",+ "(|**)"+ === toExp ((**) :: Zwirn Expression -> Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a -> a"+ --| "exponentiation",+ "exp"+ === toExp (exp :: Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a"+ --| "exponential function",+ "log"+ === toExp (log :: Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a"+ --| "logarithm base 10",+ "sqrt"+ === toExp (sqrt :: Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a"+ --| "square root",+ "sin"+ === toExp (sin :: Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a"+ --| "sine function",+ "cos"+ === toExp (cos :: Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a"+ --| "cosine function",+ "tan"+ === toExp (tan :: Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a"+ --| "tangens",+ "asin"+ === toExp (asin :: Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a"+ --| "arc sine function",+ "acos"+ === toExp (acos :: Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a"+ --| "arc cosine function",+ "atan"+ === toExp (atan :: Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a"+ --| "arc tangens",+ "sinh"+ === toExp (sinh :: Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a"+ --| "hyperbolic sine",+ "cosh"+ === toExp (cosh :: Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a"+ --| "hyperbolic cosine",+ "tanh"+ === toExp (tan :: Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a"+ --| "hyperbolic tangens",+ "asinh"+ === toExp (asinh :: Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a"+ --| "hyperbolic arc sine function",+ "acosh"+ === toExp (acosh :: Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a"+ --| "hyperbolic arc cosine function",+ "atanh"+ === toExp (atanh :: Zwirn Expression -> Zwirn Expression)+ <:: "Num a => a -> a"+ --| "hyperbolic arc tangens",+ "mod"+ === toExp (N.mod :: Zwirn Double -> Zwirn Double -> Zwirn Double)+ <:: "Number -> Number -> Number"+ --| "modulo",+ "frac"+ === toExp (N.frac :: Zwirn Double -> Zwirn Double)+ <:: "Number -> Number"+ --| "fractional part of a number",+ "trunc"+ === toExp (N.trunc :: Zwirn Double -> Zwirn Int)+ <:: "Number -> Number"+ --| "truncate",+ "ceil"+ === toExp (N.ceil :: Zwirn Double -> Zwirn Int)+ <:: "Number -> Number"+ --| "round up",+ "floor"+ === toExp (N.floor :: Zwirn Double -> Zwirn Int)+ <:: "Number -> Number"+ --| "round down",+ "round"+ === toExp (N.round :: Zwirn Double -> Zwirn Int)+ <:: "Number -> Number"+ --| "round to closest",+ "gcd"+ === toExp (N.gcd :: Zwirn Int -> Zwirn Int -> Zwirn Int)+ <:: "Number -> Number -> Number"+ --| "greatest common divisor",+ "lcm"+ === toExp (N.lcm :: Zwirn Int -> Zwirn Int -> Zwirn Int)+ <:: "Number -> Number -> Number"+ --| "least common multiple",+ "range"+ === toExp (range :: Zwirn Double -> Zwirn Double -> Zwirn Double -> Zwirn Double)+ <:: "Number -> Number -> Number -> Number"+ --| "range x y l maps number l linearly into interval (x,y), assuming l is between 0 and 1"+ ]++signals :: Map.Map Text AnnotatedExpression+signals =+ Map.unions+ [ "now"+ === toExp (now :: Zwirn Time)+ <:: "Number"+ --| "current time",+ "cyc"+ === toExp (C.cyc :: Zwirn Int)+ <:: "Number"+ --| "current cycle",+ "sine"+ === toExp (sine :: Zwirn Time)+ <:: "Number"+ --| "sine signal",+ "sine2"+ === toExp (sine2 :: Zwirn Time)+ <:: "Number"+ --| "bipolar sine signal",+ "saw"+ === toExp (saw :: Zwirn Time)+ <:: "Number"+ --| "saw signal",+ "saw2"+ === toExp (saw2 :: Zwirn Time)+ <:: "Number"+ --| "bipolar saw signal",+ "cosine"+ === toExp (cosine :: Zwirn Time)+ <:: "Number"+ --| "cosine signal",+ "cosine2"+ === toExp (cosine2 :: Zwirn Time)+ <:: "Number"+ --| "bipolar cosine signal",+ "isaw"+ === toExp (isaw :: Zwirn Time)+ <:: "Number"+ --| "inverse saw signal",+ "isaw2"+ === toExp (isaw2 :: Zwirn Time)+ <:: "Number"+ --| "bipolar inverse saw signal",+ "tri"+ === toExp (tri :: Zwirn Time)+ <:: "Number"+ --| "triangle signal",+ "tri2"+ === toExp (tri2 :: Zwirn Time)+ <:: "Number"+ --| "bipolar triangle signal",+ "square"+ === toExp (square :: Zwirn Time)+ <:: "Number"+ --| "square signal",+ "square2"+ === toExp (square2 :: Zwirn Time)+ <:: "Number"+ --| "bipolar square signal",+ "firstCyclesThen"+ === toExp (firstCyclesThen :: Zwirn Time -> Zwirn Expression -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a -> a"+ --| "bipolar square signal"+ ]++randomFunctions :: Map.Map Text AnnotatedExpression+randomFunctions =+ Map.unions+ [ "rand"+ === toExp (noise :: Zwirn Double)+ <:: "Number"+ --| "random stream of values between 0 and 1",+ "brand"+ === toExp (rand :: Zwirn Bool)+ <:: "Number"+ --| "random binary choice",+ "brandBy"+ === toExp (brandBy :: Zwirn Double -> Zwirn Bool)+ <:: "Number -> Number"+ --| "weighted random binary choice",+ "irand"+ === toExp (irand :: Zwirn Int -> Zwirn Int)+ <:: "Number -> Number"+ --| "random integer values between 0 and given input",+ "cycrand"+ === toExp (rand' :: Zwirn Double)+ <:: "Number"+ --| "random stream of values between 0 and 1, one value per cycle",+ "cycbrand"+ === toExp (rand :: Zwirn Bool)+ <:: "Number"+ --| "random binary cyclewise choice",+ "cycbrandBy"+ === toExp (brandBy' :: Zwirn Double -> Zwirn Bool)+ <:: "Number -> Number"+ --| "weighted random binary cyclewise choice",+ "cycirand"+ === toExp (irand' :: Zwirn Int -> Zwirn Int)+ <:: "Number -> Number"+ --| "random integer values between 0 and given input, one value per cycle",+ "perlin"+ === toExp (perlin :: Zwirn Double)+ <:: "Number"+ --| "perlin noise between 0 and 1",+ "simplex"+ === toExp (simplex :: Zwirn Double)+ <:: "Number"+ --| "simplex noise between 0 and 1",+ "ssimplex"+ === toExp (ssimplex :: Zwirn Double)+ <:: "Number"+ --| "super simplex noise between 0 and 1",+ "value"+ === toExp (valueN :: Zwirn Double)+ <:: "Number"+ --| "value noise between 0 and 1",+ "cubic"+ === toExp (cubic :: Zwirn Double)+ <:: "Number"+ --| "cubic value noise between 0 and 1",+ "perlinS"+ === toExp (perlinSeed :: Zwirn Int -> Zwirn Double)+ <:: "Number -> Number"+ --| "seeded perlin noise between 0 and 1",+ "simplexS"+ === toExp (simplexSeed :: Zwirn Int -> Zwirn Double)+ <:: "Number -> Number"+ --| "seeded simplex noise between 0 and 1",+ "ssimplexS"+ === toExp (ssimplexSeed :: Zwirn Int -> Zwirn Double)+ <:: "Number -> Number"+ --| "seeded super simplex noise between 0 and 1",+ "valueS"+ === toExp (valueSeed :: Zwirn Int -> Zwirn Double)+ <:: "Number -> Number"+ --| "seeded value noise between 0 and 1",+ "cubicS"+ === toExp (cubicSeed :: Zwirn Int -> Zwirn Double)+ <:: "Number -> Number"+ --| "seeded cubic value noise between 0 and 1",+ "somecyclesBy"+ === toExp (somecyclesBy :: Zwirn Double -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> (a -> b) -> a -> b"+ --| "apply a function with a given chance per cycle",+ "somecycles"+ === toExp (somecycles :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "(a -> b) -> a -> b"+ --| "apply a functions with a 50% chance per cycle",+ "sometimesBy"+ === toExp (sometimesBy :: Zwirn Double -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> (a -> b) -> a -> b"+ --| "apply a function with a given chance",+ "sometimes"+ === toExp (sometimes :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "(a -> b) -> a -> b"+ --| "apply a function with a 50% chance",+ "often"+ === toExp (often :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "(a -> b) -> a -> b"+ --| "apply a function with a 75% chance",+ "rarely"+ === toExp (rarely :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "(a -> b) -> a -> b"+ --| "apply a function with a 25% chance",+ "almostNever"+ === toExp (almostNever :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "(a -> b) -> a -> b"+ --| "apply a function with a 10% chance",+ "almostAlways"+ === toExp (almostNever :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "(a -> b) -> a -> b"+ --| "apply a function with a 90% chance",+ "chooseFromTo"+ === toExp (enumFromToChoice 0 :: Zwirn Double -> Zwirn Double -> Zwirn Double)+ <:: "Number -> Number -> Number"+ --| "```chooseFromTo x y == [x | .. y]```",+ "chooseFromThenTo"+ === toExp (enumFromThenToChoice 0 :: Zwirn Double -> Zwirn Double -> Zwirn Double -> Zwirn Double)+ <:: "Number -> Number -> Number -> Number"+ --| "```chooseFromTo x y z == [x | y .. z]```"+ ]++timeFunctions :: Map.Map Text AnnotatedExpression+timeFunctions =+ Map.unions+ [ "(*)"+ === toExp (flip fast :: Zwirn Expression -> Zwirn Time -> Zwirn Expression)+ <:: "a -> Number -> a"+ --| "multiply time, making it faster",+ "fast"+ === toExp (fast :: Zwirn Time -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a"+ --| "multiply time, making it faster",+ "(/)"+ === toExp (flip slow :: Zwirn Expression -> Zwirn Time -> Zwirn Expression)+ <:: "a -> Number -> a"+ --| "divide time, making it slower",+ "slow"+ === toExp (slow :: Zwirn Time -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a"+ --| "divide time, making it slower",+ "(+)"+ === toExp (flip shift :: Zwirn Expression -> Zwirn Time -> Zwirn Expression)+ <:: "a -> Number -> a"+ --| "shift time to the right",+ "(-)"+ === toExp (flip (shift . fmap negate) :: Zwirn Expression -> Zwirn Time -> Zwirn Expression)+ <:: "a -> Number -> a"+ --| "shift time to the left",+ "shift"+ === toExp (shift :: Zwirn Time -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a"+ --| "shift time",+ "revBy"+ === toExp (revBy :: Zwirn Time -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a"+ --| "reverse time, piecewise",+ "rev"+ === toExp (rev :: Zwirn Expression -> Zwirn Expression)+ <:: "a -> a"+ --| "reverse time completely",+ "ply"+ === toExp (ply :: Zwirn Time -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a"+ --| "speed up time inside",+ "bump"+ === toExp (bump :: Zwirn Time -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a"+ --| "shift time inside",+ "timeloop"+ === toExp (timeloop :: Zwirn Time -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a"+ --| "loop time from 0 to the given number",+ "loop"+ === toExp (loop :: Zwirn Time -> Zwirn Time -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> Number -> a -> a"+ --| "loop a specifc range of a zwirn, speeding up",+ "ribbon"+ === toExp (ribbon :: Zwirn Time -> Zwirn Time -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> Number -> a -> a"+ --| "given an offset and a length, loops the according region",+ "zoom"+ === toExp (zoom :: Zwirn Time -> Zwirn Time -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> Number -> a -> a"+ --| "zoom and loop a part of a zwirn",+ "swingBy"+ === toExp (swingBy :: Zwirn Time -> Zwirn Time -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> Number -> a -> a"+ --| "given a swing amount and a number of subdivisions, shifts the second half of every subdivion by the given amount, creating a swing feel."+ ]++structureFunctions :: Map.Map Text AnnotatedExpression+structureFunctions =+ Map.unions+ [ "euclidOff"+ === toExp (euclidOff :: Zwirn Int -> Zwirn Int -> Zwirn Int -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> Number -> Number -> a -> a"+ --| "shifted euclidean rhythm",+ "euclid"+ === toExp (euclid :: Zwirn Int -> Zwirn Int -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> Number -> a -> a"+ --| "euclidean rhythm",+ "euclideanOff"+ === toExp (euclidean :: Zwirn Int -> Zwirn Int -> Zwirn Int -> Zwirn String)+ <:: "Number -> Number -> Number -> Text"+ --| "string representation of shifted euclidean rhythm",+ "euclidean"+ === toExp ((euclidean $ pure 0) :: Zwirn Int -> Zwirn Int -> Zwirn String)+ <:: "Number -> Number -> Number -> Text"+ --| "string representation of euclidean rhythm",+ "binary"+ === toExp (binary :: Zwirn Int -> Zwirn String)+ <:: "Number -> Text"+ --| "converts number to binary sequence",+ "christoffel"+ === toExp (christoffel :: Zwirn Int -> Zwirn Int -> Zwirn String)+ <:: "Number -> Number -> Text"+ --| "create the two alphabet christoffel word",+ "neg"+ === toExp (neg :: Zwirn String -> Zwirn String)+ <:: "Text -> Text"+ --| "make onsets to offsets in chunk notation",+ -- "rotate"+ -- === toExp (rotate :: Zwirn Int -> Zwirn String -> Zwirn String)+ -- <:: "Number -> Text -> Text"+ -- --| "rotate characters in a string by amount",+ "chunkWith"+ === toExp (chunkWith :: Zwirn Double -> Zwirn String -> Zwirn Int)+ <:: "Number -> Text -> Number"+ --| "converts from Boenn's chunk notation to a rhythm",+ "chunk"+ === toExp (chunk :: Zwirn String -> Zwirn Int)+ <:: "Text -> Number"+ --| "converts from Boenn's chunk notation to a rhythm",+ "chunked"+ === toExp (chunked :: Zwirn String -> Zwirn Expression -> Zwirn Expression)+ <:: "Text -> a -> a"+ --| "converts from Boenn's chunk notation to a rhythm",+ "segment"+ === toExp (segment :: Zwirn Int -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a"+ --| "divide structure into equal pieces",+ "sample"+ === toExp (sampleAndHold :: Zwirn Int -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a"+ --| "like segment, but the values are held ('sample and hold')",+ "struct"+ === toExp (struct :: Zwirn Expression -> Zwirn Expression -> Zwirn Expression)+ <:: "a -> b -> b"+ --| "copy the structure from first value",+ "run"+ === toExp (run :: Zwirn Int -> Zwirn Int)+ <:: "Number -> Number"+ --| "```run n == [0 .. n-1]```",+ "runFromTo"+ === toExp (runFromTo :: Zwirn Double -> Zwirn Double -> Zwirn Double)+ <:: "Number -> Number -> Number"+ --| "```runFromTo x y == [x .. y]```",+ "runFromThenTo"+ === toExp (runFromThenTo :: Zwirn Double -> Zwirn Double -> Zwirn Double -> Zwirn Double)+ <:: "Number -> Number -> Number -> Number"+ --| "```runFromTo x y z == [x y .. z]```",+ "slowrun"+ === toExp (slowrun :: Zwirn Int -> Zwirn Int)+ <:: "Number -> Number"+ --| "```run n == <0 .. n-1>```",+ "slowrunFromTo"+ === toExp (slowrunFromTo :: Zwirn Double -> Zwirn Double -> Zwirn Double)+ <:: "Number -> Number -> Number"+ --| "```slowrunFromTo x y == <x .. y>```",+ "slowrunFromThenTo"+ === toExp (slowrunFromThenTo :: Zwirn Double -> Zwirn Double -> Zwirn Double -> Zwirn Double)+ <:: "Number -> Number -> Number -> Number"+ --| "```slowrunFromTo x y z == <x y .. z>```"+ ]++conditionalFunctions :: Map.Map Text AnnotatedExpression+conditionalFunctions =+ Map.unions+ [ "(==)"+ === toExp (eq :: Zwirn Expression -> Zwirn Expression -> Zwirn Bool)+ <:: "Eq a => a -> a -> Number"+ --| "equality",+ "(>=)"+ === toExp (geq :: Zwirn Expression -> Zwirn Expression -> Zwirn Bool)+ <:: "Number -> Number -> Number"+ --| "greater or equal",+ "(<=)"+ === toExp (leq :: Zwirn Expression -> Zwirn Expression -> Zwirn Bool)+ <:: "Number -> Number -> Number"+ --| "less or equal",+ "(<)"+ === toExp (ge :: Zwirn Expression -> Zwirn Expression -> Zwirn Bool)+ <:: "Number -> Number -> Number"+ --| "less",+ "(>)"+ === toExp (le :: Zwirn Expression -> Zwirn Expression -> Zwirn Bool)+ <:: "Number -> Number -> Number"+ --| "greater",+ "not"+ === toExp (Z.not :: Zwirn Bool -> Zwirn Bool)+ <:: "Number -> Number"+ --| "logical not",+ "(&&)"+ === toExp (Z.and :: Zwirn Bool -> Zwirn Bool -> Zwirn Bool)+ <:: "Number -> Number -> Number"+ --| "logical and",+ "(||)"+ === toExp (Z.or :: Zwirn Bool -> Zwirn Bool -> Zwirn Bool)+ <:: "Number -> Number -> Number"+ --| "logical or",+ "ifthen"+ === toExp (ifthen :: Zwirn Bool -> Zwirn Expression -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a -> a"+ --| "choose between two expressions based on a condition",+ "if"+ === toExp (iff :: Zwirn Bool -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a"+ --| "if condition is true produce the value, silence otherwise",+ "while"+ === toExp (while :: Zwirn Bool -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> (a -> a) -> a -> a"+ --| "apply function while condition is true",+ "everyFor"+ === toExp (everyFor :: Zwirn Time -> Zwirn Time -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> Number -> (a -> a) -> a -> a"+ --| "apply function periodically for a given amount of time",+ "every"+ === toExp (every :: Zwirn Time -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> (a -> a) -> a -> a"+ --| "apply function periodically for one cycle",+ "everyBeat"+ === toExp (everyBeat :: Zwirn Time -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> (a -> a) -> a -> a"+ --| "apply function every nth beat",+ "everyBeatShift"+ === toExp (everyBeatShift :: Zwirn Time -> Zwirn Time -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> Number -> (a -> a) -> a -> a"+ --| "apply function every nth beat, shifted in time"+ ]++cordFunctions :: Map.Map Text AnnotatedExpression+cordFunctions =+ Map.unions+ [ "pick"+ === toExp (pick :: Zwirn Int -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a"+ --| "pick a certain layer of a cord using an index statring from 0. wraps around if the depth is exceeded",+ "inhabit"+ === toExp (inhabit :: Zwirn Int -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a"+ --| "",+ "select"+ === toExp (select :: Zwirn Double -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a"+ --| "select a certain layer of a cord using a number between 0 and 1",+ "insert"+ === toExp (C.insert :: Zwirn Int -> Zwirn Expression -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a -> a"+ --| "insert into a specific layer of a cord",+ "push"+ === toExp (C.push :: Zwirn Expression -> Zwirn Expression -> Zwirn Expression)+ <:: "a -> a -> a"+ --| "push on top of a cord",+ "(&)"+ === toExp (C.concat :: Zwirn Expression -> Zwirn Expression -> Zwirn Expression)+ <:: "a -> a -> a"+ --| "concat two cords",+ "concat"+ === toExp (C.concat :: Zwirn Expression -> Zwirn Expression -> Zwirn Expression)+ <:: "a -> a -> a"+ --| "concat two cords",+ "pop"+ === toExp (C.pop :: Zwirn Expression -> Zwirn Expression)+ <:: "a -> a"+ --| "remove the top value of a cord",+ "remove"+ === toExp (remove :: Zwirn Int -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a"+ --| "remove a specific layer of a cord",+ "filter"+ === toExp (C.filter :: Zwirn (Zwirn Expression -> Zwirn Bool) -> Zwirn Expression -> Zwirn Expression)+ <:: "(a -> Number) -> a -> a"+ --| "filter with a predicate",+ "take"+ === toExp (C.take :: Zwirn Int -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a"+ --| "take the first n items of a cord",+ "drop"+ === toExp (C.drop :: Zwirn Int -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a"+ --| "drop the first n items of a cord",+ "replicate"+ === toExp (C.replicate :: Zwirn Int -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a"+ --| "layer the same item n times",+ "superimpose"+ === toExp (superimpose :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "(a -> a) -> a -> a"+ --| "```superimpose f x = [(f x), x]```",+ "ghostWith"+ === toExp (ghostWith :: Zwirn Time -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> (a -> a) -> a -> a"+ --| "```ghostWith n f x = [(shift (n |* 1.5) $ f x), (shift (n |* 2.5) $ f x), x]```",+ "arp"+ === toExp (arp :: Zwirn Expression -> Zwirn Expression)+ <:: "a -> a"+ --| "arpeggiate",+ "reverse"+ === toExp (C.reverse :: Zwirn Expression -> Zwirn Expression)+ <:: "a -> a"+ --| "reverse order of cord",+ "invert"+ === toExp (C.invert :: Zwirn Expression -> Zwirn Expression)+ <:: "Number -> Number"+ --| "chord inversion",+ "rotate"+ === toExp (C.rotate :: Zwirn Int -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> a -> a"+ --| "cord rotation",+ "expand"+ === toExp (C.expand :: Zwirn Int -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> Number -> Number"+ --| "cord expansion",+ "open"+ === toExp (C.open :: Zwirn Expression -> Zwirn Expression)+ <:: "Number -> Number"+ --| "open cord",+ "cat"+ === toExp (cordcat :: Zwirn Expression -> Zwirn Expression)+ <:: "a -> a"+ --| "```cat [x, y, .. z] == [x y .. z]```",+ "timerun"+ === toExp (timerun :: Zwirn Time -> Zwirn Int)+ <:: "Number -> Number"+ --| "",+ "map"+ === toExp (cordmap :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "(a -> b) -> a -> b"+ --| "```map f [x, y, .. z] == [(f x), (f y), .. (f z)```]",+ "layer"+ === toExp (layer :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "(a -> b) -> a -> b"+ --| "```layer [f, g, .. h] x == [(f x), (g x), .. (h x)```]",+ "echoWith"+ === toExp (echoWith :: Zwirn Int -> Zwirn Time -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> Number -> (a -> b) -> a -> b"+ --| "",+ "followWith"+ === toExp (followWith :: Zwirn Int -> Zwirn Time -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> Number -> (a -> b) -> a -> b"+ --| "",+ "fold"+ === toExp (fold :: Zwirn (Zwirn Expression -> Zwirn (Zwirn Expression -> Zwirn Expression)) -> Zwirn Expression -> Zwirn Expression)+ <:: "(a -> a -> a) -> a -> a"+ --| "fold a function over a cord",+ "smooth"+ === toExp (interpol :: Zwirn Time -> Zwirn Time)+ <:: "Number -> Number"+ --| "linear interpolation of items in a cord",+ "depth"+ === toExp (depth :: Zwirn Expression -> Zwirn Int)+ <:: "a -> Number"+ --| "the depth of a cord",+ "at"+ === toExp (at :: Zwirn Int -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression)+ <:: "Number -> (a -> a) -> a -> a"+ --| "apply a function to a specific layer of a cord",+ "cordFromTo"+ === toExp (enumFromToStack :: Zwirn Double -> Zwirn Double -> Zwirn Double)+ <:: "Number -> Number -> Number"+ --| "```cordFromTo x y == [x, .. y]```",+ "cordFromThenTo"+ === toExp (enumFromThenToStack :: Zwirn Double -> Zwirn Double -> Zwirn Double -> Zwirn Double)+ <:: "Number -> Number -> Number -> Number"+ --| "```cordFromThenTo x y z == [x, y .. z]```"+ ]++mapFunctions :: Map.Map Text AnnotatedExpression+mapFunctions =+ Map.unions+ [ "pN"+ === toExp ((\t -> fmap toExp . singleton t) :: Zwirn Text -> Zwirn Double -> Zwirn Expression)+ <:: "Text -> Number -> Map"+ --| "number singleton with specific key",+ "pI"+ === toExp ((\t -> fmap toExp . singleton t) :: Zwirn Text -> Zwirn Int -> Zwirn Expression)+ <:: "Text -> Number -> Map"+ --| "integer singleton with specific key",+ "pT"+ === toExp ((\t -> fmap toExp . singleton t) :: Zwirn Text -> Zwirn Text -> Zwirn Expression)+ <:: "Text -> Text -> Map"+ --| "text singleton with specific key",+ "(#)"+ === toExp (union :: Zwirn ExpressionMap -> Zwirn ExpressionMap -> Zwirn ExpressionMap)+ <:: "Map -> Map -> Map"+ --| "union of two maps - structure from the left",+ "lookupN"+ === toExp (M.lookup :: Zwirn Text -> Zwirn ExpressionMap -> Zwirn Expression)+ <:: "Text -> Map -> Number"+ --| "retrieve number at given key or silence if key is missing or it's value not a number",+ "lookupT"+ === toExp (M.lookup :: Zwirn Text -> Zwirn ExpressionMap -> Zwirn Expression)+ <:: "Text -> Map -> Text"+ --| "retrieve text at given key or silence if key is missing or it's value not a text",+ "fix"+ === toExp (M.fix :: Zwirn Text -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn ExpressionMap -> Zwirn ExpressionMap)+ <:: "Text -> (a -> a) -> Map -> Map"+ --| "apply a function to a specific key",+ "loopAt"+ === toExp (loopAt :: Zwirn Time -> Zwirn ExpressionMap -> Zwirn ExpressionMap)+ <:: "Number -> Map -> Map"+ --| "",+ "slice"+ === toExp (slice :: Zwirn Int -> Zwirn Int -> Zwirn ExpressionMap -> Zwirn ExpressionMap)+ <:: "Number -> Number -> Map -> Map"+ --| "slice a sample into equal btis and index into them",+ "juxBy"+ === toExp (juxBy :: Zwirn Expression -> Zwirn (Zwirn ExpressionMap -> Zwirn ExpressionMap) -> Zwirn ExpressionMap -> Zwirn ExpressionMap)+ <:: "Number -> (Map -> Map) -> Map -> Map"+ --| "thanks yaxu",+ "jux"+ === toExp (jux :: Zwirn (Zwirn ExpressionMap -> Zwirn ExpressionMap) -> Zwirn ExpressionMap -> Zwirn ExpressionMap)+ <:: "(Map -> Map) -> Map -> Map"+ --| "thanks yaxu",+ "echo"+ === toExp (echo :: Zwirn Int -> Zwirn Time -> Zwirn Expression -> Zwirn ExpressionMap -> Zwirn ExpressionMap)+ <:: "Number -> Number -> Number -> Map -> Map"+ --| "",+ "chop"+ === toExp (chop :: Zwirn Int -> Zwirn ExpressionMap -> Zwirn ExpressionMap)+ <:: "Number -> Map -> Map"+ --| "",+ "striate"+ === toExp (striate :: Zwirn Int -> Zwirn ExpressionMap -> Zwirn ExpressionMap)+ <:: "Number -> Map -> Map"+ --| "",+ "striateBy"+ === toExp (striateBy :: Zwirn Int -> Zwirn Expression -> Zwirn ExpressionMap -> Zwirn ExpressionMap)+ <:: "Number -> Number -> Map -> Map"+ --| "",+ "param"+ === toExp paramName+ <:: "(a -> Map) -> Text"+ --| "given a parameter function, gives back the name of the parameter as text"+ ]++streamFunctions :: Stream -> Map.Map Text AnnotatedExpression+streamFunctions str =+ Map.unions+ [ "replace"+ === toExp (replace str)+ <:: "Id a => a -> Map -> Action"+ --| "replace the channel running with given id",+ "target"+ === toExp target+ <:: "Id a => Text -> a -> Map"+ --| "specify a specific target",+ "($:)"+ === toExp (replace str)+ <:: "Id a => a -> Map -> Action"+ --| "replace the channel running with given id",+ "replaceAction"+ === toExp (replaceAction str)+ <:: "Id a => a -> Action -> Action"+ --| "replace the channel running with given id",+ "($!)"+ === toExp (replaceAction str)+ <:: "Id a => a -> Action -> Action"+ --| "replace the channel running with given id",+ "replaceBus"+ === toExp (replaceBus str)+ <:: "Id a => a -> Number -> Action"+ --| "replace the bus running with given id",+ "(&:)"+ === toExp (replaceBus str)+ <:: "Id a => a -> Number -> Action"+ --| "replace the bus running with given id",+ "fx"+ === toExp (fx str)+ <:: "Id a => a -> (Map -> Map) -> Action"+ --| "apply the function to channel with given id",+ "(#!)"+ === toExp (fx str)+ <:: "Id a => a -> (Map -> Map) -> Action"+ --| "replace the bus running with given id",+ "all"+ === toExp allID+ <:: "Text"+ --| "special identifier to apply effects to all channels",+ "none"+ === toExp noneID+ <:: "Text"+ --| "special identifier to remove effects from all channels",+ "tempo"+ === toExp tempo+ <:: "Number"+ --| "current tempo in bpm",+ "bpc"+ === toExp bpc+ <:: "Number"+ --| "current beats per cycle",+ "hush"+ === toExp (hush str)+ <:: "Action"+ --| "hush all channels",+ "once"+ === toExp (once str)+ <:: "Map -> Action"+ --| "play one cycle of the given zwirn",+ "tonce"+ === toExp (tonce str)+ <:: "Text -> Map -> Action"+ --| "play one cycle of the given zwirn on the given target",+ "mute"+ === toExp (mute str)+ <:: "Id a => a -> Action"+ --| "mute channel with given id",+ "unmute"+ === toExp (unmute str)+ <:: "Id a => a -> Action"+ --| "unmute channel with given id",+ "toggle"+ === toExp (toggle str)+ <:: "Id a => a -> Action"+ --| "toggle channel with given id",+ "solo"+ === toExp (solo str)+ <:: "Id a => a -> Action"+ --| "solo channel with given id",+ "unsolo"+ === toExp (unsolo str)+ <:: "Id a => a -> Action"+ --| "unsolo channel with given id",+ "togglesolo"+ === toExp (togglesolo str)+ <:: "Id a => a -> Action"+ --| "toggle solo channel with given id",+ "bpm"+ === toExp (bpm str)+ <:: "Number -> Action"+ --| "set the current bpm (beats per minute)",+ "cps"+ === toExp (cps str)+ <:: "Number -> Action"+ --| "set the current cps (cycles per second)",+ "resetcycles"+ === toExp (resetcycles str)+ <:: "Action"+ --| "resets the cycle count to 0",+ "setcycle"+ === toExp (setcycle str)+ <:: "Number -> Action"+ --| "set the current cycle to specific point in time",+ "disablelink"+ === toExp (disablelink str)+ <:: "Action"+ --| "disable ableton link",+ "enablelink"+ === toExp (enablelink str)+ <:: "Action"+ --| "enable ableton link",+ "in"+ === toExp (execIn' str)+ <:: "Number -> Action -> Action"+ --| "start an action in a given amount of seconds",+ "inMod"+ === toExp (execMod str)+ <:: "Number -> Action -> Action"+ --| "start an action in a given amount of seconds",+ "transitionmap"+ === toExp (transition str)+ <:: "Id a => a -> (Number -> Map -> Map) -> Action"+ --| "",+ "transition"+ === toExp (transition' str)+ <:: "Id a => a -> Number -> b -> b -> (Map -> b -> Map) -> Action"+ --| ""+ ]
+ src/zwirn-lang/Zwirn/Language/Compiler.hs view
@@ -0,0 +1,526 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}+{-# OPTIONS_GHC -Wno-unused-top-binds #-}++module Zwirn.Language.Compiler where++{-+ Compiler.hs - implementation of a compiler-interpreter for zwirn+ Copyright (C) 2023, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Control.Concurrent (readMVar)+import Control.Exception (SomeException, try)+import Control.Monad+import Control.Monad.Except+import Control.Monad.State+import Data.Either (lefts, rights)+import Data.List (intercalate)+import qualified Data.List.NonEmpty as NE+import qualified Data.Map as Map+import Data.Text (Text, pack, unpack)+import qualified Data.Text as T+import Data.Text.IO (readFile)+import Data.Version (showVersion)+import Paths_zwirn (version)+import System.IO (hPutStrLn, stderr)+import Text.Read (readMaybe)+import Zwirn.Core.Types (silence)+import Zwirn.Language.Block+import Zwirn.Language.Builtin.Prelude (builtinEnvironmentWithStream, builtinNames)+import Zwirn.Language.Environment+import Zwirn.Language.Evaluate+import Zwirn.Language.Location+import Zwirn.Language.Macro+import Zwirn.Language.Parser+import Zwirn.Language.Pretty+import qualified Zwirn.Language.Rotate as R+import Zwirn.Language.Simple+import Zwirn.Language.Syntax+import Zwirn.Language.TypeCheck.Constraint+import Zwirn.Language.TypeCheck.Infer+import Zwirn.Language.TypeCheck.Types+import Zwirn.Stream.Target (Targeted (..))+import Zwirn.Stream.Types+import Zwirn.Stream.UI+import Prelude hiding (readFile)++newtype CIMessage+ = CIMessage Text+ deriving (Show, Eq)++data CurrentBlock+ = CurrentBlock Int Int+ deriving (Eq, Show)++data ConfigEnv+ = ConfigEnv+ { cConfigPath :: IO String,+ cResetConfig :: IO String+ }++data CiConfig = CiConfig+ { ciConfigOverwriteBuiltin :: Bool,+ ciConfigDynamicTypes :: Bool+ }++data Environment+ = Environment+ { tStream :: Stream,+ intEnv :: InterpreterEnv,+ confEnv :: Maybe ConfigEnv,+ ciConfig :: CiConfig,+ macroMap :: MacroMap+ }++data ErrorType+ = ParseErr String RealSrcLoc+ | TypeErr TypeError+ | RotErr R.RotationError+ | OtherErr String+ | ManyErr [ErrorType]++instance Show ErrorType where+ show (ParseErr s _) = s+ show (TypeErr err) = unpack $ render err+ show (RotErr err) = show err+ show (OtherErr err) = err+ show (ManyErr errs) = intercalate "\n" $ map show errs++data CIError+ = CIError+ { eError :: ErrorType,+ eEnv :: Environment+ }++data CompilerOutput+ = OutMessage Text+ | OutEdits [CodeEdit]+ | NoOutput+ deriving (Eq, Show)++instance Show CIError where+ show (CIError err _) = show err++type CI = StateT Environment (ExceptT CIError IO)++runCI :: Environment -> CI a -> IO (Either CIError a)+runCI env m = runExceptT $ evalStateT m env++debug :: (MonadIO m) => String -> m ()+debug msg = liftIO $ hPutStrLn stderr $ "[zwirnzi] " <> msg++compilerInterpreterBasic :: Text -> CI CompilerOutput+compilerInterpreterBasic input = do+ sy <- runParser input+ runSyntax True sy++compilerInterpreterBlock :: Int -> Text -> CI (CompilerOutput, Environment)+compilerInterpreterBlock line input = do+ blocks <- runBlocks 0 input+ b <- runGetBlock line blocks+ sys <- parseBlock b+ r <- mapM (runSyntax True) sys+ e <- get+ return (last r, e)++compilerInterpreterLine :: Int -> Text -> CI (CompilerOutput, Environment)+compilerInterpreterLine line input = do+ blocks <- runBlocks 0 input+ content <- runGetLine line blocks+ sy <- runParserWithPos line "" content+ r <- runSyntax True sy+ e <- get+ return (r, e)++compilerInterpreterWhole :: Text -> CI (CompilerOutput, Environment)+compilerInterpreterWhole input = do+ blocks <- runBlocks 0 input+ syss <- mapM parseBlock blocks+ rs <- mapM (runSyntax True) $ concat syss+ e <- get+ return (last rs, e)++compilerInterpreterBoot :: [Text] -> CI Environment+compilerInterpreterBoot ps = mapM_ (runSyntax False . Command . noLoc . LoadCommand) ps >> get++-----------------------------------------------------+----------------- Throwing Errors -------------------+-----------------------------------------------------++throw :: ErrorType -> CI a+throw err = do+ env <- get+ throwError $ CIError err env++-- | catches an error by wrapping the result back into Either+catch :: CI a -> CI (Either CIError a)+catch c = catchError (fmap Right c) (return . Left)++-- | sequences the given actions, accumulating any occuring errors and wrapping them into ManyErr+catchMany :: [CI a] -> CI [a]+catchMany cs = do+ es <- mapM catch cs+ let errs = lefts es+ vs = rights es+ case errs of+ [] -> return ()+ [e] -> throw $ eError e+ xs -> throw (ManyErr $ map eError xs)+ return vs++filterErrors :: [CI a] -> CI [a]+filterErrors cs = do+ es <- mapM catch cs+ return $ rights es++-----------------------------------------------------+---------------------- Parser -----------------------+-----------------------------------------------------++extractParseErrPos :: String -> ErrorType+extractParseErrPos s = case mpos of+ Just p -> ParseErr s p+ Nothing -> OtherErr s+ where+ (ls, rs) = break (== ',') $ drop 20 s+ (c1s, _) = break (== '\n') $ drop 9 rs+ mpos = do+ l <- readMaybe ls+ c1 <- readMaybe c1s+ return (RealSrcLoc "" l c1 l c1)++runParserWithPos :: Int -> Text -> Text -> CI Syntax+runParserWithPos ln srcp t = case parseSyntaxWithPos ln srcp t of+ Left err -> throw $ extractParseErrPos err+ Right s -> return s++runParser :: Text -> CI Syntax+runParser t = case parseSyntax t of+ Left err -> throw $ extractParseErrPos err+ Right s -> return s++runBlocks :: Int -> Text -> CI [Block]+runBlocks ln t = case parseBlocks ln t of+ Left err -> throw $ OtherErr err+ Right bs -> return bs++runGetBlock :: Int -> [Block] -> CI Block+runGetBlock i bs = case getBlock i bs of+ Left err -> throw $ OtherErr err+ Right b -> return b++runGetLine :: Int -> [Block] -> CI Text+runGetLine i bs = case getSingleLine i bs of+ Left err -> throw $ OtherErr err+ Right (Line _ _ c) -> return c++parseBlock :: Block -> CI [Syntax]+parseBlock (Block ls) = catchMany $ map (\l -> runParserWithPos (lStart l) "" (lContent l)) (NE.toList ls)++getSyntaxLine :: Int -> Text -> CI (Maybe Syntax)+getSyntaxLine ln input = do+ blocks <- runBlocks 0 input+ case getSingleLine ln blocks of+ Right line -> Just <$> runParserWithPos (lStart line) "" (lContent line)+ Left _ -> return Nothing++getSyntaxBlock :: Int -> Text -> CI [Syntax]+getSyntaxBlock ln input = do+ blocks <- runBlocks 0 input+ block <- runGetBlock ln blocks+ parseBlock block++-----------------------------------------------------+---------------------- Macro ------------------------+-----------------------------------------------------++macroCI :: LocTerm -> CI (LocTerm, [CodeEdit])+macroCI t = do+ mmap <- gets macroMap+ case runMacros t mmap of+ Just x -> return x+ _ -> throw $ OtherErr "Unknown macro!"++-----------------------------------------------------+---------------------- Desugar ----------------------+-----------------------------------------------------++runSimplify :: LocTerm -> CI LocSimpleTerm+runSimplify t = return $ simplifyLoc t++runSimplifyDef :: Located Definition -> CI (Located SimpleDef, [CodeEdit])+runSimplifyDef (Located p (Definition x vs t)) = do+ (t', es) <- macroCI t+ return (Located p (LetS x (noLoc $ simplify $ TLambda vs t')), es)++-----------------------------------------------------+------------------- AST Rotation --------------------+-----------------------------------------------------++runRotate :: LocSimpleTerm -> CI LocSimpleTerm+runRotate s = case R.runRotate s of+ Left err -> throw $ RotErr err+ Right t -> return t++-----------------------------------------------------+-------------------- Type Check ---------------------+-----------------------------------------------------++runTypeCheck :: LocSimpleTerm -> CI Scheme+runTypeCheck s = do+ Environment {intEnv = env} <- get+ case inferTerm env s of+ Left err -> throw (TypeErr err)+ Right t -> return t++-----------------------------------------------------+-------------------- Interpreter --------------------+-----------------------------------------------------++interpret :: LocSimpleTerm -> CI Expression+interpret input = do+ env <- gets intEnv+ return $ evaluate env input++-- if ctx is false, highlighting should be disabled+checkHighlight :: Bool -> Expression -> CI Expression+checkHighlight True x = return x+checkHighlight False x = return $ removePosExp x++-----------------------------------------------------+----------------- Checking Options -----------------+-----------------------------------------------------++overwriteOk :: Text -> CI ()+overwriteOk name = do+ overwrite <- gets (ciConfigOverwriteBuiltin . ciConfig)+ when (not overwrite && name `elem` builtinNames) $ throw $ OtherErr "Cannot overwrite builtin function. Please use OverwriteBuiltin."++dynamicOk :: Text -> Scheme -> CI ()+dynamicOk name ty = do+ dynamic <- gets (ciConfigDynamicTypes . ciConfig)+ mayty <- gets (lookupType name . intEnv)+ case mayty of+ Just oldType ->+ when (not dynamic && not (unifiable (schemeToType oldType, schemeToType ty))) $ throw $ OtherErr "Cannot overwrite definition with new type. Please use DynamicTypes."+ Nothing -> return ()++-------------------------------------------------------+----------------- Interpreting Syntax -----------------+-------------------------------------------------------++runSyntax :: Bool -> Syntax -> CI CompilerOutput+runSyntax b (Exec t) = executeTerm b t+runSyntax _ (Command c) = runCommand (lValue c)+runSyntax b (Def d) = define b d+runSyntax b (DynDef d) = dynamicDefine b (lValue d)+runSyntax _ (MacroDef d) = macroDefine (lValue d)++executeTerm :: Bool -> LocTerm -> CI CompilerOutput+executeTerm ctx t = do+ (t', es) <- macroCI t+ s <- runSimplify t'+ rot <- runRotate s+ ty <- runTypeCheck rot+ ex <- interpret rot+ exCtx <- checkHighlight ctx ex+ case ty of+ Forall _ (Qual _ _ (TypeCon "Action")) -> do+ str <- gets tStream+ liftIO $ evalAction str (fromExp exCtx)+ return $ OutEdits es+ _ -> throw $ OtherErr "Can only execute actions!"++define :: Bool -> Located Definition -> CI CompilerOutput+define ctx d = do+ (Located _ (LetS x st), es) <- runSimplifyDef d+ rot <- runRotate st+ ty <- runTypeCheck rot+ ex <- interpret rot+ exCtx <- checkHighlight ctx ex+ overwriteOk x+ dynamicOk x ty+ modify (\env -> env {intEnv = extend (x, exCtx, ty) (intEnv env)})+ return $ OutEdits es++dynamicDefine :: Bool -> DynamicDefinition -> CI CompilerOutput+dynamicDefine ctx (DynamicDefinition x t) = do+ (t', es) <- macroCI t+ s <- runSimplify t'+ rot <- runRotate s+ ty <- runTypeCheck rot+ ex <- interpret rot+ exCtx <- checkHighlight ctx ex+ overwriteOk x+ dynamicOk x ty+ setExpression x ty exCtx+ return $ OutEdits es++macroDefine :: MacroDefinition -> CI CompilerOutput+macroDefine (MacroDefinition x t) = do+ (t', es) <- macroCI t+ s <- runSimplify t'+ rot <- runRotate s+ _ <- runTypeCheck rot+ modify (\env -> env {macroMap = Map.insert x t' (macroMap env)})+ return $ OutEdits es++setExpression :: Text -> Scheme -> Expression -> CI ()+setExpression x ty exCtx+ | isBasicType ty = do+ if checkDependency x ty+ then do+ let newEx+ | isNumberT ty = EZwirn $ getStateN (pure x)+ | isTextT ty = EZwirn $ getStateT (pure x)+ | isMapT ty = EZwirn $ getStateM (pure x)+ | otherwise = EZwirn silence+ modify (\env -> env {intEnv = extend (x, newEx, addDependency x ty) (intEnv env)})+ str <- gets tStream+ liftIO $ streamSet str x exCtx+ else throw $ OtherErr "Cyclic dependency detected!"+ | otherwise = throw $ OtherErr "Can only set basic types!"++---------------------------------------------------------+----------------- Interpreting Commands -----------------+---------------------------------------------------------++runCommand :: Command -> CI CompilerOutput+runCommand (ShowCommand t) = showCommand t+runCommand (TypeCommand t) = typeCommand t+runCommand (SetCommand f) = setCommand (unpack f)+runCommand (UnsetCommand _) = return NoOutput+runCommand (LoadCommand f) = NoOutput <$ loadCommand f+runCommand (InfoCommand f) = infoCommand f+runCommand ResetEnvCommand = resetEnvCommand+runCommand ResetConfigCommand = resetConfigCommand+runCommand ShowConfigPathCommand = showConfigPathCommand+runCommand StatusCommand = statusCommand+runCommand EnvCommand = envCommand++showCommand :: LocTerm -> CI CompilerOutput+showCommand t = do+ (t', _) <- macroCI t+ s <- runSimplify t'+ rot <- runRotate s+ ty <- runTypeCheck rot+ if isBasicType ty+ then do+ ex <- interpret rot+ stmv <- gets (sState . tStream)+ prec <- gets (streamConfigPrecision . sConfig . tStream)+ st <- liftIO $ readMVar stmv+ return $ OutMessage $ pack $ showWithStatePrec (realToFrac prec) st ex+ else throw $ OtherErr $ "Can not show expressions of type: " ++ unpack (ppscheme ty)++typeCommand :: LocTerm -> CI CompilerOutput+typeCommand t = do+ (t', _) <- macroCI t+ s <- runSimplify t'+ rot <- runRotate s+ ty <- runTypeCheck rot+ return $ OutMessage $ ppTermHasType (t, ty)++loadCommand :: Text -> CI ()+loadCommand path = do+ mayfile <- liftIO ((try $ readFile $ unpack path) :: IO (Either SomeException Text))+ case mayfile of+ Left _ -> throw $ OtherErr "File not found"+ Right input -> do+ blocks <- runBlocks 0 input+ let content = concatMap getBlockContent blocks+ ss <- mapM runParser content+ mapM_ (runSyntax False) ss++infoCommand :: Text -> CI CompilerOutput+infoCommand n = do+ env <- gets intEnv+ case lookupFull n env of+ Just (Annotated _ t (Just d)) -> return $ OutMessage $ n <> " :: " <> ppscheme t <> " \n" <> d+ Just (Annotated _ t Nothing) -> return $ OutMessage $ n <> " :: " <> ppscheme t+ Nothing -> return $ OutMessage $ pack $ "Couldn't find information about " ++ unpack n++resetConfigCommand :: CI CompilerOutput+resetConfigCommand = do+ (Environment {confEnv = mayEnv}) <- get+ case mayEnv of+ Nothing -> throw $ OtherErr "Configuration not available."+ Just (ConfigEnv _ reset) -> OutMessage . pack <$> liftIO reset++showConfigPathCommand :: CI CompilerOutput+showConfigPathCommand = do+ (Environment {confEnv = mayEnv}) <- get+ case mayEnv of+ Nothing -> throw $ OtherErr "Configuration not available."+ Just (ConfigEnv path _) -> OutMessage . pack <$> liftIO path++resetEnvCommand :: CI CompilerOutput+resetEnvCommand = do+ str <- gets tStream+ modify (\env -> env {intEnv = builtinEnvironmentWithStream str})+ return $ OutMessage "Environment reset to default!"++setCommand :: String -> CI CompilerOutput+setCommand "DynamicTypes" = modify (\env -> env {ciConfig = (ciConfig env) {ciConfigDynamicTypes = True}}) >> return (OutMessage "Successfully enabled DynamicTypes.")+setCommand "OverwriteBuiltin" = modify (\env -> env {ciConfig = (ciConfig env) {ciConfigOverwriteBuiltin = True}}) >> return (OutMessage "Successfully enabled OverwriteBuiltin.")+setCommand _ = return $ OutMessage "Unknown compiler flag. The flags are: DynamicTypes, OverwriteBuiltin."++-- for now only prints out the "basic" non-builtin expressions+-- TODO: add a flag/modifier to print all expressions (via :env all)+envCommand :: CI CompilerOutput+envCommand = do+ env <- gets (Map.toList . Map.filter isBasicExpression . eExpressions . intEnv)+ str <- gets tStream+ let builtin = Map.keys $ Map.filter isBasicExpression $ eExpressions $ builtinEnvironmentWithStream str+ filtered = filter (\(k, _) -> k `notElem` builtin) env+ return $ OutMessage $ T.intercalate "\n" $ map (\(k, Annotated _ ty _) -> k <> " :: " <> ppscheme ty) filtered++statusCommand :: CI CompilerOutput+statusCommand = do+ env <- get+ b <- liftIO (streamGetBPM (tStream env))+ pm <- liftIO $ readMVar (sPlayMap $ tStream env)+ return $ OutMessage $ renderStatus b pm++renderStatus :: Double -> PlayMap -> Text+renderStatus b pm = "zwirn " <> pack (showVersion version) <> "\ntempo: " <> pack (show b) <> "bpm\n" <> if null pm then "" else "active: " <> T.intercalate " | " ps+ where+ ps = map (\(key, Targeted _ (st, _, _)) -> ppID key <> renderState sol st) $ Map.toList pm+ sol = noSolo pm+ renderState True Normal = ""+ renderState False Normal = " (not soloed)"+ renderState _ Solo = " (solo)"+ renderState _ Mute = " (muted)"++-- | true if no pattern has a solo status+noSolo :: PlayMap -> Bool+noSolo pm = all (\(_, Targeted _ (ps, _, _)) -> ps /= Solo) $ Map.toList pm++isNumberT :: Scheme -> Bool+isNumberT (Forall _ (Qual _ _ (TypeCon "Number"))) = True+isNumberT _ = False++isTextT :: Scheme -> Bool+isTextT (Forall _ (Qual _ _ (TypeCon "Text"))) = True+isTextT _ = False++isMapT :: Scheme -> Bool+isMapT (Forall _ (Qual _ _ (TypeCon "Map"))) = True+isMapT _ = False
+ src/zwirn-lang/Zwirn/Language/Environment.hs view
@@ -0,0 +1,47 @@+module Zwirn.Language.Environment where++import qualified Data.Map as Map+import Data.Text (Text)+import Zwirn.Core.Types (silence)+import Zwirn.Language.Evaluate.Expression+import Zwirn.Language.TypeCheck.Types++data AnnotatedExpression+ = Annotated+ { aExp :: Expression,+ aType :: Scheme,+ aDesc :: Maybe Text+ }++data InterpreterEnv = IEnv+ { eExpressions :: Map.Map Text AnnotatedExpression,+ eInstances :: [Instance]+ }++withExpressions :: (Map.Map Text AnnotatedExpression -> Map.Map Text AnnotatedExpression) -> InterpreterEnv -> InterpreterEnv+withExpressions f (IEnv l i) = IEnv (f l) i++extend :: (Text, Expression, Scheme) -> InterpreterEnv -> InterpreterEnv+extend (n, x, s) = withExpressions (Map.insert n (Annotated x s Nothing))++lookupType :: Text -> InterpreterEnv -> Maybe Scheme+lookupType k (IEnv l _) = aType <$> Map.lookup k l++insertType :: Text -> Scheme -> InterpreterEnv -> InterpreterEnv+insertType t s = withExpressions (Map.alter alt t)+ where+ dummy = EZwirn silence+ alt Nothing = Just $ Annotated dummy s Nothing+ alt (Just (Annotated x _ i)) = Just $ Annotated x s i++lookupDescription :: Text -> InterpreterEnv -> Maybe Text+lookupDescription k (IEnv l _) = aDesc =<< Map.lookup k l++lookupExp :: Text -> InterpreterEnv -> Maybe Expression+lookupExp k (IEnv l _) = aExp <$> Map.lookup k l++lookupFull :: Text -> InterpreterEnv -> Maybe AnnotatedExpression+lookupFull k (IEnv l _) = Map.lookup k l++isBasicExpression :: AnnotatedExpression -> Bool+isBasicExpression (Annotated _ s _) = isBasicType s
+ src/zwirn-lang/Zwirn/Language/Evaluate.hs view
@@ -0,0 +1,12 @@+module Zwirn.Language.Evaluate+ ( module Zwirn.Language.Evaluate.Expression,+ module Zwirn.Language.Evaluate.Internal,+ module Zwirn.Language.Evaluate.Convert,+ module Zwirn.Language.Evaluate.SKI,+ )+where++import Zwirn.Language.Evaluate.Convert+import Zwirn.Language.Evaluate.Expression+import Zwirn.Language.Evaluate.Internal+import Zwirn.Language.Evaluate.SKI
+ src/zwirn-lang/Zwirn/Language/Evaluate/Convert.hs view
@@ -0,0 +1,193 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}+{-# OPTIONS_GHC -Wno-orphans #-}+{-# OPTIONS_GHC -Wno-unused-top-binds #-}++module Zwirn.Language.Evaluate.Convert where++{-+ Convert.hs - convert from and to Expressions+ Copyright (C) 2023, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import qualified Data.Map as Map+import Data.String (IsString, fromString)+import Data.Text (Text, pack, unpack)+import Zwirn.Core.Time (Time (..))+import Zwirn.Core.Types+import Zwirn.Language.Evaluate.Expression++fromZwirn :: Zwirn Expression -> Expression+fromZwirn = EZwirn++toZwirn :: Expression -> Zwirn Expression+toZwirn (EZwirn x) = x+toZwirn _ = silence++class FromExpression a where+ fromExp :: Expression -> Zwirn a++class ToExpression a where+ toExp :: a -> Expression++instance FromExpression Time where+ fromExp (EZwirn tz) = fmap (\(ENum t) -> Time (toRational t) 0) tz+ fromExp _ = silence++instance FromExpression Double where+ fromExp (EZwirn tz) = fmap (\(ENum t) -> t) tz+ fromExp _ = silence++instance FromExpression Int where+ fromExp (EZwirn tz) = fmap (\(ENum t) -> floor t) tz+ fromExp _ = silence++instance FromExpression Expression where+ fromExp (EZwirn z) = z+ fromExp _ = silence++instance FromExpression Text where+ fromExp (EZwirn z) = fmap (\(EText t) -> t) z+ fromExp _ = silence++instance FromExpression String where+ fromExp (EZwirn z) = fmap (\(EText t) -> unpack t) z+ fromExp _ = silence++instance FromExpression Bool where+ fromExp (EZwirn z) = fmap (\(ENum x) -> x >= 1) z+ fromExp _ = silence++instance FromExpression (IO ()) where+ fromExp (EZwirn z) = fmap (\(EAction x) -> x) z+ fromExp _ = silence++instance FromExpression ExpressionMap where+ fromExp (EZwirn z) = fmap (\(EMap m) -> m) z+ fromExp _ = silence++instance (ToExpression a, FromExpression b) => FromExpression (Zwirn a -> Zwirn b) where+ fromExp (EZwirn z) = fmap (\(ELam f) -> fromExp . f . toExp) z+ fromExp _ = silence++instance (FromExpression a) => FromExpression (Zwirn a) where+ fromExp (EZwirn z) = fmap fromExp z+ fromExp _ = silence++instance ToExpression Expression where+ toExp = id++instance ToExpression Double where+ toExp = ENum++instance ToExpression String where+ toExp = EText . pack++instance ToExpression (IO ()) where+ toExp = EAction++instance ToExpression Time where+ toExp (Time t _) = ENum $ fromRational t++instance ToExpression Int where+ toExp i = ENum $ fromIntegral i++instance ToExpression Bool where+ toExp True = ENum 1+ toExp False = ENum 0++instance ToExpression Text where+ toExp = EText++instance (ToExpression a) => ToExpression (Map.Map Text a) where+ toExp m = EMap $ toExp <$> m++instance (ToExpression a) => ToExpression (Zwirn a) where+ toExp a = EZwirn $ fmap toExp a++instance (FromExpression a, ToExpression b) => ToExpression (Zwirn a -> b) where+ toExp f = lambda $ \x -> toExp $ f (fromExp x)++instance Num Expression where+ (+) = pervasive2 ((+) @Double)+ (*) = pervasive2 ((*) @Double)+ abs = pervasive (abs @Double)+ signum = pervasive (signum @Double)+ fromInteger i = ENum $ fromInteger i+ negate = pervasive (negate @Double)++instance Fractional Expression where+ fromRational r = ENum $ fromRational r+ (/) = pervasive2 ((/) @Double)++instance Floating Expression where+ pi = EZwirn $ pure $ ENum pi+ exp = pervasive (exp :: Double -> Double)+ log = pervasive (log :: Double -> Double)+ sin = pervasive (sin :: Double -> Double)+ cos = pervasive (cos :: Double -> Double)+ asin = pervasive (asin :: Double -> Double)+ acos = pervasive (acos :: Double -> Double)+ atan = pervasive (atan :: Double -> Double)+ sinh = pervasive (sinh :: Double -> Double)+ cosh = pervasive (cosh :: Double -> Double)+ asinh = pervasive (asinh :: Double -> Double)+ acosh = pervasive (acosh :: Double -> Double)+ atanh = pervasive (atanh :: Double -> Double)++instance IsString Expression where+ fromString = EText . pack++class Pervasive a where+ pervasive :: (a -> a) -> Expression -> Expression+ pervasive2 :: (a -> a -> a) -> Expression -> Expression -> Expression++instance Pervasive Double where+ pervasive f (ENum d) = ENum $ f d+ pervasive f (EMap m) = EMap $ fmap (pervasive f) m+ pervasive _ e = e+ pervasive2 f (ENum d) (ENum e) = ENum $ f d e+ pervasive2 f (EMap m) (EMap n) = EMap $ Map.unionWith (pervasive2 f) m n+ pervasive2 _ e _ = e++instance Pervasive Bool where+ pervasive f (ENum d) = toExp $ f (d >= 1)+ pervasive f (EMap m) = EMap $ fmap (pervasive f) m+ pervasive _ e = e+ pervasive2 f (ENum d) (ENum e) = toExp $ f (d >= 1) (e >= 1)+ pervasive2 f (EMap m) (EMap n) = EMap $ Map.unionWith (pervasive2 f) m n+ pervasive2 _ e _ = e++instance Pervasive Text where+ pervasive f (EText d) = EText $ f d+ pervasive f (EMap m) = EMap $ fmap (pervasive f) m+ pervasive _ e = e+ pervasive2 f (EText d) (EText e) = EText $ f d e+ pervasive2 f (EMap m) (EMap n) = EMap $ Map.unionWith (pervasive2 f) m n+ pervasive2 _ e _ = e++instance Pervasive (Either Double Text) where+ pervasive f (EText d) = EText $ (\(Right t) -> t) $ f (Right d)+ pervasive f (ENum d) = ENum $ (\(Left t) -> t) $ f (Left d)+ pervasive f (EMap m) = EMap $ fmap (pervasive f) m+ pervasive _ e = e+ pervasive2 f (EText d) (EText e) = EText $ (\(Right t) -> t) $ f (Right d) (Right e)+ pervasive2 f (ENum d) (ENum e) = ENum $ (\(Left t) -> t) $ f (Left d) (Left e)+ pervasive2 f (EMap m) (EMap n) = EMap $ Map.unionWith (pervasive2 f) m n+ pervasive2 _ e _ = e
+ src/zwirn-lang/Zwirn/Language/Evaluate/Expression.hs view
@@ -0,0 +1,81 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}+{-# OPTIONS_GHC -Wno-unused-top-binds #-}++module Zwirn.Language.Evaluate.Expression where++{-+ Expression.hs - Abstract Expressions+ Copyright (C) 2023, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Data.List (intercalate)+import qualified Data.Map as Map+import Data.Text (Text, unpack)+import Zwirn.Core.Cord+import Zwirn.Core.Query+import Zwirn.Core.Time (Time (..))+import Zwirn.Language.Location (SrcLoc)+import Zwirn.Language.Syntax++type ExpressionMap = Map.Map Text Expression++type Zwirn = Cord ExpressionMap SrcLoc++data Expression+ = EVar LocVar+ | EApp Expression Expression+ | ELam (Expression -> Expression)+ | ENum !Double+ | EText !Text+ | EMap ExpressionMap+ | EAction (IO ())+ | ESeq [Expression]+ | EStack [Expression]+ | EChoice Int [Expression]+ | EZwirn (Zwirn Expression)++showWithStatePrec :: Time -> ExpressionMap -> Expression -> String+showWithStatePrec prec st (EZwirn x) = intercalate "\n" $ (\(t, y) -> show t ++ ":" ++ showWithStatePrec prec st y) <$> findAllValuesWithTimePrec prec (Time 0 1, Time 1 1) st x+showWithStatePrec _ _ (ENum x) = show $ (fromIntegral (floor (x * 10 ^ (5 :: Int)) :: Int) :: Double) / 10 ^ (5 :: Int)+showWithStatePrec _ _ (EText x) = unpack x+showWithStatePrec _ _ (EAction _) = "action"+showWithStatePrec prec st (EMap m) = show $ Map.toList $ showWithStatePrec prec st <$> m+showWithStatePrec _ _ (EVar x) = show x+showWithStatePrec _ _ (EApp x y) = "(" ++ show x ++ " " ++ show y ++ ")"+showWithStatePrec _ _ (ESeq xs) = "[" ++ unwords (map show xs) ++ "]"+showWithStatePrec _ _ (EStack xs) = "[" ++ intercalate "," (map show xs) ++ "]"+showWithStatePrec _ _ (EChoice _ xs) = "[" ++ intercalate "|" (map show xs) ++ "]"+showWithStatePrec _ _ _ = "can't show"++instance Show Expression where+ show = showWithStatePrec 0.005 Map.empty++instance Eq Expression where+ (==) (ENum n) (ENum m) = n == m+ (==) (EText n) (EText m) = n == m+ (==) (EMap n) (EMap m) = n == m+ (==) _ _ = False++instance Ord Expression where+ (<=) (ENum n) (ENum m) = n <= m+ (<=) _ _ = False++lambda :: (Expression -> Expression) -> Expression+lambda f = EZwirn $ pure $ ELam f
+ src/zwirn-lang/Zwirn/Language/Evaluate/Internal.hs view
@@ -0,0 +1,263 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}+{-# OPTIONS_GHC -Wno-orphans #-}+{-# OPTIONS_GHC -Wno-unused-imports #-}+{-# OPTIONS_GHC -Wno-unused-top-binds #-}++module Zwirn.Language.Evaluate.Internal where++{-+ Internal.hs - internal functions, specific to Expressions+ Copyright (C) 2023, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Control.Applicative (liftA2)+import Control.Concurrent (forkIO, threadDelay)+import Control.Monad (void)+import Data.Fixed (mod')+import Data.List (mapAccumL)+import qualified Data.Map as Map+import Data.Maybe (fromJust, fromMaybe)+import Data.Text (Text, pack)+import qualified Data.Text as T+import Sound.Tidal.Clock (getCPS, getCycleTime)+import Zwirn.Core.Cord+import Zwirn.Core.Core (withState)+import Zwirn.Core.Lib.Cord+import Zwirn.Core.Lib.Core (apply)+import Zwirn.Core.Lib.Map+import Zwirn.Core.Lib.State+import Zwirn.Core.Lib.Structure (segment)+import Zwirn.Core.Time (Time)+import Zwirn.Core.Tree (Tree)+import Zwirn.Core.Types+import Zwirn.Language.Evaluate.Convert+import Zwirn.Language.Evaluate.Expression+import Zwirn.Language.Location (SrcLoc)+import Zwirn.Language.Syntax+import Zwirn.Stream.Target (Targeted (..))+import Zwirn.Stream.Types (Identifier (..), Stream (..), StreamConfig (streamConfigClock))+import Zwirn.Stream.UI+import qualified Zwirn.Stream.UI as Stream++instance State Tree ExpressionMap SrcLoc where+ beatsPerCycle = (\(ENum x) -> x) <$> getStateNWith (pure $ T.pack "_beatsPerCycle") (pure 8)++insert :: (Text, Expression) -> ExpressionMap -> ExpressionMap+insert (k, x) = Map.insert k x++getStateN :: Zwirn Text -> Zwirn Expression+getStateN xc = innerJoin $ liftA2 (\k l -> fromLookup $ Map.lookup k l) xc (get (pure ()))+ where+ fromLookup (Just (EZwirn x)) = outerJoin $ fmap fromNum x+ fromLookup _ = silence+ fromNum (ENum n) = pure $ ENum n+ fromNum _ = silence++getStateNWith :: Zwirn Text -> Zwirn Expression -> Zwirn Expression+getStateNWith xc z = innerJoin $ liftA2 (\k l -> fromLookup $ Map.lookup k l) xc (get (pure ()))+ where+ fromLookup (Just (EZwirn x)) = outerJoin $ fmap fromNum x+ fromLookup _ = z+ fromNum (ENum n) = pure $ ENum n+ fromNum _ = silence++getStateT :: Zwirn Text -> Zwirn Expression+getStateT xc = innerJoin $ liftA2 (\k l -> fromLookup $ Map.lookup k l) xc (get (pure ()))+ where+ fromLookup (Just (EZwirn x)) = outerJoin $ fmap fromText x+ fromLookup _ = silence+ fromText (EText n) = pure $ EText n+ fromText _ = silence++getStateM :: Zwirn Text -> Zwirn Expression+getStateM xc = innerJoin $ liftA2 (\k l -> fromLookup $ Map.lookup k l) xc (get (pure ()))+ where+ fromLookup (Just (EZwirn x)) = outerJoin $ fmap fromMap x+ fromLookup _ = silence+ fromMap (EMap n) = pure $ EMap n+ fromMap _ = silence++modifyState :: Zwirn Text -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression+modifyState kz fz xz = (modifyState' <$> kz <*> fz) `apply` xz+ where+ modifyState' :: Text -> (Zwirn Expression -> Zwirn Expression) -> Zwirn Expression -> Zwirn Expression+ modifyState' key f = withState (Map.update (Just . toExp . f . fromExp) key)++setState :: Zwirn Text -> Zwirn Expression -> Zwirn Expression -> Zwirn Expression+setState t x = setMap t (pure $ EZwirn x)++getState :: Zwirn Expression -> Zwirn ExpressionMap+getState = get++bus :: Zwirn Double -> Zwirn Double+bus = segment (pure 128)++segbus :: Zwirn Int -> Zwirn Double -> Zwirn Double+segbus = segment++paramName :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Text+paramName f = headOrDef . Map.keys <$> (fromExp $ EZwirn $ apply f (pure (ENum 0)) :: Zwirn ExpressionMap)+ where+ headOrDef [] = ""+ headOrDef (x : _) = x++recvT :: Zwirn Text -> Zwirn Int -> Zwirn ExpressionMap+recvT t i = singleton t (fmap (toExp . (\x -> pack $ "c" ++ show x)) i)++recv :: Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn Int -> Zwirn ExpressionMap+recv f = recvT t+ where+ t = paramName f++------------------------------------+------------- stream ui ------------+------------------------------------++toID :: Expression -> Identifier+toID (ENum i) = NumID (floor i :: Int)+toID (EText t) = TextID t+toID (EMap m) = TextID $ pack $ show m+toID _ = error "Error in toID!"++toBusID :: Zwirn Expression -> Zwirn (Targeted Int)+toBusID ex = innerJoin $ mapper <$> ex+ where+ mapper (ENum i) = pure $ Targeted [] (floor i)+ mapper (EMap mex) = Targeted targs <$> idd+ where+ idd = maybe silence toIDD (Map.lookup "id" mex)+ targs = toTarget <$> Map.elems (Map.delete "id" mex)+ toIDD (ENum i) = pure $ floor i :: Zwirn Int+ toIDD _ = silence+ toTarget (EText t) = t+ toTarget _ = error "Error in toTargetedID!"+ mapper _ = silence++toTargetedID :: Expression -> Targeted Identifier+toTargetedID (EMap mex) = Targeted targs idd+ where+ idd = toID $ fromMaybe (EText "default") $ Map.lookup "id" mex+ targs = toTarget <$> Map.elems (Map.delete "id" mex)+ toTarget (EText t) = t+ toTarget _ = error "Error in toTargetedID!"+toTargetedID ex = Targeted [] (toID ex)++target :: Zwirn Text -> Zwirn Expression -> Zwirn Expression+target tz = liftA2 (\tm idd -> EMap $ Map.insert "id" idd tm) targs+ where+ targs = innerJoin $ foldl (liftA2 (\m t -> Map.insert t (EText t) m)) (pure Map.empty :: Zwirn ExpressionMap) <$> collect tz++tempo :: Zwirn Expression+tempo = getStateN (pure "tempo")++bpc :: Zwirn Expression+bpc = getStateN (pure "_beatsPerCycle")++allID :: Zwirn Text+allID = pure "_all"++noneID :: Zwirn Text+noneID = pure "_none"++replace :: Stream -> Zwirn Expression -> Zwirn ExpressionMap -> Zwirn (IO ())+replace str iz mz = (\f -> f $ EMap <$> mz) . streamReplace str . toTargetedID <$> iz++replaceAction :: Stream -> Zwirn Expression -> Zwirn Expression -> Zwirn (IO ())+replaceAction str iz mz = (\f -> f mz) . streamReplaceAction str . toID <$> iz++replaceBus :: Stream -> Zwirn Expression -> Zwirn Expression -> Zwirn (IO ())+replaceBus str iz mz = (\f -> f mz) . streamReplaceBus str <$> toBusID iz++hush :: Stream -> Zwirn (IO ())+hush str = pure $ streamHush str++mute :: Stream -> Zwirn Expression -> Zwirn (IO ())+mute str iz = streamMute str . toID <$> iz++once :: Stream -> Zwirn Expression -> Zwirn (IO ())+once str iz = pure (streamFirst str iz)++tonce :: Stream -> Zwirn Text -> Zwirn Expression -> Zwirn (IO ())+tonce str tz iz = flip (streamFirstTarget str) iz <$> tz++unmute :: Stream -> Zwirn Expression -> Zwirn (IO ())+unmute str iz = streamUnmute str . toID <$> iz++toggle :: Stream -> Zwirn Expression -> Zwirn (IO ())+toggle str iz = streamToggle str . toID <$> iz++solo :: Stream -> Zwirn Expression -> Zwirn (IO ())+solo str iz = streamSolo str . toID <$> iz++togglesolo :: Stream -> Zwirn Expression -> Zwirn (IO ())+togglesolo str iz = streamToggleSolo str . toID <$> iz++unsolo :: Stream -> Zwirn Expression -> Zwirn (IO ())+unsolo str iz = streamUnsolo str . toID <$> iz++fx :: Stream -> Zwirn Expression -> Zwirn (Zwirn Expression -> Zwirn Expression) -> Zwirn (IO ())+fx str key fxz = (\f -> f fxz) . streamSetFx str . toID <$> key++bpm :: Stream -> Zwirn Double -> Zwirn (IO ())+bpm str iz = streamSetBPM str . realToFrac <$> iz++cps :: Stream -> Zwirn Double -> Zwirn (IO ())+cps str iz = streamSetCPS str . realToFrac <$> iz++setcycle :: Stream -> Zwirn Double -> Zwirn (IO ())+setcycle str iz = streamSetCycle str . realToFrac <$> iz++resetcycles :: Stream -> Zwirn (IO ())+resetcycles str = pure $ streamResetCycles str++enablelink :: Stream -> Zwirn (IO ())+enablelink str = pure $ streamEnableLink str++disablelink :: Stream -> Zwirn (IO ())+disablelink str = pure $ streamDisableLink str++execIn :: Zwirn Double -> Zwirn (IO ()) -> Zwirn (IO ())+execIn dz acz = execInSecs_ <$> dz <*> acz+ where+ execInSecs_ :: Double -> IO () -> IO ()+ execInSecs_ d ac = void $ forkIO $ threadDelay (floor $ d * 1000000) >> ac++execIn' :: Stream -> Zwirn Double -> Zwirn (IO ()) -> Zwirn (IO ())+execIn' str dz acz = execInCycs_ <$> dz <*> acz+ where+ execInCycs_ :: Double -> IO () -> IO ()+ execInCycs_ d ac = do+ xcps <- getCPS (streamConfigClock $ sConfig str) (sClockRef str)+ void $ forkIO $ threadDelay (floor $ d * realToFrac xcps * 1000000) >> ac++execMod :: Stream -> Zwirn Double -> Zwirn (IO ()) -> Zwirn (IO ())+execMod str dz acz = execMod_ <$> dz <*> acz+ where+ execMod_ :: Double -> IO () -> IO ()+ execMod_ d ac = do+ xcps <- getCPS (streamConfigClock $ sConfig str) (sClockRef str)+ now <- getCycleTime (streamConfigClock $ sConfig str) (sClockRef str)+ let del = d - mod' (realToFrac now) d+ void $ forkIO $ threadDelay (floor $ del * realToFrac xcps * 1000000) >> ac++transition :: Stream -> Zwirn Expression -> Zwirn (Zwirn Double -> Zwirn (Zwirn Expression -> Zwirn Expression)) -> Zwirn (IO ())+transition str kz = liftA2 (Stream.transition str) (toID <$> kz)++transition' :: Stream -> Zwirn Expression -> Zwirn Time -> Zwirn Expression -> Zwirn Expression -> Zwirn (Zwirn Expression -> Zwirn (Zwirn Expression -> Zwirn Expression)) -> Zwirn (IO ())+transition' str kz dur def sig fun = (\k -> Stream.transition' str k dur def sig fun) . toID <$> kz
+ src/zwirn-lang/Zwirn/Language/Evaluate/SKI.hs view
@@ -0,0 +1,104 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}+{-# OPTIONS_GHC -Wno-unused-top-binds #-}++module Zwirn.Language.Evaluate.SKI+ ( evaluate,+ compile,+ (!),+ removePosExp,+ )+where++{-+ SKI.hs - evaluate epxressions via the SKI combinator calculus,+ code adapted from https://kseo.github.io/posts/2016-12-30-write-you-an-interpreter.html+ Copyright (C) 2023, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Data.Maybe (fromJust)+import Data.Text (unpack)+import Zwirn.Core.Cord+import Zwirn.Core.Core+import Zwirn.Core.Lib.Core+import Zwirn.Core.Lib.Modulate+import Zwirn.Core.Lib.Random (chooseWithSeed)+import Zwirn.Core.Types+import Zwirn.Language.Environment+import Zwirn.Language.Evaluate.Convert+import Zwirn.Language.Evaluate.Expression+import Zwirn.Language.Location+import Zwirn.Language.Simple+import Zwirn.Language.TypeCheck.Types++compile :: LocSimpleTerm -> Expression+compile (Located p (SVar n)) = EVar (Located p n)+compile (Located _ (SApp fun arg)) = EApp (compile fun) (compile arg)+compile (Located _ (SLambda x body)) = abstract x (compile body)+compile (Located p (SNum x)) = EZwirn $ addInfo p $ pure $ ENum $ read $ unpack x+compile (Located p (SText x)) = EZwirn $ addInfo p $ pure $ EText x+compile (Located _ (SSeq xs)) = ESeq $ map compile xs+compile (Located _ (SStack xs)) = EStack $ map compile xs+compile (Located _ (SChoice i xs)) = EChoice i $ map compile xs+compile (Located _ (SInfix s1 n s2)) = EApp (EApp (EVar n) (compile s1)) (compile s2)+compile (Located _ (SBracket s)) = compile s+compile (Located _ SRest) = EZwirn silence++abstract :: Name -> Expression -> Expression+abstract x (EVar n) | x == lValue n = combI+abstract x (EApp fun arg) = combS (abstract x fun) (abstract x arg)+abstract x (ESeq xs) = ESeq $ map (abstract x) xs+abstract x (EStack xs) = EStack $ map (abstract x) xs+abstract x (EChoice i xs) = EChoice i $ map (abstract x) xs+abstract _ k = combK k++combS :: Expression -> Expression -> Expression+combS f = EApp (EApp (EVar $ noLoc "scomb") f)++combK :: Expression -> Expression+combK = EApp (EVar $ noLoc "const")++combI :: Expression+combI = EVar (noLoc "id")++infixl 0 !++(!) :: Expression -> Expression -> Expression+(EZwirn fp) ! (EZwirn x) = EZwirn $ apply (fmap (\(ELam f) -> toZwirn . f . fromZwirn) fp) x+_ ! _ = error "Error in (!)"++link :: InterpreterEnv -> Expression -> Expression+link bs (EVar n) = addPosExp (lLoc n) $ fromJust (lookupExp (lValue n) bs)+-- link bs (EVar n) = fromJust (lookupExp n bs)+link bs (EApp f x) = link bs f ! link bs x+link bs (ESeq xs) = EZwirn $ fastcat $ map (toZwirn . link bs) xs+link bs (EStack xs) = EZwirn $ stack $ map (toZwirn . link bs) xs+link bs (EChoice i xs) = EZwirn $ chooseWithSeed i $ map (toZwirn . link bs) xs+link _ e = e++evaluate :: InterpreterEnv -> LocSimpleTerm -> Expression+evaluate bs = link bs . compile++addPosExp :: SrcLoc -> Expression -> Expression+addPosExp p (EZwirn x) = EZwirn $ withInfos (p :) x+addPosExp _ x = x++removePosExp :: Expression -> Expression+removePosExp (EZwirn z) = EZwirn $ removeInfo z+removePosExp x = x
+ src/zwirn-lang/Zwirn/Language/LSP/Diagnostics.hs view
@@ -0,0 +1,42 @@+module Zwirn.Language.LSP.Diagnostics where++import Data.Functor (void)+import qualified Data.Text as T+import Zwirn.Language.Compiler+import Zwirn.Language.Location+import Zwirn.Language.Simple (simplify)+import Zwirn.Language.Syntax++diagnoseCI :: T.Text -> CI ()+diagnoseCI input = do+ blocks <- runBlocks 0 input+ as <- concat <$> catchMany (map parseBlock blocks)+ -- debug' $ T.pack $ show blocks+ void $ catchMany $ map typeCheckAction as++validateCode :: Environment -> T.Text -> IO (Maybe ErrorType)+validateCode env input = do+ out <- runCI env (diagnoseCI input)+ case out of+ Left (CIError err _) -> return $ Just err+ Right _ -> return Nothing++runTypeCheckTerm :: LocTerm -> CI ()+runTypeCheckTerm t = do+ (t', _) <- macroCI t+ s <- runSimplify t'+ rot <- runRotate s+ void $ runTypeCheck rot++typeCheckAction :: Syntax -> CI ()+typeCheckAction (Exec t) = runTypeCheckTerm t+typeCheckAction (Def (Located p (Definition _ vs t))) = do+ (t', _) <- macroCI t+ let st = Located p (simplify $ TLambda vs t')+ rot <- runRotate st+ void $ runTypeCheck rot+typeCheckAction (DynDef (Located _ (DynamicDefinition _ t))) = runTypeCheckTerm t+typeCheckAction (MacroDef (Located _ (MacroDefinition _ t))) = runTypeCheckTerm t+typeCheckAction (Command (Located _ (ShowCommand t))) = runTypeCheckTerm t+typeCheckAction (Command (Located _ (TypeCommand t))) = runTypeCheckTerm t+typeCheckAction (Command _) = return ()
+ src/zwirn-lang/Zwirn/Language/LSP/Eval.hs view
@@ -0,0 +1,20 @@+module Zwirn.Language.LSP.Eval where++import Control.Monad.State (get)+import qualified Data.Text as T+import Zwirn.Language.Compiler+import Zwirn.Language.Macro (CodeEdit)++evalBlockAt :: T.Text -> Int -> CI (([CodeEdit], [T.Text]), Environment)+evalBlockAt doc l = do+ sys <- getSyntaxBlock l doc+ r <- mapM (runSyntax True) sys+ e <- get+ return (getOutput r, e)++getOutput :: [CompilerOutput] -> ([CodeEdit], [T.Text])+getOutput = foldl f ([], [])+ where+ f (es, ms) (OutMessage m) = (es, m : ms)+ f (es, ms) (OutEdits e) = (e ++ es, ms)+ f (es, ms) _ = (es, ms)
+ src/zwirn-lang/Zwirn/Language/LSP/Hover.hs view
@@ -0,0 +1,57 @@+module Zwirn.Language.LSP.Hover where++import Control.Monad.RWS (gets)+import qualified Data.Text as T+import Zwirn.Language.Compiler+import Zwirn.Language.Environment (AnnotatedExpression (..), lookupFull)+import Zwirn.Language.Location+import Zwirn.Language.Pretty (ppscheme)+import Zwirn.Language.Syntax++-- parses source code+parseAndGetInfoAt :: T.Text -> Position -> CI (Maybe (T.Text, RealSrcLoc))+parseAndGetInfoAt doc pos@(Position l _) = do+ syntax <- getSyntaxLine l doc+ case syntaxGetNodeAt pos =<< syntax of+ Just (Located (SrcLoc p) (TVar x)) -> (\mz -> mz >>= \z -> Just (z, p)) <$> infoMarkdown x+ Just (Located (SrcLoc p) (TNum x)) -> return $ Just (wrapCodeBlock $ x <> " :: Number", p)+ Just (Located (SrcLoc p) (TText x)) -> return $ Just (wrapCodeBlock $ x <> " :: Text", p)+ Just (Located (SrcLoc p) TRest) -> return $ Just (wrapCodeBlock "~ :: a", p)+ Just _ -> return Nothing+ Nothing -> return Nothing++syntaxGetNodeAt :: Position -> Syntax -> Maybe LocTerm+syntaxGetNodeAt p (Command c@(Located _ (ShowCommand t))) = if isContained p c then getNodeAt p t else Nothing+syntaxGetNodeAt p (Command c@(Located _ (TypeCommand t))) = if isContained p c then getNodeAt p t else Nothing+syntaxGetNodeAt _ (Command _) = Nothing+syntaxGetNodeAt p (Exec t) = getNodeAt p t+syntaxGetNodeAt p (Def ldef@(Located _ (Definition _ _ t))) = if isContained p ldef then getNodeAt p t else Nothing+syntaxGetNodeAt p (DynDef ldef@(Located _ (DynamicDefinition _ t))) = if isContained p ldef then getNodeAt p t else Nothing+syntaxGetNodeAt p (MacroDef ldef@(Located _ (MacroDefinition _ t))) = if isContained p ldef then getNodeAt p t else Nothing++getNodeAt :: Position -> LocTerm -> Maybe LocTerm+getNodeAt p lt =+ if isContained p lt+ then+ ( case lt of+ t@(Located _ (TVar _)) -> Just t+ t@(Located _ (TNum _)) -> Just t+ t@(Located _ (TText _)) -> Just t+ t@(Located _ TRest) -> Just t+ (Located _ (TInfix t1 op t2)) -> if isContained p op then Just (fmap TVar op) else findWithPos p [t1, t2] >>= getNodeAt p+ (Located _ (TSectionR op t)) -> if isContained p op then Just (fmap TVar op) else getNodeAt p t+ (Located _ (TSectionL t op)) -> if isContained p op then Just (fmap TVar op) else getNodeAt p t+ (Located _ t) -> findWithPos p (subterms t) >>= getNodeAt p+ )+ else Nothing++infoMarkdown :: T.Text -> CI (Maybe T.Text)+infoMarkdown n = do+ env <- gets intEnv+ case lookupFull n env of+ Just (Annotated _ t (Just d)) -> return $ Just $ wrapCodeBlock (n <> " :: " <> ppscheme t) <> " \n\n" <> d+ Just (Annotated _ t Nothing) -> return $ Just $ wrapCodeBlock $ n <> " :: " <> ppscheme t+ Nothing -> return Nothing++wrapCodeBlock :: T.Text -> T.Text+wrapCodeBlock t = "``` haskell\n" <> t <> "\n```"
+ src/zwirn-lang/Zwirn/Language/LSP/InlayHints.hs view
@@ -0,0 +1,75 @@+module Zwirn.Language.LSP.InlayHints where++import Control.Concurrent (readMVar)+import Control.Monad.RWS (gets, liftIO)+import qualified Data.Map as Map+import Data.Maybe (catMaybes, mapMaybe)+import Data.Text as T (Text, filter, unpack)+import Zwirn.Language (Syntax (..), Term (..), catchMany, parseBlock)+import Zwirn.Language.Compiler (CI, CompilerOutput (..), Environment (..), filterErrors, noSolo, runBlocks, runCommand)+import Zwirn.Language.Location (Located (..), Position (..), RealSrcLoc (..), SrcLoc (..))+import Zwirn.Stream.Target (Targeted (..))+import Zwirn.Stream.Types (Identifier (..), PlayMap, PlayState (..), Stream (..))++data Hint+ = Hint+ { hPos :: Position,+ hContent :: Text,+ hDescription :: Text+ }+ deriving (Show)++getHints :: Text -> CI [Hint]+getHints doc = do+ blocks <- runBlocks 0 doc+ ss <- concat <$> catchMany (map parseBlock blocks)+ sh <- getStreamHints ss+ ch <- getCommandHints ss+ return $ sh ++ ch++-- | inlay hints above stream actions of the form `id &: exp`, for displaying the play state of the according expression+getStreamHints :: [Syntax] -> CI [Hint]+getStreamHints ss = do+ pm <- gets (sPlayMap . tStream) >>= liftIO . readMVar+ let chans = map findStreamPattern ss+ return $ mapMaybe (resolvePlayState pm) chans++-- | returns the channel key and a positon one line above it+findStreamPattern :: Syntax -> Maybe (Identifier, Position)+findStreamPattern (Exec (Located (SrcLoc (RealSrcLoc _ ln ch _ _)) (TInfix (Located _ (TNum chan)) (Located _ "($:)") _))) = if ln <= 1 then Nothing else Just (NumID (read $ unpack chan), Position (ln - 1) ch)+findStreamPattern (Exec (Located (SrcLoc (RealSrcLoc _ ln ch _ _)) (TInfix (Located _ (TText chan)) (Located _ "($:)") _))) = if ln <= 1 then Nothing else Just (TextID $ stripText chan, Position (ln - 1) ch)+ where+ stripText = T.filter (/= '\"')+findStreamPattern _ = Nothing++lookupPlayState :: Identifier -> PlayMap -> Maybe PlayState+lookupPlayState key pm = (\(Targeted _ (x, _, _)) -> x) <$> Map.lookup key pm++resolvePlayState :: PlayMap -> Maybe (Identifier, Position) -> Maybe Hint+resolvePlayState pm mx+ | noSolo pm = do+ (key, pos) <- mx+ ps <- lookupPlayState key pm+ case ps of+ Solo -> return $ Hint pos "(solo)" "stream hint"+ Mute -> return $ Hint pos "(muted)" "stream hint"+ Normal -> return $ Hint pos "(playing)" "stream hint"+ | otherwise = do+ (key, pos) <- mx+ ps <- lookupPlayState key pm+ case ps of+ Solo -> return $ Hint pos "(solo)" "stream hint"+ Mute -> return $ Hint pos "(muted)" "stream hint"+ Normal -> return $ Hint pos "(not soloed)" "stream hint"++-- | inlay hints underneath commands to display their output+getCommandHints :: [Syntax] -> CI [Hint]+getCommandHints ss = catMaybes <$> filterErrors (map commandHint ss)++commandHint :: Syntax -> CI (Maybe Hint)+commandHint (Command (Located (SrcLoc (RealSrcLoc _ _ _ ln ch)) x)) = do+ out <- runCommand x+ case out of+ OutMessage ms -> return $ Just $ Hint (Position ln (ch + 1)) ("\n" <> ms) "command hint"+ _ -> return Nothing+commandHint _ = return Nothing
+ src/zwirn-lang/Zwirn/Language/Lexer.x view
@@ -0,0 +1,383 @@+{+{-# OPTIONS_GHC -Wno-name-shadowing #-}+module Zwirn.Language.Lexer+ ( -- * Invoking Alex+ Alex+ , AlexPosn (..)+ , alexGetInput+ , alexError+ , runAlex+ , alexMonadScan++ , Lexeme+ , Token (..)+ , scanMany+ , increaseChoice+ , setSrcPath+ , getSrcPath+ , setInitialLineNum+ , lineLexer+ , typeLexer+ ) where++{-+ Lexer.hs - lexer for zwirn, code adapted from+ https://serokell.io/blog/lexing-with-alex+ Copyright (C) 2025, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. IfTok not, see <http://www.gnu.org/licenses/>.+-}++import Data.Text (Text)+import qualified Data.Text as Text+import Control.Monad (when)+import Zwirn.Language.Location++}++%wrapper "monadUserState-strict-text"++$digit = [0-9]+$alphasmall = [a-z]+$alphabig = [A-Z]+$alpha = [a-zA-Z]++@id = ($alphasmall) ($alpha | $digit | \_ )*+@singles = ("&" | "$" | "?" | "#" | "." | "^" | ":")+@otherops = ("|" | "=" | "~" | "<" | ">" | "%" | "!")+@specialop = ("*" | "/" | "'" | "+" | "-")+@op = ((@singles (@singles | @otherops | @specialop)*) | ((@otherops | @specialop) (@singles | @otherops | @specialop)+))+@num = ("-")? ($digit)+ ("." ($digit)+)?+@path = $white ($alpha | "/" | ".")++@flag = $alphabig $alpha*++tokens :-++<0> $white+ ;+++-- Single Line Comments+<line> "--" .* (\n?) ;++<line> (.+ (\n?) | \n) { mkLine }+++<ty> $white+ ;+<ty> $alpha+ "." ;+<ty> "=>" { tok TypeContextTok }+<ty> "->" { tok ArrowTok }+<ty> "(" { tok LParTok }+<ty> ")" { tok RParTok }+<ty> "," { tok CommaTok }+<ty> "Text" { tok TextTypeTok }+<ty> "Number" { tok NumberTypeTok }+<ty> "Map" { tok MapTypeTok }+<ty> "Action" { tok ActionTypeTok }+<ty> @id { tokText VarTypeTok }+<ty> [A-Z] $alphasmall+ { tokText TypeClassTok }+<ty> @id { tokText IdentifierTok }+<ty> @op { tokText OperatorTok }+<ty> @specialop { tokText SpecialOperatorTok }++-- Single Line Comments+<0> "--" .* (\n?) ;++-- Macro++<0> "!"($alphasmall)+ { tokText (\t -> MacroTok $ Text.drop 1 t)}++-- Repeat+<0> "!" { tok RepeatTok }+<0> "!"($digit+) { tokText (\t -> RepeatNumberTok $ Text.drop 1 t) }++-- Parenthesis+<0> "(" { tok LParTok }+<0> ")" { tok RParTok }++-- Sequences+<0> "[" { tok LBrackTok }+<0> "]" { tok RBrackTok }++-- Stacks+<0> "," { tok CommaTok }++-- Choice+<0> "|" { tok ChoiceTok }++-- Enum+<0> ".." { tok EnumTok }++-- Polyrhythm+<0> "%" { tok PolyTok }++<0> "{" { tok LBracesTok }+<0> "}" { tok RBracesTok }++-- Lambda+<0> "\" { tok LambdaTok }+<0> "->" { tok ArrowTok }++-- Definitions+<0> "=" { tok DefineTok }+<0> "<-" { tok DynamicDefineTok }++-- Commands+<0> ":t" { tok TypeCommandTok }+<0> ":show" { tok ShowCommandTok }+<0> ":config" { tok ShowConfigCommandTok }+<0> ":resetconfig" { tok ResetShowConfigCommandTok }+<0> ":info" { tok InfoCommandTok }+<0> ":reset" { tok ResetEnvCommandTok }+<0> ":set" { tok SetCommandTok }+<0> ":status" { tok StatusCommandTok }+<0> ":env" { tok EnvCommandTok }+<0> (":load") @path { tokText (\t -> LoadCommandTok $ Text.drop 6 t) }++-- Keywords+<0> if { tok IfTok }+<0> then { tok ThenTok }+<0> else { tok ElseTok }++-- Identifiers+<0> @id { tokText IdentifierTok }++-- Constants+<0> @num { tokText NumberTok }+<0> \"[^\"]*\" { tokText TextTok }+<0> "~" { tok RestTok }+<0> "_" { tok UnderscoreTok }++-- Compiler Flags++<0> @flag { tokText CompilerFlagTok }++-- Operators+<0> @op { tokText OperatorTok }+<0> @specialop { tokText SpecialOperatorTok }++-- Alternations+<0> "<" { tok LAngleTok }+<0> ">" { tok RAngleTok }++{+data AlexUserState = AlexUserState+ { nestLevel :: Int+ , choiceNum :: Int+ , srcPath :: Text+ }++alexInitUserState :: AlexUserState+alexInitUserState = AlexUserState { nestLevel = 0, choiceNum = 0, srcPath = Text.empty}++get :: Alex AlexUserState+get = Alex $ \s -> Right (s, alex_ust s)++put :: AlexUserState -> Alex ()+put s' = Alex $ \s -> Right (s{alex_ust = s'}, ())++modify :: (AlexUserState -> AlexUserState) -> Alex ()+modify f = Alex $ \s -> Right (s{alex_ust = f (alex_ust s)}, ())++alexEOF :: Alex Lexeme+alexEOF = pure $ Located NoLoc EOF++type Lexeme = Located Token++data Token+ -- Identifiers+ = IdentifierTok Text+ -- Constants+ | TextTok Text+ | NumberTok Text+ | MacroTok Text+ | RestTok+ | UnderscoreTok+ -- Operators+ | OperatorTok Text+ | SpecialOperatorTok Text+ -- Repeat+ | RepeatTok+ | RepeatNumberTok Text+ -- Parenthesis+ | LParTok+ | RParTok+ -- Sequences+ | LBrackTok+ | RBrackTok+ -- Stacks+ | CommaTok+ -- Alternations+ | LAngleTok+ | RAngleTok+ -- Choice+ | ChoiceTok+ -- Polyrhythm+ | PolyTok+ | LBracesTok+ | RBracesTok+ -- LambdaTok+ | LambdaTok+ | ArrowTok+ -- If Then Else+ | IfTok+ | ThenTok+ | ElseTok+ -- Enum+ | EnumTok+ -- Definitions+ | DefineTok+ | DynamicDefineTok+ -- Commands+ | TypeCommandTok+ | ShowCommandTok+ | ShowConfigCommandTok+ | ResetShowConfigCommandTok+ | LoadCommandTok Text+ | InfoCommandTok+ | ResetEnvCommandTok+ | SetCommandTok+ | StatusCommandTok+ | EnvCommandTok+ | CompilerFlagTok Text+ -- Line & Block Tokens+ | LineTok Text+ | BlockSepTok+ -- Type Tokens+ | TypeContextTok+ | TextTypeTok+ | NumberTypeTok+ | MapTypeTok+ | ActionTypeTok+ | VarTypeTok Text+ | TypeClassTok Text+ -- EOF+ | EOF+ deriving (Eq)++instance Show Token where+ show (IdentifierTok s) = show s+ show (TextTok s) = show s+ show (NumberTok d) = show d+ show (MacroTok d) = show d+ show RestTok = quoted "~"+ show UnderscoreTok = quoted "_"+ show (OperatorTok o) = show o+ show (SpecialOperatorTok o) = show o+ show RepeatTok = quoted "!"+ show (RepeatNumberTok x) = quoted "!" ++ show x+ show LParTok = quoted "("+ show RParTok = quoted ")"+ show LBrackTok = quoted "["+ show RBrackTok = quoted "]"+ show CommaTok = quoted ","+ show LAngleTok = quoted "<"+ show RAngleTok = quoted ">"+ show ChoiceTok = quoted "|"+ show PolyTok = quoted "%"+ show LBracesTok = quoted "{"+ show RBracesTok = quoted "}"+ show LambdaTok = quoted "\\"+ show ArrowTok = quoted "->"+ show IfTok = quoted "if"+ show ThenTok = quoted "then"+ show ElseTok = quoted "else"+ show EnumTok = quoted ".."+ show DynamicDefineTok = quoted "<-"+ show TypeCommandTok = quoted ":t"+ show ShowCommandTok = quoted ":show"+ show ShowConfigCommandTok = quoted ":config"+ show ResetShowConfigCommandTok = quoted ":resetconfig"+ show DefineTok = quoted "="+ show (LoadCommandTok x) = ":load " <> show x+ show InfoCommandTok = quoted ":info"+ show ResetEnvCommandTok = quoted ":reset"+ show SetCommandTok = quoted ":set"+ show StatusCommandTok = quoted ":status"+ show EnvCommandTok = quoted ":env"+ show (CompilerFlagTok x) = show x+ show (LineTok t) = "line " <> show t+ show BlockSepTok = "block"+ show TypeContextTok = "=>"+ show TextTypeTok = "Text"+ show NumberTypeTok = "Number"+ show MapTypeTok = "Map"+ show ActionTypeTok = "Action"+ show (VarTypeTok t) = show t+ show (TypeClassTok c) = show c+ show EOF = "end of file"++quoted :: String -> String+quoted s = "'" ++ s ++ "'"++mkSrcLoc :: AlexAction SrcLoc+mkSrcLoc (st@(AlexPn _ lst cst), _, _, str) len = do+ let (AlexPn _ lnen cen) = Text.foldl' alexMove st $ Text.take len str+ p <- getSrcPath+ return $ SrcLoc $ RealSrcLoc p lst cst lnen cen++mkLine :: AlexAction Lexeme+mkLine inp@(_, _, _, str) len = case Text.all (\c -> elem c ("\n\t " :: String)) (Text.take len str) of+ True -> tok BlockSepTok inp len+ False -> do+ loc <- mkSrcLoc inp len+ return $ Located loc (LineTok (Text.take len str))++tok :: Token -> AlexAction Lexeme+tok ctor inp len = do+ loc <- mkSrcLoc inp len+ return $ Located loc ctor+++tokText :: (Text -> Token) -> AlexAction Lexeme+tokText f inp@(_, _, _, str) len = do+ loc <- mkSrcLoc inp len+ return $ Located loc (f $ Text.take len str)+++increaseChoice :: Alex Int+increaseChoice = do+ (AlexUserState c x e) <- get+ put $ AlexUserState c (x+1) e+ return x++getSrcPath :: Alex Text+getSrcPath = do+ (AlexUserState _ _ p) <- get+ return p++setSrcPath :: Text -> Alex ()+setSrcPath i = do+ (AlexUserState c x _) <- get+ put $ AlexUserState c x i++setInitialLineNum :: Int -> Alex ()+setInitialLineNum i = Alex alex+ where alex s = Right (s {alex_pos = AlexPn x i c }, ())+ where AlexPn x _ c = alex_pos s++lineLexer :: Alex ()+lineLexer = alexSetStartCode line++typeLexer :: Alex ()+typeLexer = alexSetStartCode ty++scanMany :: Text -> Either String [Lexeme]+scanMany input = runAlex input go+ where+ go = do+ output <- lineLexer >> alexMonadScan+ if lValue output == EOF+ then pure [output]+ else ((output) :) <$> go+}
+ src/zwirn-lang/Zwirn/Language/Location.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE DeriveFunctor #-}++module Zwirn.Language.Location where++{-+ Location.hs - data types and functions for manipulating source locations+ Copyright (C) 2023, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Data.List (find)+import Data.Text (Text)++data RealSrcLoc+ = RealSrcLoc+ { rSrcFile :: !Text,+ rStartLine :: !Int,+ rStartChar :: !Int,+ rEndLine :: !Int,+ rEndChar :: !Int+ }+ deriving (Eq, Show)++data SrcLoc+ = SrcLoc !RealSrcLoc+ | NoLoc+ deriving (Eq, Show)++data Located a+ = Located {lLoc :: !SrcLoc, lValue :: a}+ deriving (Eq, Show, Functor)++data Position = Position Int Int+ deriving (Eq, Show)++instance Semigroup SrcLoc where+ (<>) (SrcLoc (RealSrcLoc p1 lst cst _ _)) (SrcLoc (RealSrcLoc p2 _ _ len cen)) =+ if p1 == p2+ then SrcLoc (RealSrcLoc p1 lst cst len cen)+ else NoLoc+ (<>) NoLoc r = r+ (<>) r NoLoc = r++instance Monoid SrcLoc where+ mempty = NoLoc++noLoc :: a -> Located a+noLoc = Located NoLoc++withLoc :: RealSrcLoc -> a -> Located a+withLoc rn = Located (SrcLoc rn)++mapLoc :: (SrcLoc -> SrcLoc) -> Located a -> Located a+mapLoc f (Located s x) = Located (f s) x++mergeLocs :: [SrcLoc] -> SrcLoc+mergeLocs [] = NoLoc+mergeLocs [x] = x+mergeLocs (x : ys) = x <> last ys++mergeManyWith :: ([Located a] -> b) -> [Located a] -> Located b+mergeManyWith f xs = Located (mergeLocs ls) b+ where+ b = f xs+ ls = map lLoc xs++mergeTwoWith :: (Located a -> Located a -> b) -> Located a -> Located a -> Located b+mergeTwoWith f a b = Located (lLoc a <> lLoc b) (f a b)++mergeThreeWith :: (Located a -> Located a -> Located a -> b) -> Located a -> Located a -> Located a -> Located b+mergeThreeWith f a b c = Located (lLoc a <> lLoc c) (f a b c)++(<->) :: Located a -> Located b -> SrcLoc+(<->) a b = lLoc a <> lLoc b++isContained :: Position -> Located a -> Bool+isContained (Position _ _) (Located NoLoc _) = False+isContained (Position lp cp) (Located (SrcLoc (RealSrcLoc _ lst cst len cen)) _) = lst <= lp && lp <= len && cst <= cp && cp <= cen++findWithPos :: Position -> [Located a] -> Maybe (Located a)+findWithPos p = find (isContained p)
+ src/zwirn-lang/Zwirn/Language/Macro.hs view
@@ -0,0 +1,100 @@+module Zwirn.Language.Macro where++import Control.Monad.State (StateT, lift, modify, runStateT)+import qualified Data.Map as Map+import qualified Data.Text as T+import Zwirn.Language.Location (Located (..), RealSrcLoc (..), SrcLoc (..))+import Zwirn.Language.Pretty (ppterm)+import Zwirn.Language.Syntax (LocTerm, Term (..))++type MacroMap = Map.Map T.Text LocTerm++-- represents the code edit that replaces the text at editPos with editText+data CodeEdit = CodeEdit+ { editPos :: RealSrcLoc,+ editText :: T.Text+ }+ deriving (Eq, Show)++-- a simple monad that accumulates code edits+type MacroMonad = StateT [CodeEdit] Maybe++defaultMacroMap :: MacroMap+defaultMacroMap = Map.fromList [("num", Located (SrcLoc (RealSrcLoc "internal" 0 0 0 1)) (TNum "1"))]++runMacros :: LocTerm -> MacroMap -> Maybe (LocTerm, [CodeEdit])+runMacros t mmap = runStateT (substituteMacros mmap t) []++substituteMacros :: MacroMap -> LocTerm -> MacroMonad LocTerm+substituteMacros _ t@(Located _ (TNum _)) = return t+substituteMacros _ t@(Located _ (TText _)) = return t+substituteMacros _ t@(Located _ (TVar _)) = return t+substituteMacros ms (Located (SrcLoc oldPos@(RealSrcLoc doc stln stch _ _)) (TMacro name)) = do+ (Located p m) <- lift $ Map.lookup name ms+ let replace = ppterm m+ case p of+ NoLoc -> lift Nothing+ SrcLoc (RealSrcLoc _ stln' _ enln' ench') -> do+ let newPos = RealSrcLoc doc stln stch (stln + (enln' - stln')) ench'+ edit = CodeEdit oldPos replace+ modify (edit :)+ return $ Located (SrcLoc newPos) m+substituteMacros _ t@(Located _ TRest) = return t+substituteMacros ms (Located p (TBracket t)) = do+ s <- substituteMacros ms t+ return $ Located p (TBracket s)+substituteMacros ms (Located p (TRepeat t i)) = do+ s <- substituteMacros ms t+ return $ Located p (TRepeat s i)+substituteMacros ms (Located p (TLambda x t)) = do+ s <- substituteMacros ms t+ return $ Located p (TLambda x s)+substituteMacros ms (Located p (TSectionL t x)) = do+ s <- substituteMacros ms t+ return $ Located p (TSectionL s x)+substituteMacros ms (Located p (TSectionR x t)) = do+ s <- substituteMacros ms t+ return $ Located p (TSectionR x s)+substituteMacros ms (Located p (TSeq ts)) = do+ ss <- mapM (substituteMacros ms) ts+ return $ Located p (TSeq ss)+substituteMacros ms (Located p (TStack ts)) = do+ ss <- mapM (substituteMacros ms) ts+ return $ Located p (TStack ss)+substituteMacros ms (Located p (TAlt ts)) = do+ ss <- mapM (substituteMacros ms) ts+ return $ Located p (TAlt ss)+substituteMacros ms (Located p (TChoice i ts)) = do+ ss <- mapM (substituteMacros ms) ts+ return $ Located p (TChoice i ss)+substituteMacros ms (Located p (TPoly t1 t2)) = do+ s1 <- substituteMacros ms t1+ s2 <- substituteMacros ms t2+ return $ Located p (TPoly s1 s2)+substituteMacros ms (Located p (TApp t1 t2)) = do+ s1 <- substituteMacros ms t1+ s2 <- substituteMacros ms t2+ return $ Located p (TApp s1 s2)+substituteMacros ms (Located p (TInfix t1 n t2)) = do+ s1 <- substituteMacros ms t1+ s2 <- substituteMacros ms t2+ return $ Located p (TInfix s1 n s2)+substituteMacros ms (Located p (TIfThenElse t1 t2 Nothing)) = do+ s1 <- substituteMacros ms t1+ s2 <- substituteMacros ms t2+ return $ Located p (TIfThenElse s1 s2 Nothing)+substituteMacros ms (Located p (TEnum k t1 t2)) = do+ s1 <- substituteMacros ms t1+ s2 <- substituteMacros ms t2+ return $ Located p (TEnum k s1 s2)+substituteMacros ms (Located p (TEnumThen k t1 t2 t3)) = do+ s1 <- substituteMacros ms t1+ s2 <- substituteMacros ms t2+ s3 <- substituteMacros ms t3+ return $ Located p (TEnumThen k s1 s2 s3)+substituteMacros ms (Located p (TIfThenElse t1 t2 (Just t3))) = do+ s1 <- substituteMacros ms t1+ s2 <- substituteMacros ms t2+ s3 <- substituteMacros ms t3+ return $ Located p (TIfThenElse s1 s2 (Just s3))+substituteMacros _ _ = lift Nothing
+ src/zwirn-lang/Zwirn/Language/Parser.y view
@@ -0,0 +1,400 @@+{+module Zwirn.Language.Parser+ ( parseSyntaxWithPos+ , parseSyntax+ , parseTermWithPos+ , parseTerm+ , parseBlocks+ , parseScheme+ ) where++import Data.Text (Text)+import qualified Data.Text as Text+import qualified Data.List.NonEmpty as NE+import Data.Maybe (fromJust)+import Data.Monoid (First (..))+import Data.List (intercalate, sortOn)++import qualified Zwirn.Language.Lexer as L+import Zwirn.Language.Syntax+import Zwirn.Language.TypeCheck.Types+import Zwirn.Language.TypeCheck.Infer+import Zwirn.Language.Block+import Zwirn.Language.Location++{-+ Parser.hs - parser for zwirn, code adapted from+ https://serokell.io/blog/parsing-with-happy+ Copyright (C) 2025, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. IfTok not, see <http://www.gnu.org/licenses/>.+-}+++}++%name pTerm term+%name pSyntax syntax+%name pBlocks blocks+%name pScheme scheme+%tokentype { L.Lexeme }+%errorhandlertype explist+%error { parseError }+%monad { L.Alex } { >>= } { pure }+%lexer { lexer } { Located _ L.EOF }+%expect 0++%token+ -- Keywords+ if { Located _ L.IfTok }+ then { Located _ L.ThenTok }+ else { Located _ L.ElseTok }+ -- IdentifierToks+ identifier { Located _ (L.IdentifierTok _) }+ -- OperatorToks+ operator { Located _ (L.OperatorTok _) }+ specop { Located _ (L.SpecialOperatorTok _) }+ -- Constants+ string { Located _ (L.TextTok _) }+ number { Located _ (L.NumberTok _) }+ macro { Located _ (L.MacroTok _) }+ flag { Located _ (L.CompilerFlagTok _) }+ line { Located _ (L.LineTok _) }+ bsep { Located _ (L.BlockSepTok) }+ '~' { Located _ L.RestTok }+ '_' { Located _ L.UnderscoreTok }+ -- Repeat+ '!' { Located _ L.RepeatTok }+ repnum { Located _ (L.RepeatNumberTok _) }+ -- Parenthesis+ '(' { Located _ L.LParTok }+ ')' { Located _ L.RParTok }+ -- Sequences+ '[' { Located _ L.LBrackTok }+ ']' { Located _ L.RBrackTok }+ -- Stacks+ ',' { Located _ L.CommaTok }+ -- Alternations+ '<' { Located _ L.LAngleTok }+ '>' { Located _ L.RAngleTok }+ -- Choice+ '|' { Located _ L.ChoiceTok }+ -- EnumTok+ '..' { Located _ L.EnumTok }+ -- PolyTokrhythm+ '%' { Located _ L.PolyTok }+ -- LambdaTok+ '\\' { Located _ L.LambdaTok }+ '->' { Located _ L.ArrowTok }+ -- Definitions+ '=' { Located _ L.DefineTok }+ '<-' { Located _ L.DynamicDefineTok }+ -- Actions+ ':t' { Located _ L.TypeCommandTok }+ ':show' { Located _ L.ShowCommandTok }+ ':config' { Located _ L.ShowConfigCommandTok }+ ':resetconfig' { Located _ L.ResetShowConfigCommandTok }+ ':load' { Located _ (L.LoadCommandTok _ ) }+ ':info' { Located _ L.InfoCommandTok }+ ':reset' { Located _ L.ResetEnvCommandTok }+ ':set' { Located _ L.SetCommandTok }+ ':status' { Located _ L.StatusCommandTok }+ ':env' { Located _ L.EnvCommandTok }+ -- Type Tokens+ '=>' { Located _ L.TypeContextTok }+ textT { Located _ L.TextTypeTok }+ numT { Located _ L.NumberTypeTok }+ mapT { Located _ L.MapTypeTok }+ actionT { Located _ L.ActionTypeTok }+ varT { Located _ (L.VarTypeTok _) }+ classT { Located _ (L.TypeClassTok _) }++%%++-------------------------------------------------------------+------------------------- utilities -------------------------+-------------------------------------------------------------++optional(p)+ : { Nothing }+ | p { Just $1 }++many_rev(p)+ : { [] }+ | many_rev(p) p { $2 : $1 }++many(p)+ : many_rev(p) { reverse $1 }++some_rev(p)+ : p { [$1] }+ | some_rev(p) p { $2 : $1 }++some(p)+ : some_rev(p) { reverse $1 }++sepBy_rev(p, sep)+ : p { [$1] }+ | sepBy_rev(p, sep) sep p { $3 : $1 }++sepBy(p, sep)+ : sepBy_rev(p, sep) { reverse $1 }++sepBy_rev2(p, sep)+ : p sep p { [$3, $1] }+ | sepBy_rev2(p, sep) sep p { $3 : $1 }++sepBy2(p, sep)+ : sepBy_rev2(p, sep) { reverse $1 }++-------------------------------------------------------------+----------------------- parsing terms -----------------------+-------------------------------------------------------------++atom :: { LocTerm }+ : identifier { (mkAtom TVar) $1 }+ | '(' operator ')' { (mkAtom TVar) $2 }+ | '(' specop ')' { (mkAtom TVar) $2 }+ | number { (mkAtom TNum) $1 }+ | string { (mkAtom TText) $1 }+ | macro { (mkAtom TMacro) $1 }+ | '~' { Located (lLoc $1) TRest }++simpleseq :: { [LocTerm] }+ : infix %shift { [$1] }+ | infix simpleseq { $1:$2 }++seq :: { LocTerm }+ : simpleseq { mergeManyWith TSeq $1 }+ | infix '..' infix { Located ($1 <-> $3) $ (TEnum Run) $1 $3 }+ | infix infix '..' infix { Located ($1 <-> $4) $ (TEnumThen Run) $1 $2 $4 }++sequence :: { LocTerm }+ : '[' seq ']' { $2 }+ | '[' ']' { Located ($1 <-> $2) TRest }++choice :: { LocTerm }+ : '[' sepBy2(simpleseq, '|') ']' { % L.increaseChoice >>= \x -> return $ Located ($1 <-> $3) (TChoice x (map (mergeManyWith TSeq) $2)) }+ | '[' simpleseq '|' '..' simpleseq ']' { Located ($1 <-> $6) $ (TEnum Choice) (mergeManyWith TSeq $2) (mergeManyWith TSeq $5) }+ | '[' simpleseq '|' simpleseq '..' simpleseq ']' { Located ($1 <-> $7) $ (TEnumThen Choice) (mergeManyWith TSeq $2) (mergeManyWith TSeq $4) (mergeManyWith TSeq $6) }++lambda :: { LocTerm }+ : '\\' some(identifier) '->' term %shift { Located ($1 <-> $4) $ TLambda (map unTok $2) $4 }++polyrhythm :: { LocTerm }+ : simple '%' simple %shift { Located ($1 <-> $3) $ TPoly $1 $3 }++repeat :: { LocTerm }+ : simple repnum { Located ($1 <-> $2) $ TRepeat $1 (Just $ read $ Text.unpack $ unTok $2) }+ | simple '!' { Located ($1 <-> $2) $ TRepeat $1 Nothing }++stack :: { LocTerm }+ : '[' sepBy2(simpleseq, ',') ']' { Located ($1 <-> $3) $ TStack (map (mergeManyWith TSeq) $2) }+ | '[' simpleseq ',' '..' simpleseq ']' { Located ($1 <-> $6) $ TEnum Cord (mergeManyWith TSeq $2) (mergeManyWith TSeq $5) }+ | '[' simpleseq ',' simpleseq '..' simpleseq ']' { Located ($1 <-> $7) $ TEnumThen Cord (mergeManyWith TSeq $2) (mergeManyWith TSeq $4) (mergeManyWith TSeq $6) }++alt :: { LocTerm }+ : simpleseq { mergeManyWith TAlt $1 }+ | infix '..' infix { Located ($1 <-> $3) $ (TEnum Alt) $1 $3 }+ | infix infix '..' infix { Located ($1 <-> $4) $ (TEnumThen Alt) $1 $2 $4 }++alternation :: { LocTerm }+ : '<' alt '>' { mapLoc (const ($1 <-> $3)) $2 }++bracket :: { LocTerm }+ : '(' term ')' { Located ($1 <-> $3) $ TBracket $2 }++simple :: { LocTerm }+ : atom { $1 }+ | alternation { $1 }+ | sequence { $1 }+ | choice { $1 }+ | stack { $1 }+ | lambda { $1 }+ | polyrhythm { $1 }+ | repeat { $1 }+ | bracket { $1 }++-- special operators are left-associative+specialinfix :: { LocTerm }+ : specialinfix specop simple %shift { mapLoc (const $ $1 <-> $3) $ mkAtom (\t -> TInfix $1 (Located (lLoc $2) t) $3) $2 }+ | simple %shift { $1 }++-- all other operators are assumed to be right-associative, AST rotation will fix it+-- this definition is for use inside of sequences+infix :: { LocTerm }+ : specialinfix operator infix %shift { mapLoc (const $ $1 <-> $3) $ mkAtom (\t -> TInfix $1 (Located (lLoc $2) t) $3) $2 }+ | specialinfix %shift { $1 }++-- application is left-associative, binds stronger than operators+-- outside of sequences+app :: { LocTerm }+ : app specialinfix %shift { Located ($1 <-> $2) $ TApp $1 $2 }+ | specialinfix %shift { $1 }++sectionR :: { LocTerm }+ : operator app %shift { mapLoc (const $ $1 <-> $2) $ mkAtom (\t -> TSectionR (Located (lLoc $1) t) $2) $1 }++sectionL :: { LocTerm }+ : app operator %shift { mapLoc (const $ $1 <-> $2) $ mkAtom (\t -> TSectionL $1 (Located (lLoc $2) t)) $2 }++conditional :: { LocTerm }+ : if term then term %shift { Located ($1 <-> $4) $ TIfThenElse $2 $4 Nothing }+ | if term then term else term { Located ($1 <-> $6) $ TIfThenElse $2 $4 (Just $6) }++-- operators outside of sequences have the weakest binding+term :: { LocTerm }+ : app operator term %shift { mapLoc (const $ $1 <-> $3) $ mkAtom (\t -> TInfix $1 (Located (lLoc $2) t) $3) $2 }+ | app %shift { $1 }+ | sectionR %shift { $1 }+ | sectionL %shift { $1 }+ | conditional { $1 }++-----------------------------------------------------------------+---------------------- parsing full syntax ----------------------+-----------------------------------------------------------------++def :: { Located Definition }+ : identifier many(identifier) '=' term { Located ($1 <-> $4) $ Definition (unTok $1) (map unTok $2) $4 }+ | '(' operator ')' many(identifier) '=' term { Located ($1 <-> $6) $ Definition (unTok $2) (map unTok $4) $6 }++dyndef :: { Located DynamicDefinition }+ : identifier '<-' term { Located ($1 <-> $3) $ DynamicDefinition (unTok $1) $3 }++macrodef :: { Located MacroDefinition }+ : macro '=' term { Located ($1 <-> $3) $ MacroDefinition (unTok $1) $3 }++command :: { Located Command }+ : ':config' { Located (lLoc $1) ShowConfigPathCommand }+ | ':resetconfig' { Located (lLoc $1) ResetConfigCommand }+ | ':reset' { Located (lLoc $1) ResetEnvCommand }+ | ':status' { Located (lLoc $1) StatusCommand }+ | ':env' { Located (lLoc $1) EnvCommand }+ | ':load' { Located (lLoc $1) $ LoadCommand $ unTok $1 }+ | ':set' flag { Located ($1 <-> $2) $ SetCommand (unTok $2) }+ | ':info' identifier { Located ($1 <-> $2) $ InfoCommand $ unTok $2 }+ | ':t' term { Located ($1 <-> $2) $ TypeCommand $2 }+ | ':show' term { Located ($1 <-> $2) $ ShowCommand $2 }++syntax :: { Syntax }+ : dyndef { DynDef $1 }+ | def { Def $1 }+ | macrodef { MacroDef $1 }+ | command { Command $1 }++-------------------------------------------------------------+----------------------- parsing blocks ----------------------+-------------------------------------------------------------++block :: { Block }+ : some(line) { toBlock $1 }++blocksrecrev :: { [Block] }+ : blocksrecrev some(bsep) block { $3:$1 }+ | block { [$1] }++blocks :: { [Block] }+ : some(bsep) blocksrecrev some(bsep) { reverse $2 }+ | some(bsep) blocksrecrev { reverse $2 }+ | blocksrecrev some(bsep) { reverse $1 }+ | blocksrecrev { reverse $1 }++-------------------------------------------------------------+----------------------- parsing types -----------------------+-------------------------------------------------------------++atomType :: { Type }+ : textT { TypeCon "Text" }+ | numT { TypeCon "Number" }+ | mapT { TypeCon "Map" }+ | actionT { TypeCon "Action" }+ | varT { TypeVar (unTok $1) }++fullType :: { Type }+ : atomType { $1 }+ | fullType '->' fullType %shift { TypeArr (noLoc $1) (noLoc $3) }+ | '(' fullType ')' { $2 }++predicate :: { Predicate }+ : classT varT { IsIn (unTok $1) (TypeVar (unTok $2)) }++predicates :: { [Predicate] }+ : predicate '=>' { [$1] }+ | { [] }++scheme :: { Scheme }+ : predicates fullType %shift { generalize $1 [] (noLoc $2) }+++{++parseError :: (L.Lexeme, [String]) -> L.Alex a+parseError (Located _ t, poss) = do+ (L.AlexPn _ ln column, _, _, _) <- L.alexGetInput+ L.alexError $ "Parse error at line " <> show ln <> ", column " <> show column+ <> "\n\tunexpected " <> show t+ <> "\n\texpecting " <> (intercalate "," poss)++lexer :: (L.Lexeme -> L.Alex a) -> L.Alex a+lexer = (=<< L.alexMonadScan)++unTok :: L.Lexeme -> Text+unTok (Located _ (L.IdentifierTok x)) = x+unTok (Located _ (L.NumberTok x)) = x+unTok (Located _ (L.TextTok x))= x+unTok (Located _ (L.MacroTok x))= x+unTok (Located _ (L.OperatorTok x)) = "(" <> x <> ")"+unTok (Located _ (L.SpecialOperatorTok x)) = "(" <> x <> ")"+unTok (Located _ (L.LoadCommandTok x)) = x+unTok (Located _ (L.LineTok x)) = x+unTok (Located _ (L.VarTypeTok x)) = x+unTok (Located _ (L.TypeClassTok x)) = x+unTok (Located _ (L.RepeatNumberTok x)) = x+unTok (Located _ (L.CompilerFlagTok x)) = x+unTok _ = error "can't untok"++mkAtom :: (Text -> Term) -> L.Lexeme -> LocTerm+mkAtom constr tok@(Located l _) = Located l (constr (unTok tok))++toBlock :: [L.Lexeme] -> Block+toBlock xs = Block ls+ where ls = case NE.nonEmpty xs of+ Just ys -> structureLines $ NE.map (\r -> Line (getLn r) (getLn r) (unTok r)) ys+ Nothing -> error "Can't happen"+ getLn (Located (SrcLoc (RealSrcLoc _ l _ _ _)) _) = l+++parseSyntaxWithPos :: Int -> Text -> Text -> Either String Syntax+parseSyntaxWithPos ln srcp input = case parseTermWithPos ln srcp input of+ Left _ -> L.runAlex input (L.setSrcPath srcp >> L.setInitialLineNum ln >> pSyntax)+ Right s -> Right $ Exec s++parseSyntax :: Text -> Either String Syntax+parseSyntax input = case L.runAlex input pSyntax of+ Left _ -> Exec <$> parseTerm input+ Right s -> Right s++parseTermWithPos :: Int -> Text -> Text -> Either String LocTerm+parseTermWithPos ln srcp input = L.runAlex input (L.setSrcPath srcp >> L.setInitialLineNum ln >> pTerm)++parseTerm :: Text -> Either String LocTerm+parseTerm input = L.runAlex input pTerm++parseBlocks :: Int -> Text -> Either String [Block]+parseBlocks line input = L.runAlex input (L.lineLexer >> L.setInitialLineNum line >> pBlocks)++parseScheme :: Text -> Either String Scheme+parseScheme input = L.runAlex input (L.typeLexer >> pScheme)++}
+ src/zwirn-lang/Zwirn/Language/Pretty.hs view
@@ -0,0 +1,174 @@+{-# OPTIONS_GHC -Wno-orphans #-}++module Zwirn.Language.Pretty where++{-+ Pretty.hs - prettyprinter for zwirn+ Copyright (C) 2025, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import qualified Data.Text as T+import Prettyprinter+import Prettyprinter.Render.Text (renderStrict)+import Text.Read (readMaybe)+import Zwirn.Language.Location (Located (..), RealSrcLoc (..), SrcLoc (..), noLoc)+import Zwirn.Language.Syntax+import Zwirn.Language.TypeCheck.Constraint (TypeError (..))+import Zwirn.Language.TypeCheck.Types+import Zwirn.Stream.Types (Identifier (..))++instance (Pretty a) => Pretty (Located a) where+ pretty (Located _ x) = pretty x++instance Pretty Term where+ pretty (TVar x) = pretty x+ pretty (TNum x) = case (readMaybe (T.unpack x) :: Maybe Int) of+ Just i -> pretty i+ Nothing -> pretty x+ pretty (TText x) = pretty x+ pretty (TMacro x) = "!" <> pretty x+ pretty (TBracket x) = parens $ pretty x+ pretty TRest = "~"+ pretty (TRepeat x Nothing) = pretty x <> "!"+ pretty (TRepeat x (Just i)) = pretty x <> sep (replicate i "!")+ pretty (TSeq [t]) = pretty t+ pretty (TSeq ts) = group $ brackets $ sep $ map pretty ts+ pretty (TStack ts) = alignedBetween (map pretty ts) lbracket rbracket comma+ pretty (TAlt ts) = group $ alignedBetween (map pretty ts) langle rangle space+ pretty (TChoice _ ts) = group $ alignedBetween (map pretty ts) lbracket rbracket pipe+ pretty (TPoly x y) = pretty x <> "%" <> pretty y+ pretty (TApp x y) = pretty x <+> pretty y+ pretty (TLambda vs x) = "\\" <> hcat (punctuate space $ map (pretty . T.unpack) vs) <+> "->" <+> pretty x+ pretty (TIfThenElse x y (Just z)) = "if" <+> pretty x <+> "then" <+> pretty y <+> "else" <+> pretty z+ pretty (TIfThenElse x y Nothing) = "if" <+> pretty x <+> "then" <+> pretty y+ pretty (TSectionL t n) = pretty t <+> pretty (unpackOp $ lValue n)+ pretty (TSectionR n t) = pretty (unpackOp $ lValue n) <+> pretty t+ pretty (TEnum Run x y) = brackets (pretty x <+> ".." <+> pretty y)+ pretty (TEnumThen Run x y z) = brackets (pretty x <+> pretty y <+> ".." <+> pretty z)+ pretty (TEnum Cord x y) = brackets (pretty x <+> ", .." <+> pretty y)+ pretty (TEnumThen Cord x y z) = brackets (pretty x <> comma <+> pretty y <+> ".." <+> pretty z)+ pretty (TEnum Choice x y) = brackets (pretty x <+> "| .." <+> pretty y)+ pretty (TEnumThen Choice x y z) = brackets (pretty x <+> pipe <+> pretty y <+> ".." <+> pretty z)+ pretty (TEnum Alt x y) = angles (pretty x <+> ".." <+> pretty y)+ pretty (TEnumThen Alt x y z) = angles (pretty x <+> pretty y <+> ".." <+> pretty z)+ pretty inf@(TInfix {}) = startThenAlign (pretty x) (map (\(op, y) -> pretty (unpackOp $ lValue op) <+> pretty y) xs)+ where+ (x, xs) = infixChain (noLoc inf)++alignedBetween :: [Doc a] -> Doc a -> Doc a -> Doc a -> Doc a+alignedBetween [] _ _ _ = mempty+alignedBetween (x : xs) l r s = align $ vcat $ (l <> x) : map (s <>) xs ++ [r]++startThenAlign :: Doc ann -> [Doc ann] -> Doc ann+startThenAlign x xs = x <+> align (vsep xs)++unpackOp :: T.Text -> String+unpackOp t = filter (\c -> c /= '(' && c /= ')') $ T.unpack t++infixChain :: LocTerm -> (LocTerm, [(LocVar, LocTerm)])+infixChain (Located _ (TInfix x op y)) = let (t, cs) = infixChain y in (x, (op, t) : cs)+infixChain t = (t, [])++parensIf :: Bool -> Doc a -> Doc a+parensIf True = parens+parensIf False = id++instance Pretty Identifier where+ pretty (TextID t) = pretty t+ pretty (NumID i) = pretty i++instance Pretty Type where+ pretty (TypeArr a b) = parensIf (isArrow a) (pretty a) <+> "->" <+> pretty b+ where+ isArrow (Located _ TypeArr {}) = True+ isArrow _ = False+ pretty (TypeVar a) = pretty a+ pretty (TypeCon a) = pretty a++instance Pretty Predicate where+ pretty (IsIn c t) = pretty c <+> pretty t++prettyPredicates :: [Predicate] -> Doc a+prettyPredicates ps = parensIf (length ps > 1) (hcat (punctuate comma (map pretty ps)))++instance (Pretty a) => Pretty (Qualified a) where+ pretty (Qual [] _ t) = pretty t+ pretty (Qual ps _ t) = prettyPredicates ps <+> "=>" <+> pretty t++instance Pretty Scheme where+ pretty (Forall _ t) = pretty t++instance Pretty SrcLoc where+ pretty NoLoc = "NoLoc"+ pretty (SrcLoc (RealSrcLoc _ lst cst len cen)) = parens $ vcat $ punctuate comma [pretty lst, pretty cst, pretty len, pretty cen]++renderDoc :: Doc a -> T.Text+renderDoc = renderStrict . layoutPretty defaultLayoutOptions++render :: (Pretty a) => a -> T.Text+render = renderDoc . pretty++pptype :: Type -> T.Text+pptype = render++ppscheme :: Scheme -> T.Text+ppscheme = render++ppterm :: Term -> T.Text+ppterm = render++ppTermHasType :: (LocTerm, Scheme) -> T.Text+ppTermHasType (t, s) = renderDoc $ pretty t <+> "::" <+> pretty s++ppID :: Identifier -> T.Text+ppID = render++instance Pretty TypeError where+ pretty (UnificationFail (Located _ (a, b))) = "Cannot unify types:" <+> pretty a <+> "~" <+> pretty b+ pretty (InfiniteType a b) = "Cannot construct the infinite type:" <+> pretty a <+> "=" <+> pretty b+ pretty (Ambigious cs) = vsep ["Cannot not match expected type: '" <> pretty a <> "' with actual type: '" <> pretty b <> "'\n" | Located _ (a, b) <- cs]+ pretty (UnboundVariable a) = "Variable not in scope:" <+> pretty a+ pretty (NoInstance (Located _ (IsIn c x))) = "No instance for" <+> pretty c <+> pretty x+ pretty NotImplemented = "Case not implemented in type-checker."++instance Pretty Command where+ pretty (TypeCommand t) = ":t" <+> pretty t+ pretty (ShowCommand t) = ":show" <+> pretty t+ pretty (InfoCommand t) = ":info" <+> pretty t+ pretty (SetCommand t) = ":set" <+> pretty t+ pretty (UnsetCommand t) = ":unset" <+> pretty t+ pretty (LoadCommand t) = ":load" <+> pretty t+ pretty ResetConfigCommand = ":resetconfig"+ pretty ShowConfigPathCommand = ":showconfig"+ pretty ResetEnvCommand = ":reset"+ pretty StatusCommand = ":status"+ pretty EnvCommand = ":env"++instance Pretty Definition where+ pretty (Definition x xs t) = pretty x <+> vsep (map pretty xs) <+> "=" <+> pretty t++instance Pretty DynamicDefinition where+ pretty (DynamicDefinition x t) = pretty x <+> "<-" <+> pretty t++instance Pretty MacroDefinition where+ pretty (MacroDefinition x t) = "!" <> pretty x <+> "=" <+> pretty t++instance Pretty Syntax where+ pretty (Exec t) = pretty t+ pretty (Def d) = pretty d+ pretty (DynDef d) = pretty d+ pretty (MacroDef d) = pretty d+ pretty (Command c) = pretty c
+ src/zwirn-lang/Zwirn/Language/Rotate.hs view
@@ -0,0 +1,147 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections #-}++module Zwirn.Language.Rotate+ ( runRotate,+ runRotateUnsafe,+ RotationError (..),+ )+where++{-+ Rotate.hs - syntax tree rotation, code adapted from+ https://gist.github.com/heitor-lassarote/b20d6da0a9042d31e439befb8c236a4e+ Copyright (C) 2023, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Control.Monad.Except+import Control.Monad.Identity+import Zwirn.Language.Location+import Zwirn.Language.Simple+import Zwirn.Language.Syntax++ops :: [Declaration]+ops =+ [ ("(*)", Fixity LeftA 9),+ ("(/)", Fixity LeftA 9),+ ("(.)", Fixity RightA 9),+ ("(+)", Fixity LeftA 6),+ ("(-)", Fixity LeftA 6),+ ("($)", Fixity RightA 0),+ ("($:)", Fixity RightA 0),+ ("(&:)", Fixity RightA 0),+ ("($!)", Fixity RightA 0),+ ("(#!)", Fixity RightA 0),+ ("($|)", Fixity RightA 0),+ ("(|$)", Fixity RightA 0),+ ("(#)", Fixity RightA 3),+ ("(&)", Fixity RightA 2),+ ("(++)", Fixity RightA 5),+ -- arithmetic+ ("(|-)", Fixity LeftA 6),+ ("(-|)", Fixity LeftA 6),+ ("(|+)", Fixity LeftA 6),+ ("(+|)", Fixity LeftA 6),+ ("(|*)", Fixity LeftA 7),+ ("(*|)", Fixity LeftA 7),+ ("(//)", Fixity LeftA 7),+ ("(|/)", Fixity LeftA 7),+ ("(/|)", Fixity LeftA 7),+ -- ord+ ("(<=)", Fixity NonA 4),+ ("(>=)", Fixity NonA 4),+ ("(&&)", Fixity RightA 3),+ ("(||)", Fixity RightA 3),+ ("(==)", Fixity NonA 4)+ ]++defaultFixity :: Fixity+defaultFixity = Fixity LeftA 8++newtype RotationError = RotationError SrcLoc++instance Show RotationError where+ show (RotationError (SrcLoc p)) = "Could not resolve operator precedence at location " ++ show p+ show (RotationError NoLoc) = "Could not resolve operator precedence"++type Rotate a = ExceptT RotationError Identity a++-- | Describes which action the rotation algorithm should use.+data Rotation+ = -- | Fail due to the mixing of incompatible operators.+ Fail+ | -- | Keep the tree as it is.+ Keep+ | -- | Balance the tree to the left.+ Rotate++runRotate :: LocSimpleTerm -> Either RotationError LocSimpleTerm+runRotate t = runIdentity $ runExceptT $ rotate t++runRotateUnsafe :: LocSimpleTerm -> LocSimpleTerm+runRotateUnsafe t = case runRotate t of+ Left err -> error $ show err+ Right r -> r++-- | The Happy parser is written in a way so that it will always create a right-balanced AST.+-- We compare the operators and indicate how to rotate the tree.+shouldRotate :: Fixity -> Fixity -> Rotation+shouldRotate (Fixity a p) (Fixity a' p') = case compare p p' of+ LT -> Keep+ EQ -> case (a, a') of+ (LeftA, LeftA) -> Rotate+ (RightA, RightA) -> Keep+ (_, _) -> Fail+ GT -> Rotate++-- | Rebalances the tree to respect the associativity and precedence of the+-- parsed operators.++-- Not very efficient, but enough for demonstration purposes.+findOp :: LocVar -> Rotate Fixity+findOp (Located _ o) = case lookup o ops of+ Just d -> return d+ Nothing -> return defaultFixity++rotate :: LocSimpleTerm -> Rotate LocSimpleTerm+rotate (Located p1 (SInfix l op r)) = do+ -- Rotating the left side is unneeded since this grammar is very simple.+ -- This is because trees are always right-balanced and the left side is+ -- always an atom.+ lRotated <- rotate l+ rRotated <- rotate r+ case rRotated of+ (Located p2 (SInfix l' op' r')) -> do+ opDec <- findOp op+ opDec' <- findOp op'+ case shouldRotate opDec opDec' of+ Fail -> throwError (RotationError $ lLoc op)+ Keep -> return $ Located p1 $ SInfix lRotated op rRotated+ Rotate -> return $ Located p2 $ SInfix (Located p1 $ SInfix lRotated op l') op' r'+ _ -> return $ Located p1 $ SInfix lRotated op rRotated+rotate (Located p (SApp l r)) = do+ lRotated <- rotate l+ rRotated <- rotate r+ return $ Located p $ SApp lRotated rRotated+rotate (Located p e@(SVar _)) = return $ Located p e+rotate (Located p e@(SText _)) = return $ Located p e+rotate (Located p e@(SNum _)) = return $ Located p e+rotate (Located p SRest) = return $ Located p SRest+rotate (Located p (SSeq ts)) = fmap (Located p . SSeq) (mapM rotate ts)+rotate (Located p (SStack ts)) = fmap (Located p . SStack) (mapM rotate ts)+rotate (Located p (SChoice n ts)) = fmap (Located p . SChoice n) (mapM rotate ts)+rotate (Located p (SLambda vs t)) = Located p . SLambda vs <$> rotate t+rotate (Located p (SBracket t)) = fmap (Located p . SBracket) (rotate t)
+ src/zwirn-lang/Zwirn/Language/Simple.hs view
@@ -0,0 +1,110 @@+{-# LANGUAGE OverloadedStrings #-}++module Zwirn.Language.Simple+ ( simplify,+ simplifyLoc,+ SimpleTerm (..),+ SimpleDef (..),+ LocSimpleTerm,+ )+where++{-+ Simple.hs - desugaring of the zwirn AST+ Copyright (C) 2023, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Data.Text as Text (Text, filter, pack)+import Zwirn.Language.Location+import Zwirn.Language.Syntax++type LocSimpleTerm = Located SimpleTerm++-- simple representation of patterns+data SimpleTerm+ = SVar !Var+ | SText !Text+ | SNum !Text+ | SRest+ | SSeq [LocSimpleTerm]+ | SStack [LocSimpleTerm]+ | SChoice Int [LocSimpleTerm]+ | SLambda Var LocSimpleTerm+ | SApp LocSimpleTerm LocSimpleTerm+ | SInfix LocSimpleTerm LocVar LocSimpleTerm+ | SBracket LocSimpleTerm+ deriving (Eq, Show)++data SimpleDef+ = LetS !Var LocSimpleTerm+ deriving (Eq, Show)++simplifyLoc :: LocTerm -> LocSimpleTerm+simplifyLoc (Located p t) = Located p (simplify t)++simplify :: Term -> SimpleTerm+simplify (TVar x) = SVar x+simplify (TText x) = SText $ stripText x+ where+ stripText = Text.filter (/= '\"')+simplify (TNum x) = SNum x+simplify TRest = SRest+simplify x@(TRepeat _ _) = SSeq $ map (fmap simplify) $ resolveRepeat (Located NoLoc x)+simplify (TSeq ts) = SSeq (map (fmap simplify) $ concatMap resolveRepeat ts)+simplify (TStack ts) = SStack (map (fmap simplify) ts)+simplify (TChoice i ts) = SChoice i (map (fmap simplify) ts)+simplify (TAlt ts) = SBracket $ noLoc $ SInfix (noLoc $ SSeq ss) (noLoc "(/)") (noLoc $ SNum (pack $ show $ length ss))+ where+ ss = map (fmap simplify) $ concatMap resolveRepeat ts+simplify (TPoly (Located _ (TSeq ts)) n) = SBracket $ noLoc $ SInfix (noLoc $ SInfix (noLoc $ SSeq ss) (noLoc "(/)") (noLoc $ SNum (pack $ show $ length ss))) (noLoc "(*)") (fmap simplify n)+ where+ ss = map (fmap simplify) $ concatMap resolveRepeat ts+simplify (TPoly x n) = SInfix (fmap simplify x) (noLoc "(*)") (fmap simplify n)+simplify (TLambda [] t) = simplify (lValue t)+simplify (TLambda (x : xs) t) = SLambda x (noLoc $ simplify $ TLambda xs t)+simplify (TIfThenElse x y (Just z)) = SApp (noLoc $ SApp (noLoc $ SApp (noLoc $ SVar "ifthen") (fmap simplify x)) (fmap simplify y)) (fmap simplify z)+simplify (TIfThenElse x y Nothing) = SApp (noLoc $ SApp (noLoc $ SVar "if") (fmap simplify x)) (fmap simplify y)+simplify (TApp x y) = SApp (fmap simplify x) (fmap simplify y)+simplify (TInfix x op y) = SInfix (fmap simplify x) op (fmap simplify y)+simplify (TSectionR op y) = SLambda "_x" (noLoc $ SInfix (noLoc $ SVar "_x") op (fmap simplify y))+simplify (TSectionL x op) = SLambda "_x" (noLoc $ SInfix (fmap simplify x) op (noLoc $ SVar "_x"))+simplify (TBracket x) = SBracket (fmap simplify x)+simplify (TEnum Run x y) = SApp (noLoc $ SApp (noLoc $ SVar "runFromTo") (fmap simplify x)) (fmap simplify y)+simplify (TEnumThen Run x y z) = SApp (noLoc $ SApp (noLoc $ SApp (noLoc $ SVar "runFromThenTo") (fmap simplify x)) (fmap simplify y)) (fmap simplify z)+simplify (TEnum Alt x y) = SApp (noLoc $ SApp (noLoc $ SVar "slowrunFromTo") (fmap simplify x)) (fmap simplify y)+simplify (TEnumThen Alt x y z) = SApp (noLoc $ SApp (noLoc $ SApp (noLoc $ SVar "slowrunFromThenTo") (fmap simplify x)) (fmap simplify y)) (fmap simplify z)+simplify (TEnum Cord x y) = SApp (noLoc $ SApp (noLoc $ SVar "cordFromTo") (fmap simplify x)) (fmap simplify y)+simplify (TEnumThen Cord x y z) = SApp (noLoc $ SApp (noLoc $ SApp (noLoc $ SVar "cordFromThenTo") (fmap simplify x)) (fmap simplify y)) (fmap simplify z)+simplify (TEnum Choice x y) = SApp (noLoc $ SApp (noLoc $ SVar "chooseFromTo") (fmap simplify x)) (fmap simplify y)+simplify (TEnumThen Choice x y z) = SApp (noLoc $ SApp (noLoc $ SApp (noLoc $ SVar "chooseFromThenTo") (fmap simplify x)) (fmap simplify y)) (fmap simplify z)+simplify _ = error "Found unsubstituted macro while desugaring!"++resolveRepeat :: LocTerm -> [LocTerm]+resolveRepeat t = case getTotalRepeat t of+ Located _ (TRepeat x (Just i)) -> replicate i x+ Located _ (TRepeat x Nothing) -> [x, x]+ x -> [x]++-- TODO : not completely right when Nothing followed by Just...+getRepeat :: (LocTerm, Int) -> LocTerm+getRepeat (Located _ (TRepeat x (Just j)), k) = getRepeat (x, j * k)+getRepeat (Located _ (TRepeat x Nothing), k) = getRepeat (x, k + 1)+getRepeat (x, j) = noLoc $ TRepeat x (Just j)++getTotalRepeat :: LocTerm -> LocTerm+getTotalRepeat (Located _ (TRepeat t (Just i))) = getRepeat (t, i)+getTotalRepeat (Located _ (TRepeat t Nothing)) = getRepeat (t, 2)+getTotalRepeat t = t
+ src/zwirn-lang/Zwirn/Language/Syntax.hs view
@@ -0,0 +1,156 @@+module Zwirn.Language.Syntax where++{-+ Syntax.hs - definition of the zwirn language,+ inspired by tidals mini-notation+ Copyright (C) 2023, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Data.Text (Text)+import Zwirn.Language.Location++type Var = Text++type LocVar = Located Var++data EnumKind = Cord | Choice | Run | Alt deriving (Eq, Show)++type LocTerm = Located Term++-- sugary representation of patterns+data Term+ = TVar !Var+ | TText !Text+ | TNum !Text+ | TMacro !Text+ | TRest+ | TBracket LocTerm+ | TSeq [LocTerm]+ | TStack [LocTerm]+ | TAlt [LocTerm]+ | TChoice !Int [LocTerm]+ | TPoly LocTerm LocTerm+ | TApp LocTerm LocTerm+ | TInfix LocTerm LocVar LocTerm+ | TSectionL LocTerm LocVar+ | TSectionR LocVar LocTerm+ | TLambda [Var] LocTerm+ | TIfThenElse LocTerm LocTerm (Maybe LocTerm)+ | TRepeat LocTerm (Maybe Int)+ | TEnum EnumKind LocTerm LocTerm+ | TEnumThen EnumKind LocTerm LocTerm LocTerm+ deriving (Eq, Show)++data Command+ = TypeCommand LocTerm+ | ShowCommand LocTerm+ | InfoCommand !Text+ | SetCommand !Text+ | UnsetCommand !Text+ | LoadCommand !Text+ | ResetConfigCommand+ | ShowConfigPathCommand+ | ResetEnvCommand+ | StatusCommand+ | EnvCommand+ deriving (Eq, Show)++data Definition = Definition !Text [Text] LocTerm+ deriving (Eq, Show)++data DynamicDefinition = DynamicDefinition !Text LocTerm+ deriving (Eq, Show)++data MacroDefinition = MacroDefinition !Text LocTerm+ deriving (Eq, Show)++data Syntax+ = Exec LocTerm+ | Def (Located Definition)+ | DynDef (Located DynamicDefinition)+ | MacroDef (Located MacroDefinition)+ | Command (Located Command)+ deriving (Eq, Show)++data Associativity+ = NonA+ | LeftA+ | RightA+ deriving (Eq, Show)++type Precedence = Int++data Fixity+ = Fixity Associativity !Precedence+ deriving (Eq, Show)++type Declaration = (Var, Fixity)++subterms :: Term -> [LocTerm]+subterms (TVar _) = []+subterms (TNum _) = []+subterms (TText _) = []+subterms (TMacro _) = []+subterms TRest = []+subterms (TBracket t) = [t]+subterms (TLambda _ t) = [t]+subterms (TRepeat t _) = [t]+subterms (TSeq ts) = ts+subterms (TStack ts) = ts+subterms (TAlt ts) = ts+subterms (TChoice _ ts) = ts+subterms (TPoly x y) = [x, y]+subterms (TApp x y) = [x, y]+subterms (TInfix x _ y) = [x, y]+subterms (TSectionR _ t) = [t]+subterms (TSectionL t _) = [t]+subterms (TEnum _ x y) = [x, y]+subterms (TEnumThen _ x y z) = [x, y, z]+subterms (TIfThenElse x y (Just z)) = [x, y, z]+subterms (TIfThenElse x y Nothing) = [x, y]++-- mapTerm :: (Position -> Text -> Term -> a) -> Term -> [a]+-- mapTerm f t@(TVar pos x) = [f pos x t]+-- mapTerm f t@(TNum pos x) = [f pos x t]+-- mapTerm f t@(TText pos x) = [f pos x t]+-- mapTerm f t@(TRest pos) = [f pos (pack "~") t]+-- mapTerm f (TSeq ts) = concatMap (mapTerm f) ts+-- mapTerm f (TAlt ts) = concatMap (mapTerm f) ts+-- mapTerm f (TStack ts) = concatMap (mapTerm f) ts+-- mapTerm f (TChoice _ ts) = concatMap (mapTerm f) ts+-- mapTerm f (TCase x (Just y) ts) = concatMap (mapTerm f) ([x, y] ++ map snd ts)+-- mapTerm f (TCase x Nothing ts) = concatMap (mapTerm f) (x : map snd ts)+-- mapTerm f (TRepeat t _) = mapTerm f t+-- mapTerm f (TLambda _ t) = mapTerm f t+-- mapTerm f (TBracket t) = mapTerm f t+-- mapTerm f t@(TInfix x n pos y) = [f pos n t] ++ mapTerm f x ++ mapTerm f y+-- mapTerm f t@(TSectionR n pos x) = f pos n t : mapTerm f x+-- mapTerm f t@(TSectionL x n pos) = f pos n t : mapTerm f x+-- mapTerm f (TPoly x y) = mapTerm f x ++ mapTerm f y+-- mapTerm f (TEnum _ x y) = mapTerm f x ++ mapTerm f y+-- mapTerm f (TEnumThen _ x y z) = mapTerm f x ++ mapTerm f y ++ mapTerm f z+-- mapTerm f (TIfThenElse x y (Just z)) = mapTerm f x ++ mapTerm f y ++ mapTerm f z+-- mapTerm f (TIfThenElse x y Nothing) = mapTerm f x ++ mapTerm f y+-- mapTerm f (TApp x y) = mapTerm f x ++ mapTerm f y++-- mapAction :: (Term -> a) -> Action -> Maybe a+-- mapAction f (StreamAction _ t) = Just $ f t+-- mapAction f (StreamSet _ t) = Just $ f t+-- mapAction f (StreamOnce t) = Just $ f t+-- mapAction f (Type t) = Just $ f t+-- mapAction f (Show t) = Just $ f t+-- mapAction f (Def (Let _ _ t)) = Just $ f t+-- mapAction _ _ = Nothing
+ src/zwirn-lang/Zwirn/Language/TypeCheck/Constraint.hs view
@@ -0,0 +1,167 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++module Zwirn.Language.TypeCheck.Constraint+ ( Substitutable (..),+ Subst (..),+ TypeError (..),+ Constraint,+ runSolve,+ unifiable,+ )+where++{-+ Constraint.hs - unification constraint solver adapted from+ https://github.com/sdiehl/write-you-a-haskell/tree/master/chapter7/poly_constraints+ Copyright (C) 2023, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Control.Monad.Except+import Control.Monad.Identity+import qualified Data.Map as Map+import qualified Data.Set as Set+import Zwirn.Language.Environment+import Zwirn.Language.Location+import Zwirn.Language.Syntax (LocVar)+import Zwirn.Language.TypeCheck.Types++data TypeError+ = UnificationFail (Located Constraint)+ | InfiniteType TypeVar LocType+ | UnboundVariable LocVar+ | Ambigious [Located Constraint]+ | NoInstance (Located Predicate)+ | NotImplemented+ deriving (Eq)++type Constraint = (Type, Type)++newtype Subst = Subst (Map.Map TypeVar Type)+ deriving (Eq, Show, Semigroup, Monoid)++type Unifier = (Subst, [Located Constraint])++-- | Constraint solver monad+type Solve a = ExceptT TypeError Identity a++class Substitutable a where+ apply :: Subst -> a -> a+ ftv :: a -> Set.Set TypeVar++instance Substitutable Type where+ apply _ (TypeCon a) = TypeCon a+ apply (Subst s) t@(TypeVar a) = Map.findWithDefault t a s+ apply s (t1 `TypeArr` t2) = apply s t1 `TypeArr` apply s t2++ ftv TypeCon {} = Set.empty+ ftv (TypeVar a) = Set.singleton a+ ftv (t1 `TypeArr` t2) = ftv t1 `Set.union` ftv t2++instance Substitutable Scheme where+ apply (Subst s) (Forall as t) = Forall as $ apply s' t+ where+ s' = Subst $ foldr Map.delete s as+ ftv (Forall as t) = ftv t `Set.difference` Set.fromList as++instance Substitutable Constraint where+ apply s (t1, t2) = (apply s t1, apply s t2)+ ftv (t1, t2) = ftv t1 `Set.union` ftv t2++instance Substitutable AnnotatedExpression where+ apply s (Annotated x sc d) = Annotated x (apply s sc) d+ ftv (Annotated _ s _) = ftv s++instance (Substitutable a) => Substitutable [a] where+ apply = map . apply+ ftv = foldr (Set.union . ftv) Set.empty++instance Substitutable InterpreterEnv where+ apply s (IEnv ty cl) = IEnv (Map.map (apply s) ty) (apply s cl)+ ftv (IEnv ty cl) = ftv (Map.elems ty) `Set.union` ftv cl++instance Substitutable Predicate where+ apply s (IsIn x t) = IsIn x (apply s t)+ ftv (IsIn _ t) = ftv t++instance (Substitutable t) => Substitutable (Qualified t) where+ apply s (Qual ps ds t) = Qual (apply s ps) ds (apply s t)+ ftv (Qual ps _ t) = ftv ps `Set.union` ftv t++instance (Substitutable t) => Substitutable (Located t) where+ apply s = fmap (apply s)+ ftv (Located _ t) = ftv t++-------------------------------------------------------------------------------+-- Constraint Solver+-------------------------------------------------------------------------------++unifiable :: (Type, Type) -> Bool+unifiable t = case runSolve [noLoc t] of+ Left _ -> False+ Right _ -> True++-- | The empty substitution+emptySubst :: Subst+emptySubst = mempty++-- | Compose substitutions+compose :: Subst -> Subst -> Subst+(Subst s1) `compose` (Subst s2) = Subst $ Map.map (apply (Subst s1)) s2 `Map.union` s1++-- | Run the constraint solver+runSolve :: [Located Constraint] -> Either TypeError Subst+runSolve cs = runIdentity $ runExceptT $ solver st+ where+ st = (emptySubst, cs)++unifies :: Located Constraint -> Solve Subst+unifies (Located _ (t1, t2)) | t1 == t2 = return emptySubst+unifies (Located _ (TypeVar v, t)) = v `bind` t+unifies (Located _ (t, TypeVar v)) = v `bind` t+unifies (Located p (TypeArr t1 t2, TypeArr t3 t4)) = do+ su1 <- unifies (Located (specifyLoc (lLoc t1) (lLoc t3) p) (lValue t1, lValue t3))+ su2 <- unifies (Located (specifyLoc (lLoc t2) (lLoc t4) p) (apply su1 (lValue t2), apply su1 (lValue t4)))+ return $ su2 `compose` su1+unifies c = throwError $ UnificationFail c++specifyLoc :: SrcLoc -> SrcLoc -> SrcLoc -> SrcLoc+specifyLoc NoLoc NoLoc p = p+specifyLoc (SrcLoc _) (SrcLoc _) p = p+specifyLoc NoLoc p _ = p+specifyLoc p NoLoc _ = p++-- Unification solver+solver :: Unifier -> Solve Subst+solver (su, cs) =+ case cs of+ [] -> return su+ (c : cs0) -> do+ su1 <- unifies c+ solver (su1 `compose` su, apply su1 cs0)++bind :: TypeVar -> Type -> Solve Subst+bind a t+ | isTypeVar a t = return emptySubst+ | occursCheck a t = throwError $ InfiniteType a (Located NoLoc t)+ | otherwise = return (Subst $ Map.singleton a t)++isTypeVar :: TypeVar -> Type -> Bool+isTypeVar x (TypeVar y) = x == y+isTypeVar _ _ = False++occursCheck :: (Substitutable a) => TypeVar -> a -> Bool+occursCheck a t = a `Set.member` ftv t
+ src/zwirn-lang/Zwirn/Language/TypeCheck/Infer.hs view
@@ -0,0 +1,194 @@+{-# LANGUAGE OverloadedStrings #-}++module Zwirn.Language.TypeCheck.Infer+ ( inferTerm,+ generalize,+ )+where++{-+ Infer.hs - type inference algorithm adapted from+ https://github.com/sdiehl/write-you-a-haskell/tree/master/chapter7/poly_constraints+ Copyright (C) 2023, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Control.Monad (replicateM)+import Control.Monad.Except+import Control.Monad.Reader+import Control.Monad.State+import Data.List (nub)+import qualified Data.Map as Map+import qualified Data.Set as Set+import Data.Text (Text, pack)+import Zwirn.Language.Environment+import Zwirn.Language.Location+import Zwirn.Language.Simple+import Zwirn.Language.Syntax (Var)+import Zwirn.Language.TypeCheck.Constraint+import Zwirn.Language.TypeCheck.Types++type Infer a =+ ReaderT+ InterpreterEnv+ ( StateT+ InferState+ ( Except+ TypeError+ )+ )+ a++newtype InferState = InferState {count :: Int}++initInfer :: InferState+initInfer = InferState {count = 0}++-------------------------------------------------------------------------------+-- Inference+-------------------------------------------------------------------------------++-- | Run the inference monad+runInfer :: InterpreterEnv -> Infer a -> Either TypeError a+runInfer env m = runExcept $ evalStateT (runReaderT m env) initInfer++-- | Solve for the toplevel type of an expression in a given environment+inferTerm :: InterpreterEnv -> LocSimpleTerm -> Either TypeError Scheme+inferTerm env ex = case runInfer env (infer ex) of+ Left err -> Left err+ Right (ty, ps, ds, cs) -> case runSolve cs of+ Left err -> Left err+ Right subst -> case runInfer env (filterAndCheck (apply subst ps) (apply subst ty)) of+ Left err -> Left err+ Right xs -> Right $ closeOver (map lValue xs) (map lValue ds) $ apply subst ty++-- | Canonicalize and return the polymorphic toplevel type.+closeOver :: [Predicate] -> [Dependency] -> LocType -> Scheme+closeOver ps ds t = normalize $ generalize ps ds t++-- | modified environment where x :: sc+inEnv :: (Name, Scheme) -> Infer a -> Infer a+inEnv (x, sc) m = do+ let scope = insertType x sc+ local scope m++-- | Lookup type in the environment+lookupEnv :: (SrcLoc, Var) -> Infer (Type, [Located Predicate], [Located Dependency])+lookupEnv (pos, x) = do+ env <- ask+ case lookupType x env of+ Nothing -> throwError $ UnboundVariable (Located pos x)+ Just s -> instantiate s pos++letters :: [Text]+letters = map pack $ [1 ..] >>= flip replicateM ['a' .. 'z']++fresh :: Infer Type+fresh = do+ s <- get+ put s {count = count s + 1}+ return $ TypeVar (letters !! count s)++addPos :: SrcLoc -> Predicate -> Located Predicate+addPos pos (IsIn x y) = Located pos $ IsIn x y++instantiate :: Scheme -> SrcLoc -> Infer (Type, [Located Predicate], [Located Dependency])+instantiate (Forall as (Qual ps ds t)) mpos = do+ as' <- mapM (const fresh) as+ let s = Subst $ Map.fromList $ zip as as'+ return (apply s t, map (addPos mpos) $ apply s ps, map (Located mpos) ds)++generalize :: [Predicate] -> [Dependency] -> LocType -> Scheme+generalize ps ds t = Forall as (Qual ps ds (lValue t))+ where+ as = Set.toList $ ftv t++filterAndCheck :: [Located Predicate] -> LocType -> Infer [Located Predicate]+filterAndCheck [] _ = return []+filterAndCheck (p@(Located _ (IsIn _ (TypeVar _))) : ps) t =+ if or $ Set.map (\x -> elem x $ ftv p) (ftv t)+ then (p :) <$> filterAndCheck ps t+ else filterAndCheck ps t+filterAndCheck (p : ps) t = checkInstance p >> filterAndCheck ps t++checkInstance :: Located Predicate -> Infer ()+checkInstance p = do+ (IEnv _ is) <- ask+ (if lValue p `elem` is then return () else throwError $ NoInstance p)++infer :: LocSimpleTerm -> Infer (Located Type, [Located Predicate], [Located Dependency], [Located Constraint])+infer expr = case expr of+ (Located p (SVar x)) -> do+ (t, ps, ds) <- lookupEnv (p, x)+ return (Located p t, ps, ds, [])+ (Located p (SText _)) -> return (Located p textT, [], [], [])+ (Located p (SNum _)) -> return (Located p numberT, [], [], [])+ (Located _ (SBracket s)) -> infer s+ Located p SRest -> do+ tv <- fresh+ return (Located p tv, [], [], [])+ (Located p (SLambda x e)) -> do+ tv <- fresh+ (t, ps, ds, cs) <- inEnv (x, Forall [] (Qual [] [] tv)) (infer e)+ return (Located p $ noLoc tv `TypeArr` t, ps, ds, cs)+ (Located p (SApp e1 e2)) -> do+ (t1, ps1, ds1, c1) <- infer e1+ (t2, ps2, ds2, c2) <- infer e2+ tv <- fresh+ return (noLoc tv, ps1 ++ ps2, ds1 ++ ds2, c1 ++ c2 ++ [Located p (lValue t1, t2 `TypeArr` noLoc tv)])+ (Located p (SInfix e1 op e2)) -> do+ (t1, ps1, ds1, c1) <- infer e1+ (t2, ps2, ds2, c2) <- infer e2+ tv <- fresh+ let u1 = t1 `TypeArr` Located (lLoc e2) (t2 `TypeArr` noLoc tv)+ (u2, p3, d3) <- lookupEnv (lLoc op, lValue op)+ return (noLoc tv, ps1 ++ ps2 ++ p3, ds1 ++ ds2 ++ d3, c1 ++ c2 ++ [Located p (u1, u2)])+ (Located _ (SSeq (x : xs))) -> do+ (t, ps, ds, cs) <- infer x+ infs <- mapM infer xs+ return (t, ps, ds ++ concatMap third4 infs, cs ++ concatMap fourth4 infs ++ [Located (lLoc t') (lValue t, lValue t') | t' <- map first4 infs])+ (Located _ (SStack (x : xs))) -> do+ (t, ps, ds, cs) <- infer x+ infs <- mapM infer xs+ return (t, ps, ds ++ concatMap third4 infs, cs ++ concatMap fourth4 infs ++ [Located (lLoc t') (lValue t, lValue t') | t' <- map first4 infs])+ (Located _ (SChoice _ (x : xs))) -> do+ (t, ps, ds, cs) <- infer x+ infs <- mapM infer xs+ return (t, ps, ds ++ concatMap third4 infs, cs ++ concatMap fourth4 infs ++ [Located (lLoc t') (lValue t, lValue t') | t' <- map first4 infs])+ _ -> throwError NotImplemented+ where+ first4 (x, _, _, _) = x+ third4 (_, _, x, _) = x+ fourth4 (_, _, _, x) = x++normalize :: Scheme -> Scheme+normalize (Forall _ (Qual ps ds body)) = Forall (map snd ord) (Qual (map normpred ps) ds (normtype body))+ where+ ord = zip (nub $ fv body) letters++ fv :: Type -> [TypeVar]+ fv (TypeVar a) = [a]+ fv (TypeArr a b) = fv (lValue a) ++ fv (lValue b)+ fv (TypeCon _) = []++ normtype :: Type -> Type+ normtype (TypeArr a b) = TypeArr (normtype <$> a) (normtype <$> b)+ normtype (TypeCon a) = TypeCon a+ normtype (TypeVar a) =+ case Prelude.lookup a ord of+ Just x -> TypeVar x+ Nothing -> error "type variable not in signature"++ normpred (IsIn n t) = IsIn n (normtype t)
+ src/zwirn-lang/Zwirn/Language/TypeCheck/Types.hs view
@@ -0,0 +1,100 @@+{-# LANGUAGE OverloadedStrings #-}++module Zwirn.Language.TypeCheck.Types where++{-+ Types.hs - defintion of types adapted from+ https://github.com/sdiehl/write-you-a-haskell/tree/master/chapter7/poly_constraints+ Copyright (C) 2023, Martin Gius++ This library is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this library. If not, see <http://www.gnu.org/licenses/>.+-}++import Data.Text (Text)+import Zwirn.Language.Location++type Name = Text++type TypeVar = Text++type LocType = Located Type++data Type+ = TypeVar TypeVar+ | TypeCon Text+ | TypeArr LocType LocType+ deriving (Show, Eq)++newtype Dependency+ = Dep Name+ deriving (Eq, Show)++data Predicate+ = IsIn Name Type+ deriving (Eq, Show)++data Qualified t+ = Qual [Predicate] [Dependency] t+ deriving (Show, Eq)++data Scheme+ = Forall [TypeVar] (Qualified Type)+ deriving (Show, Eq)++type Instance = Predicate++numberT :: Type+numberT = TypeCon "Number"++textT :: Type+textT = TypeCon "Text"++mapT :: Type+mapT = TypeCon "Map"++busT :: Type+busT = TypeCon "Bus"++soundT :: Type+soundT = TypeCon "Sound"++varA :: Type+varA = TypeVar "a"++varB :: Type+varB = TypeVar "b"++varC :: Type+varC = TypeVar "c"++isBasicType :: Scheme -> Bool+isBasicType (Forall _ (Qual _ _ (TypeCon _))) = True+isBasicType (Forall _ (Qual _ _ (TypeVar _))) = True+isBasicType _ = False++unqual :: Type -> Qualified Type+unqual = Qual [] []++schemeToType :: Scheme -> Type+schemeToType (Forall _ (Qual _ _ t)) = t++addDependency :: Text -> Scheme -> Scheme+addDependency x (Forall xs (Qual ps ds t)) = Forall xs (Qual ps (d : ds) t)+ where+ d = Dep x++checkDependency :: Text -> Scheme -> Bool+checkDependency x (Forall _ (Qual _ ds _)) = all check ds+ where+ check (Dep y) = x /= y
+ src/zwirn-lang/Zwirn/Stream/Handshake.hs view
@@ -0,0 +1,29 @@+module Zwirn.Stream.Handshake where++import Control.Concurrent.MVar (MVar, swapMVar)+import Control.Monad (void)+import Data.Maybe (catMaybes, isJust)+import qualified Sound.Osc as O+import qualified Sound.Osc.Transport.Fd.Udp as O+import Zwirn.Stream.Target++-- handshake is in the responsibility of a specific listener implementation+-- these functions can be used to implement it++sendHandshake :: O.Udp -> RemoteAddress -> IO ()+sendHandshake udp = O.sendTo udp (O.Packet_Message $ O.Message "/dirt/handshake" [])++isHandshakeMsg :: O.Message -> Bool+isHandshakeMsg (O.Message "/dirt/hello" _) = True+isHandshakeMsg (O.Message "/dirt/handshake/reply" _) = True+isHandshakeMsg _ = False++actOnHandshake :: O.Message -> O.Udp -> RemoteAddress -> MVar [Int] -> IO ()+actOnHandshake (O.Message "/dirt/hello" _) udp remote _ = sendHandshake udp remote+actOnHandshake (O.Message "/dirt/handshake/reply" xs) _ _ bussesMV = void $ swapMVar bussesMV $ bufferIndices xs+ where+ bufferIndices [] = []+ bufferIndices (x : xs')+ | x == O.AsciiString (O.ascii "&controlBusIndices") = catMaybes $ takeWhile isJust $ map O.datum_integral xs'+ | otherwise = bufferIndices xs'+actOnHandshake _ _ _ _ = return ()
+ src/zwirn-lang/Zwirn/Stream/Listen.hs view
@@ -0,0 +1,48 @@+module Zwirn.Stream.Listen where++import Data.Bifunctor (first)+import qualified Data.Text as T+import Data.Text.Encoding (decodeUtf8, encodeUtf8)+import qualified Network.Socket as N+import Sound.Osc as O+import Sound.Osc.Transport.Fd.Udp as O+import Zwirn.Language.Evaluate (Expression (..))+import Zwirn.Stream.Handshake+import Zwirn.Stream.Types (Stream (..))+import Zwirn.Stream.UI++type RemoteAddress = N.SockAddr++listen :: Stream -> IO ()+listen str = recvMessageFrom (sLocal str) >>= act str >> listen str++recvMessageFrom :: O.Udp -> IO (Maybe Message, RemoteAddress)+recvMessageFrom loc = fmap (first packet_to_message) (recvFrom loc)++act :: Stream -> (Maybe O.Message, RemoteAddress) -> IO ()+act str (Just (Message "/ping" []), remote) = replyOk (sLocal str) remote+act str (Just (Message "/ctrl" [AsciiString key, Double val]), remote) = streamSet str (toUTF8 key) (EZwirn $ pure $ ENum val) >> replyOk (sLocal str) remote+act str (Just (Message "/ctrl" [AsciiString key, Float val]), remote) = streamSet str (toUTF8 key) (EZwirn $ pure $ ENum $ realToFrac val) >> replyOk (sLocal str) remote+act str (Just (Message "/ctrl" [AsciiString key, Int32 val]), remote) = streamSet str (toUTF8 key) (EZwirn $ pure $ ENum $ fromIntegral val) >> replyOk (sLocal str) remote+act str (Just (Message "/ctrl" [AsciiString key, Int64 val]), remote) = streamSet str (toUTF8 key) (EZwirn $ pure $ ENum $ fromIntegral val) >> replyOk (sLocal str) remote+act str (Just (Message "/ctrl" [AsciiString key, AsciiString val]), remote) = streamSet str (toUTF8 key) (EZwirn $ pure $ EText $ toUTF8 val) >> replyOk (sLocal str) remote+act str (Just m, remote) =+ if isHandshakeMsg m+ then actOnHandshake m (sLocal str) remote (sBusses str)+ else replyError (sLocal str) remote ("Unhandeled Message: " ++ show m)+act _ _ = return ()++reply :: O.Udp -> RemoteAddress -> O.Packet -> IO ()+reply loc remote msg = O.sendTo loc msg remote++replyOk :: O.Udp -> RemoteAddress -> IO ()+replyOk loc = flip (reply loc) (O.p_message "/ok" [])++replyError :: O.Udp -> RemoteAddress -> String -> IO ()+replyError loc remote err = reply loc remote (O.p_message "/error" [utf8String err])++utf8String :: String -> O.Datum+utf8String s = O.AsciiString $ encodeUtf8 $ T.pack s++toUTF8 :: O.Ascii -> T.Text+toUTF8 = decodeUtf8
+ src/zwirn-lang/Zwirn/Stream/Process.hs view
@@ -0,0 +1,164 @@+{-# LANGUAGE BangPatterns #-}+{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}++{-# HLINT ignore "Use mapMaybe" #-}++module Zwirn.Stream.Process where++import Control.Concurrent.MVar (MVar, modifyMVar_, readMVar)+import Data.Bifunctor (first)+import Data.List (mapAccumL)+import qualified Data.Map as Map+import Data.Maybe (catMaybes)+import qualified Data.Text as T+import Data.Tuple (swap)+import qualified Sound.Osc as O+import qualified Sound.Osc.Transport.Fd.Udp as O+import Sound.Tidal.Clock+import qualified Sound.Tidal.Clock as Clock+import Sound.Tidal.Link+import Zwirn.Core.Lib.Core (apply)+import Zwirn.Core.Query+import qualified Zwirn.Core.Time as Z+import Zwirn.Language.Evaluate.Expression+import Zwirn.Stream.Target+import Zwirn.Stream.Types++tickAction ::+ MVar PlayMap -> -- maps from channels to expressions+ MVar ActionMap -> -- maps from channels to expressions+ MVar BusMap -> -- maps from busses to expressions+ MVar ExpressionMap -> -- state map+ MVar [Int] -> -- bus mapping+ TargetMap -> -- targets+ O.Udp -> -- local address+ Time -> -- precision+ (Time, Time) -> -- arc of the current tick+ Double -> -- nudge+ ClockConfig -> -- configuration of the clock+ ClockRef -> -- reference to the clock+ (SessionState, SessionState) ->+ IO ()+tickAction zMV actionMapMV busMapMV stMV bussesMV targetMap local prec (star, end) nudge cconf cref (ss, _) = do+ cps <- Clock.getCPS cconf cref+ vs <- processPlayMap prec (star, end) cps zMV stMV+ bs <- processBusMap prec (star, end) busMapMV stMV bussesMV+ processActionMap prec (star, end) cps actionMapMV stMV+ mapM_ (stampAndSend targetMap False local nudge cconf cref ss) vs+ mapM_ (stampAndSend targetMap True local nudge cconf cref ss . (\(Targeted ts (t, m)) -> Targeted ts (t, Just m))) bs++processPlayMap :: Time -> (Time, Time) -> Time -> MVar PlayMap -> MVar ExpressionMap -> IO [Targeted (Z.Time, Maybe (T.Text -> O.Message))]+processPlayMap prec (star, end) cps zMV stMV = do+ pm <- readMVar zMV+ let ps = resolvePlayMap pm+ st <- readMVar stMV++ let (enst, vs) = mapAccumL (\ !s (Targeted ts p) -> swap $ first (Targeted ts) $ findAllValuesWithTimeStatePrec (Z.Time prec 0) (Z.Time (align prec star) 1, Z.Time (align prec end) 1) s p) st ps++ modifyMVar_ stMV (const $ return enst)+ let func (t, ex) = expressionToMessage (fromIntegral (floor t :: Int)) (realToFrac cps) ex >>= \m -> return (t, m)++ concat <$> mapM (\targ -> (\(Targeted ts xs) -> mapM (fmap (Targeted ts) . func) xs) targ) vs++processActionMap :: Time -> (Time, Time) -> Time -> MVar ActionMap -> MVar ExpressionMap -> IO ()+processActionMap prec (star, end) cps zMV stMV = do+ pm <- readMVar zMV+ let ps = Map.elems pm+ st <- readMVar stMV++ let (enst, vs) = mapAccumL (\ !s p -> swap $ findAllValuesWithTimeStatePrec (Z.Time prec 0) (Z.Time (align prec star) 1, Z.Time (align prec end) 1) s p) st ps++ modifyMVar_ stMV (const $ return enst)+ mapM_ (\(t, ex) -> expressionToMessage (fromIntegral (floor t :: Int)) (realToFrac cps) ex >>= \m -> return (t, m)) (concat vs)++processBusMap :: Time -> (Time, Time) -> MVar BusMap -> MVar ExpressionMap -> MVar [Int] -> IO [Targeted (Z.Time, T.Text -> O.Message)]+processBusMap prec (star, end) busMV stMV bussesMV = do+ bm <- readMVar busMV+ let bs = Map.toList bm+ busses <- readMVar bussesMV+ st <- readMVar stMV++ concat <$> mapM (\(i, Targeted ts x) -> map (Targeted ts) <$> busToMessage prec (star, end) busses st (i, x)) bs++busToMessage :: Time -> (Time, Time) -> [Int] -> ExpressionMap -> (Int, Zwirn Expression) -> IO [(Z.Time, T.Text -> O.Message)]+busToMessage prec (star, end) busses st (i, p) = do+ let vs = findAllValuesWithTimePrec (Z.Time prec 0) (Z.Time (align prec star) 1, Z.Time (align prec end) 1) st p++ mapM (\(t, ex) -> busExpressionToMessage (toBus busses i) ex >>= \m -> return (t, m)) vs++toBus :: [Int] -> Int -> Int+toBus [] i = i+toBus xs i = xs !! (i `mod` length xs)++applyFx :: Targeted (PlayState, Zwirn Expression, Maybe (Zwirn (Zwirn Expression -> Zwirn Expression))) -> Targeted (Zwirn Expression)+applyFx (Targeted ts (_, x, Nothing)) = Targeted ts x+applyFx (Targeted ts (_, x, Just fx)) = Targeted ts (apply fx x)++resolvePlayMap :: PlayMap -> [Targeted (Zwirn Expression)]+resolvePlayMap pm = if null ss then map applyFx rs else map applyFx ss+ where+ ps = Map.elems pm+ ss = filter (\(Targeted _ (x, _, _)) -> x == Solo) ps+ rs = filter (\(Targeted _ (x, _, _)) -> x == Normal) ps++align :: Time -> Time -> Time+align prec t = fromIntegral (floor $ t / prec :: Int) * prec++----------------------------------------------------------+-------------- expressions --> osc messages --------------+----------------------------------------------------------++expressionToMessage :: Double -> Double -> Expression -> IO (Maybe (T.Text -> O.Message))+expressionToMessage cyc cps ex = do+ os <- expressionToOSC ex+ let additionalData = [O.string "cps", O.float cps, O.string "cycle", O.float cyc]+ if null os+ then return Nothing+ else return $ Just $ \pat -> O.message (T.unpack pat) (additionalData ++ os)++busExpressionToMessage :: Int -> Expression -> IO (T.Text -> O.Message)+busExpressionToMessage bus ex = do+ os <- expressionToOSC ex+ return $ \path -> O.message (T.unpack path) (O.int32 bus : os)++expressionToOSC :: Expression -> IO [O.Datum]+expressionToOSC (ENum n) = return [O.float n]+expressionToOSC (EText n) = return [O.string $ T.unpack n]+expressionToOSC (EMap m) = concat <$> mapM (\(k, v) -> expressionToOSC v >>= \xs -> return $ O.string (T.unpack k) : xs) (Map.toList m)+expressionToOSC (EAction a) = a >> return []+expressionToOSC _ = return []++----------------------------------------------+-------------- sending messages --------------+----------------------------------------------++defaultLatency :: Double+defaultLatency = 0.2++stampAndSend :: TargetMap -> Bool -> O.Udp -> Double -> ClockConfig -> ClockRef -> SessionState -> Targeted (Z.Time, Maybe (T.Text -> O.Message)) -> IO ()+stampAndSend _ _ _ _ _ _ _ (Targeted _ (_, Nothing)) = return ()+stampAndSend targetMap bus local nudge cconf cref ss (Targeted ts (t, Just msg)) = do+ let onBeat = Clock.cyclesToBeat cconf ((\(Z.Time r _) -> fromRational r :: Double) t)+ let targs = catMaybes $ map (`Map.lookup` targetMap) ts+ let getBusTargets (Target _ path _ (Just addr)) = [(path, addr)]+ getBusTargets (Target _ _ _ Nothing) = []+ let addrs = if bus then concatMap getBusTargets targs else map (\x -> (tOSCPath x, tAddress x)) targs++ on <- Clock.timeAtBeat cconf ss onBeat+ onOSC <- Clock.linkToOscTime cref on++ mapM_ (\(path, addr) -> sendMessage addr local defaultLatency nudge (onOSC, msg path)) addrs++sendMessage :: RemoteAddress -> O.Udp -> Double -> Double -> (Double, O.Message) -> IO ()+sendMessage remote local latency extraLatency (time, m) = sendBndl $ O.Bundle timeWithLatency [m]+ where+ timeWithLatency = time - latency + extraLatency+ sendBndl bndl = O.sendTo local (O.Packet_Bundle bndl) remote++-------------------------------------------------+------------------- utilities -------------------+-------------------------------------------------++updateState :: MVar ExpressionMap -> [ExpressionMap] -> IO ()+updateState _ [] = return ()+updateState stmv (st : _) = modifyMVar_ stmv (const $ return st)
+ src/zwirn-lang/Zwirn/Stream/Target.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE DeriveFunctor #-}++module Zwirn.Stream.Target where++import qualified Data.Map as Map+import Data.Text (Text)+import qualified Network.Socket as N++type RemoteAddress = N.SockAddr++type TargetName = Text++data Targeted a+ = Targeted+ { targets :: [TargetName],+ tValue :: a+ }+ deriving (Functor)++data Target = Target+ { tOSCPath :: Text,+ tBusOSCPath :: Text,+ tAddress :: RemoteAddress,+ tBusAddress :: Maybe RemoteAddress+ }++type TargetMap = Map.Map TargetName Target++data TargetConfig = TargetConfig+ { targetConfigName :: Text,+ targetConfigOSCPath :: Text,+ targetConfigBusOSCPath :: Text,+ targetConfigAddress :: String,+ targetConfigPort :: Int,+ targetConfigBusPort :: Maybe Int+ }++resolve :: String -> Int -> IO N.AddrInfo+resolve host port = do+ let hints = N.defaultHints {N.addrSocketType = N.Stream}+ addr : _ <- N.getAddrInfo (Just hints) (Just host) (Just $ show port)+ return addr++getTarget :: TargetConfig -> IO (Text, Target)+getTarget config = do+ let target_address = targetConfigAddress config+ target_port = targetConfigPort config+ target_bus_port = targetConfigBusPort config+ remote <- resolve target_address target_port+ remoteBus <- mapM (resolve target_address) target_bus_port+ return (targetConfigName config, Target (targetConfigOSCPath config) (targetConfigBusOSCPath config) (N.addrAddress remote) (N.addrAddress <$> remoteBus))++getTargetMap :: [TargetConfig] -> IO TargetMap+getTargetMap confs = Map.fromList <$> mapM getTarget confs
+ src/zwirn-lang/Zwirn/Stream/Types.hs view
@@ -0,0 +1,52 @@+{-# LANGUAGE DeriveGeneric #-}++module Zwirn.Stream.Types where++import Control.Concurrent.MVar (MVar)+import qualified Data.Map as Map+import Data.Text (Text)+import GHC.Generics (Generic)+import qualified Sound.Osc.Transport.Fd.Udp as O+import Sound.Tidal.Clock+import Zwirn.Language.Evaluate.Expression+import Zwirn.Stream.Target++data PlayState+ = Normal+ | Solo+ | Mute+ deriving (Eq, Show)++data Identifier+ = TextID Text+ | NumID Int+ deriving (Eq, Show, Ord)++type PlayMap =+ Map.Map Identifier (Targeted (PlayState, Zwirn Expression, Maybe (Zwirn (Zwirn Expression -> Zwirn Expression))))++type ActionMap = Map.Map Identifier (Zwirn Expression)++type BusMap = Map.Map Int (Targeted (Zwirn Expression))++data StreamConfig = StreamConfig+ { streamConfigTargets :: [TargetConfig],+ streamConfigDefaultTarget :: Text,+ streamConfigLocalPort :: Int,+ streamConfigPrecision :: Rational,+ streamConfigClock :: ClockConfig+ }+ deriving (Generic)++data Stream = Stream+ { sPlayMap :: MVar PlayMap,+ sActionMap :: MVar ActionMap,+ sBusMap :: MVar BusMap,+ sState :: MVar ExpressionMap,+ sBusses :: MVar [Int],+ sTargetMap :: TargetMap,+ sDefaultTarget :: Text,+ sLocal :: O.Udp,+ sClockRef :: ClockRef,+ sConfig :: StreamConfig+ }
+ src/zwirn-lang/Zwirn/Stream/UI.hs view
@@ -0,0 +1,203 @@+module Zwirn.Stream.UI where++import Control.Concurrent (readMVar)+import Control.Concurrent.MVar (modifyMVar_, newMVar)+import qualified Data.Map as Map+import Data.Text (pack)+import qualified Data.Text as T+import qualified Sound.Osc.Transport.Fd.Udp as O+import Sound.Tidal.Clock+import qualified Sound.Tidal.Clock as Clock+import Zwirn.Core.Lib.Core (zipApply)+import Zwirn.Core.Lib.Modulate (shift, slow)+import Zwirn.Core.Lib.Number (firstCyclesThen)+import Zwirn.Core.Lib.Structure (segment)+import qualified Zwirn.Core.Time as Zwirn+import Zwirn.Core.Types (silence, toList, unzwirn, value)+import Zwirn.Language.Evaluate.Expression+import Zwirn.Stream.Process+import Zwirn.Stream.Target+import Zwirn.Stream.Types++streamDefaultBPM :: Double+streamDefaultBPM = 138++streamReplace :: Stream -> Targeted Identifier -> Zwirn Expression -> IO ()+streamReplace _ (Targeted _ (TextID "_all")) _ = return ()+streamReplace _ (Targeted _ (TextID "_none")) _ = return ()+streamReplace str (Targeted ts key) p = modifyMVar_ (sPlayMap str) (return . Map.alter alterFunc key)+ where+ newTargs = if null ts then [sDefaultTarget str] else ts+ filterTargs = filter (\t -> t `elem` Map.keys (sTargetMap str)) newTargs+ alterFunc Nothing = if null filterTargs then Nothing else Just (Targeted filterTargs (Normal, p, Nothing))+ alterFunc (Just (Targeted _ (_, _, fx))) = if null filterTargs then Nothing else Just (Targeted filterTargs (Normal, p, fx))++streamReplaceBus :: Stream -> Targeted Int -> Zwirn Expression -> IO ()+streamReplaceBus str (Targeted ts key) p = modifyMVar_ (sBusMap str) (return . Map.insert key (Targeted filterTargs $ segment (pure 128) p))+ where+ newTargs = if null ts then [sDefaultTarget str] else ts+ filterTargs = filter (\t -> t `elem` Map.keys (sTargetMap str)) newTargs++streamReplaceAction :: Stream -> Identifier -> Zwirn Expression -> IO ()+streamReplaceAction _ (TextID "_all") _ = return ()+streamReplaceAction _ (TextID "_none") _ = return ()+streamReplaceAction str key p = modifyMVar_ (sActionMap str) (return . Map.insert key p)++streamHush :: Stream -> IO ()+streamHush str = do+ modifyMVar_ (sPlayMap str) (return . const Map.empty)+ modifyMVar_ (sActionMap str) (return . const Map.empty)+ modifyMVar_ (sBusMap str) (return . const Map.empty)++streamSet :: Stream -> T.Text -> Expression -> IO ()+streamSet str x ex = modifyMVar_ (sState str) (return . Map.insert x ex)++streamSetFx :: Stream -> Identifier -> Zwirn (Zwirn Expression -> Zwirn Expression) -> IO ()+streamSetFx str (TextID "_all") fx = modifyMVar_ (sPlayMap str) (return . fmap (fmap (\(st, p, _) -> (st, p, Just fx))))+streamSetFx str (TextID "_none") _ = modifyMVar_ (sPlayMap str) (return . fmap (fmap (\(st, p, _) -> (st, p, Nothing))))+streamSetFx str key fx = modifyMVar_ (sPlayMap str) (return . Map.update (\(Targeted ts (st, p, _)) -> Just $ Targeted ts (st, p, Just fx)) key)++streamGet :: Stream -> T.Text -> IO Expression+streamGet str key = do+ sm <- readMVar (sState str)+ return $ Map.findWithDefault (EZwirn silence) key sm++streamToggle :: Stream -> Identifier -> IO ()+streamToggle str key = case key of+ (TextID "_all") -> modifyMVar_ (sPlayMap str) (return . fmap (fmap toggle))+ _ -> modifyMVar_ (sPlayMap str) (return . Map.adjust (fmap toggle) key)+ where+ toggle (Mute, p, fx) = (Normal, p, fx)+ toggle (_, p, fx) = (Mute, p, fx)++streamMute :: Stream -> Identifier -> IO ()+streamMute str key = case key of+ TextID "_all" -> modifyMVar_ (sPlayMap str) (return . fmap (fmap toggle))+ TextID "_none" -> streamUnmute str (TextID "_all")+ _ -> modifyMVar_ (sPlayMap str) (return . Map.adjust (fmap toggle) key)+ where+ toggle (Normal, p, fx) = (Mute, p, fx)+ toggle (Solo, p, fx) = (Mute, p, fx)+ toggle x = x++streamUnmute :: Stream -> Identifier -> IO ()+streamUnmute str key = case key of+ TextID "_all" -> modifyMVar_ (sPlayMap str) (return . fmap (fmap toggle))+ TextID "_none" -> streamMute str (TextID "_all")+ _ -> modifyMVar_ (sPlayMap str) (return . Map.adjust (fmap toggle) key)+ where+ toggle (Mute, p, fx) = (Normal, p, fx)+ toggle x = x++streamSolo :: Stream -> Identifier -> IO ()+streamSolo str key = case key of+ (TextID "_all") -> modifyMVar_ (sPlayMap str) (return . fmap (fmap toggle))+ (TextID "_none") -> streamUnsolo str (TextID "_all")+ _ -> modifyMVar_ (sPlayMap str) (return . Map.adjust (fmap toggle) key)+ where+ toggle (Normal, p, fx) = (Solo, p, fx)+ toggle (Mute, p, fx) = (Solo, p, fx)+ toggle x = x++streamUnsolo :: Stream -> Identifier -> IO ()+streamUnsolo str key = case key of+ (TextID "_all") -> modifyMVar_ (sPlayMap str) (return . fmap (fmap toggle))+ (TextID "_none") -> streamSolo str (TextID "_all")+ _ -> modifyMVar_ (sPlayMap str) (return . Map.adjust (fmap toggle) key)+ where+ toggle (Solo, p, fx) = (Normal, p, fx)+ toggle x = x++streamToggleSolo :: Stream -> Identifier -> IO ()+streamToggleSolo str key = case key of+ (TextID "_all") -> modifyMVar_ (sPlayMap str) (return . fmap (fmap toggle))+ _ -> modifyMVar_ (sPlayMap str) (return . Map.adjust (fmap toggle) key)+ where+ toggle (Solo, p, fx) = (Normal, p, fx)+ toggle (_, p, fx) = (Solo, p, fx)++streamSetCPS :: Stream -> Time -> IO ()+streamSetCPS str c = streamSetBPM str (c * toRational (cBeatsPerCycle (streamConfigClock $ sConfig str) * 60))++-- | set the bpm in the clock and update the tempo variable+streamSetBPM :: Stream -> Time -> IO ()+streamSetBPM s t = streamSet s "tempo" (EZwirn $ pure $ ENum $ realToFrac t) >> Clock.setBPM (sClockRef s) t++-- | read the tempo variable+streamGetBPM :: Stream -> IO Double+streamGetBPM str = do+ st <- readMVar (sState str)+ (EZwirn zt) <- streamGet str "tempo"+ let vs = toList $ unzwirn zt 0 st+ case vs of+ [] -> return streamDefaultBPM+ (v : _) -> case value $ fst v of+ ENum x -> return x+ _ -> return streamDefaultBPM++streamResetCycles :: Stream -> IO ()+streamResetCycles s = streamSetCycle s 0++streamSetCycle :: Stream -> Time -> IO ()+streamSetCycle s = Clock.setClock (sClockRef s)++streamEnableLink :: Stream -> IO ()+streamEnableLink s = Clock.enableLink (sClockRef s)++streamDisableLink :: Stream -> IO ()+streamDisableLink s = Clock.disableLink (sClockRef s)++streamGetNow :: Stream -> IO Time+streamGetNow s = Clock.getCycleTime (streamConfigClock $ sConfig s) (sClockRef s)++streamFirst :: Stream -> Zwirn Expression -> IO ()+streamFirst str = streamFirstTarget str (sDefaultTarget str)++streamFirstTarget :: Stream -> TargetName -> Zwirn Expression -> IO ()+streamFirstTarget str targ z = do+ dummy <- newMVar $ Map.singleton (TextID $ pack "_streamOnceDummy_") (Targeted [targ] (Normal, z, Nothing))+ Clock.clockOnce (tickAction dummy (sActionMap str) (sBusMap str) (sState str) (sBusses str) (sTargetMap str) (sLocal str) (streamConfigPrecision $ sConfig str)) (streamConfigClock $ sConfig str) (sClockRef str)++startStream :: StreamConfig -> IO Stream+startStream config = do+ let targetConfigs = streamConfigTargets config+ conf = (streamConfigClock config) {cFrameTimespan = 10 * realToFrac (streamConfigPrecision config)}+ targetMap <- getTargetMap targetConfigs+ local <- O.udp_server (streamConfigLocalPort config)++ zMV <- newMVar Map.empty+ stMV <- newMVar Map.empty+ busMapMV <- newMVar Map.empty+ actionMapMV <- newMVar Map.empty+ bussesMV <- newMVar []++ cref <- clocked conf (tickAction zMV actionMapMV busMapMV stMV bussesMV targetMap local (streamConfigPrecision config))+ let str = Stream zMV actionMapMV busMapMV stMV bussesMV targetMap "superdirt" local cref config++ -- set the default bpm+ streamSetBPM str (realToFrac streamDefaultBPM)+ return str++evalAction :: Stream -> Zwirn Expression -> IO ()+evalAction str z = do+ st <- readMVar (sState str)+ let exps = toList $ unzwirn z 0 st+ sts = map snd exps+ exs = map (value . fst) exps++ updateState (sState str) sts+ mapM_ evalActionExp exs+ where+ evalActionExp (EAction i) = i+ evalActionExp _ = return ()++transition :: Stream -> Identifier -> (Zwirn Double -> Zwirn (Zwirn Expression -> Zwirn Expression)) -> IO ()+transition str key mapper = do+ now <- realToFrac <$> streamGetNow str+ modifyMVar_ (sPlayMap str) (return . Map.update (\(Targeted ts (ps, ex, fx)) -> Just $ Targeted ts (ps, mapper (pure now) `zipApply` ex, fx)) key)++transition' :: Stream -> Identifier -> Zwirn Zwirn.Time -> Zwirn Expression -> Zwirn Expression -> Zwirn (Zwirn Expression -> Zwirn (Zwirn Expression -> Zwirn Expression)) -> IO ()+transition' str key dur def sig mapper = do+ now <- realToFrac <$> streamGetNow str+ let shifted = shift (pure now) $ firstCyclesThen dur def (slow dur sig)+ modifyMVar_ (sPlayMap str) (return . Map.update (\(Targeted ts (ps, ex, fx)) -> Just $ Targeted ts (ps, mapper `zipApply` ex `zipApply` shifted, fx)) key)
+ test/zwirn-core/Main.hs view
@@ -0,0 +1,173 @@+{-# LANGUAGE MultiParamTypeClasses #-}++import Control.Monad+import Data.Bifunctor (first)+import Data.Functor.Identity+import qualified Data.List as L+import qualified Data.Map as Map+import qualified Data.Ratio as R+import Test.Tasty+import Test.Tasty.HUnit+import Zwirn.Core.Cord as Z+import Zwirn.Core.Core+import Zwirn.Core.Lib.Conditional+import Zwirn.Core.Lib.Cord+import Zwirn.Core.Lib.Core+import Zwirn.Core.Lib.Map+import Zwirn.Core.Lib.Modulate+import Zwirn.Core.Lib.Number+import Zwirn.Core.Lib.Random+import Zwirn.Core.Lib.Structure+import Zwirn.Core.Query+import Zwirn.Core.Time+import Zwirn.Core.Tree (Tree (..))+import Zwirn.Core.Types++main = defaultMain tests++tests :: TestTree+tests = testGroup "Tests" [unitTests]++queryFirst :: Cord () () a -> [(Time, a)]+queryFirst = findAllValuesWithTime (Time 0 1, Time 1 1) ()++queryN :: Rational -> Cord () () a -> [(Time, a)]+queryN n = findAllValuesWithTime (Time 0 1, Time n 1) ()++(@?~) :: (Show a, Eq a) => [(Time, a)] -> [(Time, a)] -> Assertion+(@?~) actual expected = unless (Prelude.and check && length actual == length expected) (assertFailure msg)+ where+ msg = "expected: " ++ show expected ++ "\n but got: " ++ show actual+ check = zipWith (\(a, v1) (b, v2) -> abs (a - b) < 0.01 && v1 == v2) expected actual++-- | should be used for signals+(~@?~) :: (Show a, Eq a, Fractional a, Ord a) => [(Time, a)] -> [(Time, a)] -> Assertion+(~@?~) actual expected = unless (Prelude.and check && length actual == length expected) (assertFailure msg)+ where+ msg = "expected: " ++ show expected ++ "\n but got: " ++ show actual+ check = zipWith (\(a, v1) (b, v2) -> abs (a - b) < 0.01 && (abs (v1 - v2) < 0.01)) expected actual++(%) :: Integer -> Integer -> Time+(%) x y = Time (x R.% y) (0 R.% 1)++simpleZwirn :: Cord () () Int+simpleZwirn = fastcat [pure 1, pure 2, pure 3, pure 4]++nestedZwirn :: Cord () () Int+nestedZwirn = fastcat [pure 10, pure 20, fastcat [pure 30, pure 40]]++veryNested :: Cord () () Int+veryNested = fastcat [simpleZwirn, nestedZwirn]++simpleCord :: Cord () () Int+simpleCord = stack [pure 10, simpleZwirn]++instance State Tree () () where+ beatsPerCycle = pure 8++unitTests =+ testGroup+ "Unit tests"+ [ testCase "pure for Zwirns" $+ queryFirst (pure 1 :: Cord () () Int) @?~ [(0, 1)],+ testCase "simple nesting" $+ queryFirst simpleZwirn @?~ [(0, 1), (1 % 4, 2), (1 % 2, 3), (3 % 4, 4)],+ testCase "more nesting" $+ queryFirst nestedZwirn @?~ [(0, 10), (1 % 3, 20), (2 % 3, 30), (5 % 6, 40)],+ testCase "very nested" $+ queryFirst veryNested @?~ [(0, 1), (1 % 8, 2), (2 % 8, 3), (3 % 8, 4), (1 % 2, 10), (4 % 6, 20), (5 % 6, 30), (11 % 12, 40)],+ testCase "reverse simple Zwirn" $+ queryFirst (rev simpleZwirn) @?~ [(0, 4), (1 % 4, 3), (1 % 2, 2), (3 % 4, 1)],+ testCase "reverse more nesting" $+ queryFirst (rev nestedZwirn) @?~ [(0, 40), (1 % 6, 30), (1 % 3, 20), (2 % 3, 10)],+ testCase "reverse inside" $+ queryFirst (fastcat [pure 100, rev simpleZwirn, pure 200, pure 300]) @?~ [(0, 100), (4 % 16, 4), (5 % 16, 3), (6 % 16, 2), (7 % 16, 1), (1 % 2, 200), (3 % 4, 300)],+ testCase "reverse reverse inside" $+ queryFirst (rev $ fastcat [pure 100, rev simpleZwirn, pure 200, pure 300]) @?~ [(0, 300), (1 % 4, 200), (8 % 16, 1), (9 % 16, 2), (10 % 16, 3), (11 % 16, 4), (3 % 4, 100)],+ testCase "squeezeJoin" $+ queryFirst (squeezeJoin $ fmap (const $ fastcat [pure 10, pure 20 :: Cord () () Int]) simpleZwirn) @?~ [(0, 10), (1 % 8, 20), (2 % 8, 10), (3 % 8, 20), (4 % 8, 10), (5 % 8, 20), (6 % 8, 10), (7 % 8, 20)],+ testCase "ply" $+ queryFirst (ply (pure 2) simpleZwirn) @?~ [(0, 1), (1 / 8, 1), (1 / 4, 2), (3 / 8, 2), (1 / 2, 3), (5 / 8, 3), (3 / 4, 4), (7 / 8, 4)],+ testCase "loop" $+ queryFirst (loop (pure 0.25) (pure 0.75) simpleZwirn) @?~ [(0, 2), (1 / 4, 3), (1 / 2, 2), (3 / 4, 3)],+ testCase "zoom rev" $+ queryFirst (zoom (pure 1) (pure 0) simpleZwirn) @?~ queryFirst (rev simpleZwirn),+ testCase "timeloop" $+ queryFirst (timeloop (pure 0.25) simpleZwirn) @?~ [(0, 1), (1 / 4, 1), (1 / 2, 1), (3 / 4, 1)],+ testCase "cat" $+ queryN 4 (cat (0.25, pure 1) (0.25, pure 2)) @?~ [(0, 1), (1 / 4, 2), (2, 1), (9 / 4, 2)],+ testCase "cyclecat" $+ queryN 3 (cyclecat [(1, pure 10), (2, slow (pure 2) $ pure 20)]) @?~ [(0, 10), (1, 20)],+ testCase "cyclecat 2" $+ queryN 2 (cyclecat [(0.5, pure 10), (1, pure 20), (0.5, pure 30)]) @?~ [(0, 10), (1 / 2, 20), (3 / 2, 30)],+ testCase "fastcyclecat" $+ queryFirst (fastcyclecat [(0.25, pure 1), (0.25, pure 2)]) @?~ [(0, 1), (1 / 4, 2), (1 / 2, 1), (3 / 4, 2)],+ testCase "fastcyclecat2" $+ queryFirst (fastcyclecat [(0.5, pure 1), (0.25, pure 2)]) @?~ [(0, 1), (1 / 2, 2), (3 / 4, 1)],+ testCase "euclid" $+ queryFirst (euclid (pure 3) (pure 8) (pure 1)) @?~ [(0, 1), (3 / 8, 1), (3 / 4, 1)],+ testCase "everyFor" $+ queryFirst (everyFor (pure 1) (pure 0.5) (pure $ fmap succ) simpleZwirn) @?~ [(0, 2), (1 / 4, 3), (1 / 2, 3), (3 / 4, 4)],+ testCase "everyFor 2" $+ queryN 2 (everyFor (pure 0.75) (pure 0.5) (pure $ fmap (const 100)) simpleZwirn) @?~ [(0, 100), (1 / 4, 100), (1 / 2, 3), (3 / 4, 100), (1, 100), (5 / 4, 2), (3 / 2, 100), (7 / 4, 100)],+ testCase "ifthen" $+ queryFirst (ifthen (fastcat [pure True, pure False]) (pure 10) (fastcat [pure 20, pure 30])) @?~ [(0, 10), (1 / 2, 30)],+ testCase "ifthen 2" $+ queryFirst (ifthen (fastcat [pure True, pure False]) simpleZwirn simpleZwirn) @?~ [(0, 1), (1 / 4, 2), (1 / 2, 3), (3 / 4, 4)],+ testCase "while" $+ queryFirst (while (fastcat [pure True, pure False]) (pure $ fmap succ) simpleZwirn) @?~ [(0, 2), (1 / 4, 3), (1 / 2, 3), (3 / 4, 4)],+ testCase "simpleCord" $+ queryFirst simpleCord @?~ [(0, 10), (0, 1), (1 / 4, 2), (1 / 2, 3), (3 / 4, 4)],+ testCase "enum cord" $+ queryFirst (enumFromToStack (pure 0) (pure 4)) @?~ [(0, 0), (0, 1), (0, 2), (0, 3)],+ testCase "zipApply" $+ queryFirst (zipApply (stack [pure $ fmap (+ 10), pure $ fmap (+ 100)]) (stack [fastcat [pure 1, pure 2], pure 3])) @?~ [(0 % 1, 11), (0 % 1, 103), (1 % 2, 12)],+ testCase "sine" $+ queryFirst (segment (pure 4) sine) ~@?~ [(0, 0.5), (1 / 4, 1), (1 / 2, 0.5), (3 / 4, 0)],+ testCase "rev sine" $+ queryFirst (segment (pure 4) $ rev sine) ~@?~ [(0, 0.5), (1 / 4, 0), (1 / 2, 0.5), (3 / 4, 1)],+ testCase "singleton" $+ queryFirst (singleton (pure "n") (fast (pure 2) $ pure 1)) @?~ [(0, Map.singleton "n" 1), (1 / 2, Map.singleton "n" 1)],+ testCase "union" $+ queryFirst (singleton (pure "n") (fast (pure 2) $ pure 1) `union` singleton (pure "s") (pure 1))+ @?~ [ (0, Map.singleton "n" 1 `Map.union` Map.singleton "s" 1),+ (1 / 2, Map.singleton "n" 1 `Map.union` Map.singleton "s" 1)+ ],+ testCase "fix" $+ queryFirst (fix (fastcat [pure "n"]) (pure $ const $ pure 10) (singleton (pure "n") (fast (pure 2) $ pure 1) `union` singleton (pure "s") (pure 1)))+ @?~ [ (0, Map.singleton "n" 10 `Map.union` Map.singleton "s" 1),+ (1 / 2, Map.singleton "n" 10 `Map.union` Map.singleton "s" 1)+ ],+ testCase "fix 2" $+ queryFirst (fix (fastcat [pure "n", pure "s"]) (pure $ const $ pure 10) (singleton (pure "n") (fast (pure 2) $ pure 1) `union` singleton (pure "s") (pure 1)))+ @?~ [ (0, Map.singleton "n" 10 `Map.union` Map.singleton "s" 1),+ (1 / 2, Map.singleton "n" 1 `Map.union` Map.singleton "s" 10)+ ],+ testCase "chunked" $+ queryFirst (chunked (pure "I:") (pure 1))+ @?~ [(0, 1), (1 / 4, 1), (3 / 8, 1), (1 / 2, 1), (3 / 4, 1), (7 / 8, 1)],+ testCase "chunked2" $+ queryFirst (fast (pure $ 5 / 8) $ chunked (pure "vi") (pure 1))+ @?~ [(1 / 5, 1), (2 / 5, 1), (3 / 5, 1), (4 / 5, 1)],+ testCase "chunked3" $+ queryFirst (chunked (pure "i~") (fastcat [pure 1, pure 2]))+ @?~ [(0, 1), (1 / 8, 1), (1 / 4, 1), (1 / 2, 2), (5 / 8, 2), (3 / 4, 2)],+ testCase "chunk" $+ queryFirst (chunk (pure "i~"))+ @?~ [(0, 0), (1 / 8, 1), (1 / 4, 2), (1 / 2, 0), (5 / 8, 1), (3 / 4, 2)],+ testCase "nested chunk" $+ queryFirst (slow (pure 2) $ chunked (pure "I[:]~") (pure 1))+ @?~ [(0, 1), (1 / 2, 1), (5 / 8, 1)],+ testCase "binary" $+ queryFirst (slow (pure 2) $ chunked (binary (pure 5)) (pure 1))+ @?~ [(0, 1), (1 / 2, 1), (3 / 4, 1)],+ testCase "christoffel" $+ queryFirst (chunked (christoffel (pure 3) (pure 5)) (pure 1))+ @?~ [(0, 1), (1 / 8, 1), (3 / 8, 1), (1 / 2, 1), (3 / 4, 1)],+ testCase "silence" $+ queryFirst (fastcat [silence, pure 2, silence, pure 4])+ @?~ [(1 / 4, 2), (3 / 4, 4)],+ testCase "everyBeat" $+ queryFirst (everyBeat (pure 4) (pure $ \x -> x + 1) simpleZwirn)+ @?~ [(0, 2), (1 / 4, 2), (1 / 2, 4), (3 / 4, 4)]+ ]
+ test/zwirn-lang/Main.hs view
@@ -0,0 +1,15 @@+import Control.Monad (unless)+import Test.Tasty+import Test.Tasty.HUnit+import Zwirn.Language.Syntax++main :: IO ()+main = defaultMain tests++tests :: TestTree+tests = testGroup "Tests" [unitTests]++unitTests =+ testGroup+ "Unit tests"+ []
zwirn.cabal view
@@ -1,10 +1,10 @@ cabal-version: 3.0 name: zwirn-version: 0.1.0.0+version: 0.2.2.0 synopsis: a live coding language for playing with nested functions of time description: zwirn is a live coding language for playing with nested functions of time, which trigger the sending of osc-messages. it's syntax is inspired by TidalCycles'- mini-notation and it's API for manipulating patterns.+ mini-notation and its API for manipulating patterns. license: GPL-3.0-only license-file: LICENSE author: Martin Gius@@ -13,16 +13,66 @@ category: Language, Sound build-type: Simple extra-doc-files: README.md-tested-with: GHC == 9.8.2+tested-with: GHC == 9.12.1, GHC == 9.8.2, GHC == 8.10.7 -source-repository this+source-repository head type: git- location: https://github.com/polymorphicengine/zwirn- tag: 0.1.0.0+ location: https://codeberg.org/uzu/zwirn/ +common common-options+ ghc-options: -Wall+ -Wincomplete-uni-patterns+ -Wincomplete-record-updates+ -Wcompat+ -Widentities+ -Wredundant-constraints+ -- -Wmissing-export-lists+ -Wpartial-fields -library- hs-source-dirs: src+ default-language: Haskell2010+ default-extensions: OverloadedStrings++common common-deps+ build-depends: base >= 4.14 && < 4.22,+ mtl >= 2.3 && < 2.4,+ containers >= 0.6.8 && < 0.8,+ text >= 2 && < 2.2,++++library zwirn-core+ import: common-options,+ common-deps+ visibility: public+ hs-source-dirs: src/zwirn-core+ exposed-modules: Zwirn.Core.Time+ Zwirn.Core.Core+ Zwirn.Core.Tree+ Zwirn.Core.Query+ Zwirn.Core.Types+ Zwirn.Core.Cord+ Zwirn.Core.Lib.Cord+ Zwirn.Core.Lib.Core+ Zwirn.Core.Lib.Random+ Zwirn.Core.Lib.Text+ Zwirn.Core.Lib.State+ Zwirn.Core.Lib.Modulate+ Zwirn.Core.Lib.Structure+ Zwirn.Core.Lib.Conditional+ Zwirn.Core.Lib.Number+ Zwirn.Core.Lib.Map+ build-depends: hmt >= 0.20 && < 0.21,+ stm >= 2.5 && < 2.6,+ random >= 1.2 && < 1.4,+ pure-noise >= 0.1.0.1 && < 0.2,++++library zwirn-lang+ import: common-options,+ common-deps+ visibility: public+ hs-source-dirs: src/zwirn-lang exposed-modules: Zwirn.Language.TypeCheck.Types Zwirn.Language.TypeCheck.Constraint Zwirn.Language.TypeCheck.Infer@@ -34,6 +84,8 @@ Zwirn.Language.Simple Zwirn.Language.Pretty Zwirn.Language.Block+ Zwirn.Language.Macro+ Zwirn.Language.Location Zwirn.Language.Builtin.Internal Zwirn.Language.Builtin.Prelude Zwirn.Language.Builtin.Parameters@@ -43,22 +95,127 @@ Zwirn.Language.Evaluate.Expression Zwirn.Language.Evaluate.SKI Zwirn.Language.Evaluate.Internal+ Zwirn.Language.LSP.Hover+ Zwirn.Language.LSP.Eval+ Zwirn.Language.LSP.Diagnostics+ Zwirn.Language.LSP.InlayHints Zwirn.Language- Zwirn.Stream- build-depends: array >= 0.5.6 && < 0.6,- base >= 4.17 && < 4.20,- bytestring >= 0.12.1 && < 0.13,- pretty >= 1.1.3 && < 1.2,- containers >= 0.6.8 && < 0.7,- exceptions >= 0.10.9 && < 0.11,- mtl >= 2.3.1 && < 2.4,- filepath >= 1.5.4 && < 1.6,- hosc >= 0.21.1 && < 0.22,- text >= 2.1.1 && < 2.2,- network >= 3.2.7 && < 3.3,- zwirn-core >= 0.1.1 && < 0.2,- tidal-link >= 1.1 && < 1.2+ Zwirn.Stream.Handshake+ Zwirn.Stream.Process+ Zwirn.Stream.Types+ Zwirn.Stream.UI+ Zwirn.Stream.Listen+ Zwirn.Stream.Target+ other-modules: Paths_zwirn+ autogen-modules: Paths_zwirn++ build-depends: zwirn:zwirn-core,+ array >= 0.5 && < 0.6,+ bytestring >= 0.12.1 && < 0.13,+ prettyprinter >= 1.7 && < 1.8,+ exceptions >= 0.10.9 && < 0.11,+ filepath >= 1.5.4 && < 1.6,+ hosc >= 0.21.1 && < 0.22,+ network >= 3.2.7 && < 3.3,+ tidal-link >= 1.1 && < 1.2,+ build-tool-depends: alex:alex, happy:happy- default-language: Haskell2010- ghc-options: -threaded- -Wall++executable zwirnzi+ import: common-options,+ common-deps+ hs-source-dirs: app/zwirnzi+ main-is: Main.hs+ other-modules: CI.Backend+ CI.Setup+ CI.Config+ LSP.Main+ LSP.Diagnostic+ LSP.Util+ LSP.Handlers.Action+ LSP.Handlers.Command+ LSP.Handlers.Completion+ LSP.Handlers.File+ LSP.Handlers.Hover+ LSP.Handlers.InlayHint+ build-depends: zwirn:zwirn-core,+ zwirn:zwirn-lang,+ tidal-link >= 1.1 && < 1.2,+ bytestring >= 0.12.1 && < 0.13,+ exceptions >= 0.10.9 && < 0.11,+ lsp >= 2.7 && < 2.7.1,+ haskeline >= 0.8.4 && < 0.9,+ file-io >= 0.1.5 && < 0.1.6,+ filepath >= 1.5.4 && < 1.5.5,+ directory >= 1.3.9 && < 1.4,+ conferer >= 1.1 && < 1.2,+ conferer-yaml >= 1.1 && < 1.2,+ utf8-string >= 1.0.2 && < 1.1,+ lens >= 5.3 && < 5.3.6,+ text-rope >= 0.3 && < 0.4,+ aeson >= 2.2.3 && < 2.2.4+ default-language: Haskell2010+ ghc-options: -threaded++executable zwirn-plot+ import: common-options,+ common-deps+ hs-source-dirs: app/zwirn-plot+ main-is: Main.hs+ build-depends: zwirn:zwirn-core,+ easyplot >= 1 && < 1.1+ default-language: Haskell2010+ ghc-options: -threaded++executable zwirn-docs+ import: common-options,+ common-deps+ hs-source-dirs: app/zwirn-docs+ main-is: Main.hs+ build-depends: zwirn:zwirn-lang,+ directory >= 1.3.9 && < 1.4,+ file-io >= 0.1.5 && < 0.1.6,+ filepath >= 1.5.4 && < 1.5.5+ default-language: Haskell2010+ ghc-options: -threaded+++test-suite test-zwirn-core+ type: exitcode-stdio-1.0+ hs-source-dirs: test/zwirn-core/+ main-is: Main.hs+ build-depends: zwirn:zwirn-core,+ base >= 4.14 && < 4.22,+ containers >= 0.6.8 && < 0.8,+ tasty >= 1.5,+ tasty-smallcheck >= 0.8.2,+ tasty-quickcheck >= 0.10.3,+ tasty-hunit >= 0.10.1,+ default-language: Haskell2010+ ghc-options: -threaded++test-suite test-zwirn-lang+ type: exitcode-stdio-1.0+ hs-source-dirs: test/zwirn-lang/+ main-is: Main.hs+ build-depends: zwirn:zwirn-lang,+ base >= 4.14 && < 4.22,+ tasty >= 1.5,+ tasty-smallcheck >= 0.8.2,+ tasty-quickcheck >= 0.10.3,+ tasty-hunit >= 0.10.1,+ default-language: Haskell2010+ ghc-options: -threaded+++benchmark bench-zwirn-core+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs: bench/Core+ build-depends:+ base >= 4.14 && < 4.22,+ criterion >=1.6.4 && < 1.6.5,+ deepseq,+ zwirn:zwirn-core,+ default-language: Haskell2010+ ghc-options: -Wall