├── installers
├── npm
│ ├── .gitignore
│ ├── .npmignore
│ ├── index.js
│ ├── package.json
│ ├── README.md
│ └── bin
│ │ └── elm
├── mac
│ ├── helper-scripts
│ │ ├── elm-startup.sh
│ │ └── uninstall.sh
│ ├── postinstall
│ ├── Resources
│ │ └── en.lproj
│ │ │ ├── background.png
│ │ │ ├── welcome.rtf
│ │ │ └── conclusion.rtf
│ ├── preinstall
│ ├── make-installer.sh
│ └── Distribution.xml
└── win
│ ├── file.ico
│ ├── logo.ico
│ ├── welcome.bmp
│ ├── inst.dat
│ ├── uninst.dat
│ ├── README.md
│ ├── CreateInternetShortcut.nsh
│ ├── make_installer.cmd
│ ├── updatepath.vbs
│ └── removefrompath.vbs
├── ContributorAgreement.pdf
├── .gitignore
├── reactor
├── assets
│ ├── favicon.ico
│ ├── waiting.gif
│ ├── source-code-pro.ttf
│ ├── source-sans-pro.ttf
│ └── styles.css
├── src
│ ├── NotFound.elm
│ ├── Index
│ │ ├── Skeleton.elm
│ │ ├── Navigator.elm
│ │ └── Icon.elm
│ └── Errors.elm
├── elm.json
└── check.py
├── terminal
└── src
│ ├── Diff.hs
│ ├── Bump.hs
│ ├── Publish.hs
│ ├── Install.hs
│ ├── Develop
│ ├── Socket.hs
│ ├── StaticFiles
│ │ └── Build.hs
│ ├── Generate
│ │ ├── Help.hs
│ │ └── Index.hs
│ └── StaticFiles.hs
│ ├── Terminal
│ └── Args
│ │ ├── Internal.hs
│ │ └── Helpers.hs
│ ├── Make.hs
│ └── Init.hs
├── compiler
└── src
│ ├── AST
│ ├── Utils
│ │ ├── Shader.hs
│ │ ├── Binop.hs
│ │ └── Type.hs
│ ├── Valid.hs
│ ├── Source.hs
│ └── Module
│ │ └── Name.hs
│ ├── Elm
│ ├── Compiler
│ │ ├── Version.hs
│ │ ├── Imports.hs
│ │ ├── Module.hs
│ │ ├── Objects.hs
│ │ └── Type.hs
│ ├── Header.hs
│ ├── Compiler.hs
│ └── Interface.hs
│ ├── Data
│ ├── OneOrMore.hs
│ ├── Bag.hs
│ └── Index.hs
│ ├── Reporting
│ ├── Suggest.hs
│ ├── Annotation.hs
│ ├── Error.hs
│ ├── Region.hs
│ ├── Report.hs
│ ├── Result.hs
│ ├── Render
│ │ ├── Type
│ │ │ └── Localizer.hs
│ │ └── Code.hs
│ ├── Warning.hs
│ └── Error
│ │ ├── Main.hs
│ │ └── Docs.hs
│ ├── Parse
│ ├── Parse.hs
│ ├── Primitives
│ │ ├── Shader.hs
│ │ ├── Kernel.hs
│ │ └── Keyword.hs
│ ├── Repl.hs
│ ├── Shader.hs
│ ├── Declaration.hs
│ └── Primitives.hs
│ ├── Type
│ ├── Instantiate.hs
│ ├── Occurs.hs
│ └── UnionFind.hs
│ ├── Generate
│ └── JavaScript
│ │ └── Mode.hs
│ ├── Canonicalize
│ ├── Environment
│ │ └── Dups.hs
│ ├── Type.hs
│ └── Pattern.hs
│ ├── Compile.hs
│ └── Optimize
│ ├── Case.hs
│ └── Names.hs
├── .github
├── ISSUE_TEMPLATE.md
└── CONTRIBUTING.md
├── LICENSE
├── docs
└── elm.json
│ ├── application.md
│ └── package.md
└── README.md
/installers/npm/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
--------------------------------------------------------------------------------
/installers/npm/.npmignore:
--------------------------------------------------------------------------------
1 | README.md
2 | .gitignore
3 | .git
4 |
--------------------------------------------------------------------------------
/installers/mac/helper-scripts/elm-startup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | open 'http://elm-lang.org'
4 |
--------------------------------------------------------------------------------
/ContributorAgreement.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CurrySoftware/compiler/HEAD/ContributorAgreement.pdf
--------------------------------------------------------------------------------
/installers/mac/postinstall:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -ex
4 |
5 | echo "$(date)" > /tmp/elm-installer.log
6 |
--------------------------------------------------------------------------------
/installers/win/file.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CurrySoftware/compiler/HEAD/installers/win/file.ico
--------------------------------------------------------------------------------
/installers/win/logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CurrySoftware/compiler/HEAD/installers/win/logo.ico
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | elm-stuff
2 | dist
3 | cabal-dev
4 | .cabal-sandbox/
5 | cabal.sandbox.config
6 | .DS_Store
7 | *~
8 |
--------------------------------------------------------------------------------
/installers/win/welcome.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CurrySoftware/compiler/HEAD/installers/win/welcome.bmp
--------------------------------------------------------------------------------
/reactor/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CurrySoftware/compiler/HEAD/reactor/assets/favicon.ico
--------------------------------------------------------------------------------
/reactor/assets/waiting.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CurrySoftware/compiler/HEAD/reactor/assets/waiting.gif
--------------------------------------------------------------------------------
/reactor/assets/source-code-pro.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CurrySoftware/compiler/HEAD/reactor/assets/source-code-pro.ttf
--------------------------------------------------------------------------------
/reactor/assets/source-sans-pro.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CurrySoftware/compiler/HEAD/reactor/assets/source-sans-pro.ttf
--------------------------------------------------------------------------------
/installers/mac/Resources/en.lproj/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CurrySoftware/compiler/HEAD/installers/mac/Resources/en.lproj/background.png
--------------------------------------------------------------------------------
/installers/win/inst.dat:
--------------------------------------------------------------------------------
1 | SetOutPath "$INSTDIR\bin"
2 | File "${FILES_SOURCE_PATH}\bin\elm.exe"
3 |
4 | SetOutPath "$INSTDIR"
5 | File "file.ico"
6 | File "updatepath.vbs"
7 | File "removefrompath.vbs"
--------------------------------------------------------------------------------
/installers/win/uninst.dat:
--------------------------------------------------------------------------------
1 | Delete "$INSTDIR\bin\elm.exe"
2 | RmDir "$INSTDIR\bin"
3 |
4 | Delete "$INSTDIR\file.ico"
5 | Delete "$INSTDIR\updatepath.vbs"
6 | Delete "$INSTDIR\removefrompath.vbs"
7 | RmDir "$INSTDIR"
--------------------------------------------------------------------------------
/installers/win/README.md:
--------------------------------------------------------------------------------
1 | # Building Windows installer
2 |
3 | You will need the [NSIS installer](http://nsis.sourceforge.net/Download) to be installed.
4 |
5 | Once everything is installed, run something like this command:
6 |
7 | make_installer.cmd 0.19
8 |
9 | It will build an installer called `Elm-0.19-setup.exe`.
--------------------------------------------------------------------------------
/installers/win/CreateInternetShortcut.nsh:
--------------------------------------------------------------------------------
1 | !macro CreateInternetShortcut FILENAME URL ICONFILE ICONINDEX
2 | WriteINIStr "${FILENAME}.url" "InternetShortcut" "URL" "${URL}"
3 | WriteINIStr "${FILENAME}.url" "InternetShortcut" "IconFile" "${ICONFILE}"
4 | WriteINIStr "${FILENAME}.url" "InternetShortcut" "IconIndex" "${ICONINDEX}"
5 | !macroend
--------------------------------------------------------------------------------
/terminal/src/Diff.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module Diff (run) where
3 |
4 |
5 | import qualified Elm.Diff as Diff
6 | import qualified Reporting.Task as Task
7 | import qualified Reporting.Progress.Terminal as Terminal
8 |
9 |
10 |
11 | -- RUN
12 |
13 |
14 | run :: Diff.Args -> () -> IO ()
15 | run args () =
16 | do reporter <- Terminal.create
17 | Task.run reporter $ Diff.diff args
18 |
--------------------------------------------------------------------------------
/installers/mac/Resources/en.lproj/welcome.rtf:
--------------------------------------------------------------------------------
1 | {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400
2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;}
3 | {\colortbl;\red255\green255\blue255;}
4 | \paperw11900\paperh16840\margl1440\margr1440\vieww10800\viewh8400\viewkind0
5 | \pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural
6 |
7 | \f0\fs28 \cf0 This package will install Elm on your machine.}
--------------------------------------------------------------------------------
/installers/mac/preinstall:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | installdir=/usr/local/bin
6 |
7 | for bin in elm elm-compiler elm-package elm-reactor elm-repl
8 | do
9 | if [ -f $installdir/$bin ]; then
10 | sudo rm -f $installdir/$bin
11 | fi
12 | if [ -f $installdir/$bin-unwrapped ]; then
13 | sudo rm -f $installdir/$bin-unwrapped
14 | fi
15 | done
16 |
17 | sharedir=/usr/local/share/elm
18 | sudo rm -rf $sharedir
19 |
--------------------------------------------------------------------------------
/installers/win/make_installer.cmd:
--------------------------------------------------------------------------------
1 |
2 | set version=%1
3 |
4 | mkdir files
5 | mkdir files\bin
6 |
7 | xcopy ..\..\dist\build\elm\elm.exe files\bin /s /e
8 | xcopy updatepath.vbs files
9 |
10 | if EXIST "%ProgramFiles%\NSIS" (
11 | set nsis="%ProgramFiles%\NSIS\makensis.exe"
12 | ) else (
13 | set nsis="%ProgramFiles(x86)%\NSIS\makensis.exe"
14 | )
15 |
16 | %nsis% /DPLATFORM_VERSION=%version% Nsisfile.nsi
17 |
18 | rd /s /q files
19 |
--------------------------------------------------------------------------------
/terminal/src/Bump.hs:
--------------------------------------------------------------------------------
1 | module Bump (run) where
2 |
3 |
4 | import qualified Elm.Bump as Bump
5 | import qualified Elm.Project as Project
6 | import qualified Reporting.Task as Task
7 | import qualified Reporting.Progress.Terminal as Terminal
8 |
9 |
10 |
11 | -- RUN
12 |
13 |
14 | run :: () -> () -> IO ()
15 | run () () =
16 | do reporter <- Terminal.create
17 | Task.run reporter $
18 | do summary <- Project.getRoot
19 | Bump.bump summary
20 |
--------------------------------------------------------------------------------
/terminal/src/Publish.hs:
--------------------------------------------------------------------------------
1 | module Publish (run) where
2 |
3 |
4 | import qualified Elm.Project as Project
5 | import qualified Elm.Publish as Publish
6 | import qualified Reporting.Task as Task
7 | import qualified Reporting.Progress.Terminal as Terminal
8 |
9 |
10 |
11 | -- RUN
12 |
13 |
14 | run :: () -> () -> IO ()
15 | run () () =
16 | do reporter <- Terminal.create
17 | Task.run reporter $
18 | do summary <- Project.getRoot
19 | Publish.publish summary
20 |
--------------------------------------------------------------------------------
/installers/win/updatepath.vbs:
--------------------------------------------------------------------------------
1 | Set WshShell = CreateObject("WScript.Shell")
2 | elmPath = WScript.Arguments(0)
3 | 'const PathRegKey = "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\Path"
4 | const PathRegKey = "HKCU\Environment\Path"
5 |
6 | on error resume next
7 | path = WshShell.RegRead(PathRegKey)
8 | if err.number <> 0 then
9 | path = ""
10 | end if
11 | on error goto 0
12 |
13 | newPath = elmPath & ";" & path
14 | Call WshShell.RegWrite(PathRegKey, newPath, "REG_EXPAND_SZ")
15 |
--------------------------------------------------------------------------------
/installers/mac/helper-scripts/uninstall.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | echo "Warning: You are about to remove all Elm executables!"
6 |
7 | installdir=/usr/local/bin
8 |
9 | for bin in elm elm-compiler elm-get elm-reactor elm-repl elm-doc elm-server elm-package elm-make
10 | do
11 | if [ -f $installdir/$bin ]; then
12 | sudo rm -f $installdir/$bin
13 | fi
14 | if [ -f $installdir/$bin-unwrapped ]; then
15 | sudo rm -f $installdir/$bin-unwrapped
16 | fi
17 |
18 | done
19 |
20 | sharedir=/usr/local/share/elm
21 | sudo rm -rf $sharedir
22 |
--------------------------------------------------------------------------------
/installers/mac/Resources/en.lproj/conclusion.rtf:
--------------------------------------------------------------------------------
1 | {\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400
2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;}
3 | {\colortbl;\red255\green255\blue255;}
4 | \paperw11900\paperh16840\margl1440\margr1440\vieww10800\viewh8400\viewkind0
5 | \pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural
6 |
7 | \f0\fs26 \cf0 A bunch of useful programs were just placed in /usr/local/bin/\
8 | \
9 | Check out {\field{\*\fldinst HYPERLINK "http://elm-lang.org/Get-Started.elm"}{\fldrslt this tutorial}} to learn how to use them!}
--------------------------------------------------------------------------------
/compiler/src/AST/Utils/Shader.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module AST.Utils.Shader
3 | ( Shader(..)
4 | , Type(..)
5 | )
6 | where
7 |
8 |
9 | import qualified Data.Map as Map
10 | import qualified Elm.Name as N
11 |
12 |
13 |
14 | -- SHADERS
15 |
16 |
17 | data Shader =
18 | Shader
19 | { _attribute :: Map.Map N.Name Type
20 | , _uniform :: Map.Map N.Name Type
21 | , _varying :: Map.Map N.Name Type
22 | }
23 |
24 |
25 |
26 | -- GL TYPES
27 |
28 |
29 | data Type
30 | = Int
31 | | Float
32 | | V2
33 | | V3
34 | | V4
35 | | M4
36 | | Texture
37 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | BEFORE DELETING THIS:
2 |
3 | - Runtime error? https://github.com/elm-lang/elm-compiler/issues/1591
4 | - Type annotations? https://github.com/elm-lang/elm-compiler/issues/1214
5 | - Core libraries? https://github.com/elm-lang/core/issues
6 | - Bad error message? https://github.com/elm-lang/error-message-catalog/issues
7 | - Compiler hangs? https://github.com/elm-lang/elm-compiler/issues/1240
8 | - Parser / code gen problem? https://github.com/elm-lang/elm-compiler/labels/meta
9 |
10 |
11 | If none of that applies, write up a report that satisfies this checklist:
12 |
13 | https://github.com/process-bot/contribution-checklist/blob/master/issues.md
14 |
--------------------------------------------------------------------------------
/compiler/src/Elm/Compiler/Version.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module Elm.Compiler.Version (version) where
3 |
4 | import qualified Data.Version as Version
5 | import qualified Paths_elm
6 | import Elm.Package (Version(Version))
7 |
8 |
9 |
10 | -- VERSION
11 |
12 |
13 | version :: Version
14 | version =
15 | case map fromIntegral (Version.versionBranch Paths_elm.version) of
16 | major : minor : patch : _ ->
17 | Version major minor patch
18 |
19 | [major, minor] ->
20 | Version major minor 0
21 |
22 | [major] ->
23 | Version major 0 0
24 |
25 | [] ->
26 | error "could not detect version of elm-compiler you are using"
27 |
28 |
--------------------------------------------------------------------------------
/reactor/src/NotFound.elm:
--------------------------------------------------------------------------------
1 | module NotFound exposing (main)
2 |
3 |
4 | import Browser
5 | import Html exposing (..)
6 | import Html.Attributes exposing (..)
7 |
8 |
9 |
10 | main : Program () () ()
11 | main =
12 | Browser.document
13 | { init = \_ -> ((), Cmd.none)
14 | , update = \_ _ -> ((), Cmd.none)
15 | , subscriptions = \_ -> Sub.none
16 | , view = \_ -> page
17 | }
18 |
19 |
20 | page : Browser.Document ()
21 | page =
22 | { title = "Page not found"
23 | , body =
24 | [ div [ class "not-found" ]
25 | [ div [ style "font-size" "12em" ] [ text "404" ]
26 | , div [ style "font-size" "3em" ] [ text "Page not found" ]
27 | ]
28 | ]
29 | }
--------------------------------------------------------------------------------
/installers/npm/index.js:
--------------------------------------------------------------------------------
1 | var binwrap = require("binwrap");
2 | var path = require("path");
3 |
4 | var packageInfo = require(path.join(__dirname, "package.json"));
5 | // Use major.minor.patch from version string - e.g. "1.2.3" from "1.2.3-alpha"
6 | var binVersion = packageInfo.version.replace(/^(\d+\.\d+\.\d+).*$/, "$1");
7 |
8 | var root =
9 | "https://github.com/elm/compiler/releases/download/" +
10 | binVersion +
11 | "/binaries-for-";
12 |
13 | module.exports = binwrap({
14 | binaries: ["elm"],
15 | urls: {
16 | "darwin-x64": root + "mac.tar.gz",
17 | "win32-x64": root + "windows.tar.gz",
18 | "win32-ia32": root + "windows.tar.gz",
19 | "linux-x64": root + "linux.tar.gz"
20 | }
21 | });
22 |
--------------------------------------------------------------------------------
/installers/win/removefrompath.vbs:
--------------------------------------------------------------------------------
1 | Set WshShell = CreateObject("WScript.Shell")
2 | ' Make sure there is no trailing slash at the end of elmBasePath
3 | elmBasePath = WScript.Arguments(0)
4 | 'const PathRegKey = "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\Path"
5 | const PathRegKey = "HKCU\Environment\Path"
6 |
7 | on error resume next
8 | path = WshShell.RegRead(PathRegKey)
9 | if err.number = 0 then
10 | Set regEx = New RegExp
11 | elmBasePath = Replace(Replace(Replace(elmBasePath, "\", "\\"), "(", "\("), ")", "\)")
12 | regEx.Pattern = elmBasePath & "\\\d+\.\d+(\.\d+|)\\bin(;|)"
13 | regEx.Global = True
14 | newPath = regEx.Replace(path, "")
15 | Call WshShell.RegWrite(PathRegKey, newPath, "REG_EXPAND_SZ")
16 | end if
17 | on error goto 0
18 |
--------------------------------------------------------------------------------
/reactor/elm.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "application",
3 | "source-directories": [
4 | "src"
5 | ],
6 | "elm-version": "0.19.0",
7 | "dependencies": {
8 | "direct": {
9 | "elm/browser": "1.0.0",
10 | "elm/core": "1.0.0",
11 | "elm/html": "1.0.0",
12 | "elm/http": "1.0.0",
13 | "elm/json": "1.0.0",
14 | "elm/project-metadata-utils": "1.0.0",
15 | "elm/svg": "1.0.0",
16 | "elm-explorations/markdown": "1.0.0"
17 | },
18 | "indirect": {
19 | "elm/parser": "1.0.0",
20 | "elm/time": "1.0.0",
21 | "elm/url": "1.0.0",
22 | "elm/virtual-dom": "1.0.0"
23 | }
24 | },
25 | "test-dependencies": {
26 | "direct": {},
27 | "indirect": {}
28 | }
29 | }
--------------------------------------------------------------------------------
/terminal/src/Install.hs:
--------------------------------------------------------------------------------
1 | module Install
2 | ( Args(..)
3 | , run
4 | )
5 | where
6 |
7 |
8 | import Control.Monad.Trans (liftIO)
9 |
10 | import qualified Elm.Install as Install
11 | import qualified Elm.PerUserCache as PerUserCache
12 | import qualified Elm.Package as Pkg
13 | import qualified Reporting.Exit as Exit
14 | import qualified Reporting.Exit.Install as E
15 | import qualified Reporting.Task as Task
16 | import qualified Reporting.Progress.Terminal as Terminal
17 |
18 |
19 |
20 | -- RUN
21 |
22 |
23 | data Args
24 | = NoArgs
25 | | Install Pkg.Name
26 |
27 |
28 | run :: Args -> () -> IO ()
29 | run args () =
30 | do reporter <- Terminal.create
31 | Task.run reporter $
32 | case args of
33 | NoArgs ->
34 | do elmHome <- liftIO PerUserCache.getElmHome
35 | Task.throw (Exit.Install (E.NoArgs elmHome))
36 |
37 | Install pkg ->
38 | Install.install pkg
39 |
--------------------------------------------------------------------------------
/compiler/src/AST/Utils/Binop.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module AST.Utils.Binop
3 | ( Precedence(..)
4 | , Associativity(..)
5 | )
6 | where
7 |
8 |
9 | import Prelude hiding (Either(..))
10 | import Control.Monad (liftM)
11 | import Data.Binary
12 |
13 |
14 |
15 | -- BINOP STUFF
16 |
17 |
18 | newtype Precedence = Precedence Int
19 | deriving (Eq, Ord)
20 |
21 |
22 | data Associativity
23 | = Left
24 | | Non
25 | | Right
26 | deriving (Eq)
27 |
28 |
29 |
30 | -- BINARY
31 |
32 |
33 | instance Binary Precedence where
34 | get =
35 | liftM Precedence get
36 |
37 | put (Precedence n) =
38 | put n
39 |
40 |
41 | instance Binary Associativity where
42 | get =
43 | do n <- getWord8
44 | return $
45 | case n of
46 | 0 -> Left
47 | 1 -> Non
48 | 2 -> Right
49 | _ -> error "Error reading valid associativity from serialized string"
50 |
51 | put assoc =
52 | putWord8 $
53 | case assoc of
54 | Left -> 0
55 | Non -> 1
56 | Right -> 2
57 |
--------------------------------------------------------------------------------
/compiler/src/Data/OneOrMore.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module Data.OneOrMore
3 | ( OneOrMore(..)
4 | , one
5 | , more
6 | , toList
7 | , map
8 | )
9 | where
10 |
11 |
12 | import Prelude hiding (map)
13 |
14 |
15 |
16 | -- ONE OR MORE
17 |
18 |
19 | data OneOrMore a
20 | = One a
21 | | More (OneOrMore a) (OneOrMore a)
22 |
23 |
24 | one :: a -> OneOrMore a
25 | one =
26 | One
27 |
28 |
29 | more :: OneOrMore a -> OneOrMore a -> OneOrMore a
30 | more =
31 | More
32 |
33 |
34 |
35 | -- TO LIST
36 |
37 |
38 | toList :: OneOrMore a -> [a]
39 | toList oneOrMore =
40 | toListHelp oneOrMore []
41 |
42 |
43 | toListHelp :: OneOrMore a -> [a] -> [a]
44 | toListHelp oneOrMore list =
45 | case oneOrMore of
46 | One x ->
47 | x : list
48 |
49 | More a b ->
50 | toListHelp a (toListHelp b list)
51 |
52 |
53 |
54 | -- MAP
55 |
56 |
57 | map :: (a -> b) -> OneOrMore a -> OneOrMore b
58 | map func oneOrMore =
59 | case oneOrMore of
60 | One value ->
61 | One (func value)
62 |
63 | More left right ->
64 | More (map func left) (map func right)
65 |
--------------------------------------------------------------------------------
/compiler/src/Reporting/Suggest.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Reporting.Suggest
4 | ( distance
5 | , sort
6 | , rank
7 | )
8 | where
9 |
10 |
11 | import qualified Data.Char as Char
12 | import qualified Data.List as List
13 | import qualified Text.EditDistance as Dist
14 |
15 |
16 |
17 | -- DISTANCE
18 |
19 |
20 | distance :: String -> String -> Int
21 | distance x y =
22 | Dist.restrictedDamerauLevenshteinDistance Dist.defaultEditCosts x y
23 |
24 |
25 |
26 | -- SORT
27 |
28 |
29 | sort :: String -> (a -> String) -> [a] -> [a]
30 | sort target toString values =
31 | List.sortOn (distance (toLower target) . toLower . toString) values
32 |
33 |
34 | toLower :: String -> String
35 | toLower string =
36 | map Char.toLower string
37 |
38 |
39 |
40 | -- RANK
41 |
42 |
43 | rank :: String -> (a -> String) -> [a] -> [(Int,a)]
44 | rank target toString values =
45 | let
46 | toRank v =
47 | distance (toLower target) (toLower (toString v))
48 |
49 | addRank v =
50 | (toRank v, v)
51 | in
52 | List.sortOn fst (map addRank values)
53 |
--------------------------------------------------------------------------------
/installers/npm/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "elm",
3 | "version": "0.19.0-bugfix2",
4 | "description": "The Elm Platform: Binaries for the Elm programming language.",
5 | "main": "index.js",
6 | "preferGlobal": true,
7 | "license": "BSD-3-Clause",
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/elm/compiler.git"
11 | },
12 | "homepage": "https://github.com/elm/compiler/tree/master/installers/npm",
13 | "bugs": "https://github.com/elm/compiler/issues",
14 | "author": {
15 | "name": "Richard Feldman",
16 | "email": "richard.t.feldman@gmail.com",
17 | "url": "https://github.com/rtfeldman"
18 | },
19 | "engines": {
20 | "node": ">=4.0.0"
21 | },
22 | "scripts": {
23 | "install": "binwrap-install",
24 | "prepublishOnly": "binwrap-test"
25 | },
26 | "files": [
27 | "index.js",
28 | "bin",
29 | "bin/elm"
30 | ],
31 | "keywords": [
32 | "bin",
33 | "binary",
34 | "binaries",
35 | "elm",
36 | "platform"
37 | ],
38 | "bin": {
39 | "elm": "bin/elm"
40 | },
41 | "dependencies": {
42 | "binwrap": "0.1.4"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/installers/mac/make-installer.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # Run the following command to create an installer:
3 | #
4 | # bash make-installer.sh
5 | #
6 |
7 |
8 |
9 | #### SETUP ####
10 |
11 | set -e
12 |
13 | # Create directory structure for new pkgs
14 | pkg_root=$(mktemp -d -t package-artifacts)
15 | pkg_binaries=$pkg_root
16 | pkg_scripts=$pkg_root/Scripts
17 |
18 | mkdir -p $pkg_binaries
19 | mkdir -p $pkg_scripts
20 |
21 | usr_binaries=/usr/local/bin
22 |
23 |
24 | #### BUILD ASSETS ####
25 |
26 | cp ../../dist/build/elm/elm $pkg_binaries/elm
27 |
28 | cp $(pwd)/preinstall $pkg_scripts
29 | cp $(pwd)/postinstall $pkg_scripts
30 |
31 | pkgbuild \
32 | --identifier org.elm-lang.binaries.pkg \
33 | --install-location $usr_binaries \
34 | --scripts $pkg_scripts \
35 | --filter 'Scripts.*' \
36 | --root $pkg_root \
37 | binaries.pkg
38 |
39 |
40 | #### BUNDLE ASSETS ####
41 |
42 | rm -f Elm.pkg
43 |
44 | productbuild \
45 | --distribution Distribution.xml \
46 | --package-path . \
47 | --resources Resources \
48 | Elm.pkg
49 |
50 |
51 | #### CLEAN UP ####
52 |
53 | rm binaries.pkg
54 | rm -rf $pkg_root
55 |
--------------------------------------------------------------------------------
/compiler/src/Reporting/Annotation.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module Reporting.Annotation
3 | ( Located(..)
4 | , at, merge
5 | , map
6 | , toValue
7 | , toRegion
8 | , traverse
9 | )
10 | where
11 |
12 |
13 | import Prelude hiding (map, traverse)
14 | import qualified Reporting.Region as R
15 |
16 |
17 |
18 | -- ANNOTATION
19 |
20 |
21 | data Located a =
22 | At R.Region a
23 |
24 |
25 |
26 | -- CREATE
27 |
28 |
29 | at :: R.Position -> R.Position -> a -> Located a
30 | at start end value =
31 | At (R.Region start end) value
32 |
33 |
34 | merge :: Located a -> Located b -> value -> Located value
35 | merge (At region1 _) (At region2 _) value =
36 | At (R.merge region1 region2) value
37 |
38 |
39 |
40 | -- MANIPULATE
41 |
42 |
43 | map :: (a -> b) -> Located a -> Located b
44 | map f (At info value) =
45 | At info (f value)
46 |
47 |
48 | toValue :: Located a -> a
49 | toValue (At _ value) =
50 | value
51 |
52 |
53 | toRegion :: Located a -> R.Region
54 | toRegion (At region _) =
55 | region
56 |
57 |
58 | traverse :: (Functor f) => (a -> f b) -> Located a -> f (Located b)
59 | traverse func (At region value) =
60 | At region <$> func value
61 |
--------------------------------------------------------------------------------
/terminal/src/Develop/Socket.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Develop.Socket (watchFile) where
4 |
5 | import Control.Concurrent (forkIO, threadDelay)
6 | import Control.Exception (SomeException, catch)
7 | import qualified Data.ByteString.Char8 as BS
8 | import qualified Network.WebSockets as WS
9 | import qualified System.FSNotify.Devel as Notify
10 | import qualified System.FSNotify as Notify
11 |
12 |
13 |
14 | watchFile :: FilePath -> WS.PendingConnection -> IO ()
15 | watchFile watchedFile pendingConnection =
16 | do connection <- WS.acceptRequest pendingConnection
17 |
18 | Notify.withManager $ \mgmt ->
19 | do stop <- Notify.treeExtAny mgmt "." ".elm" print
20 | tend connection
21 | stop
22 |
23 |
24 | tend :: WS.Connection -> IO ()
25 | tend connection =
26 | let
27 | pinger :: Integer -> IO a
28 | pinger n =
29 | do threadDelay (5 * 1000 * 1000)
30 | WS.sendPing connection (BS.pack (show n))
31 | pinger (n + 1)
32 |
33 | receiver :: IO ()
34 | receiver =
35 | do _ <- WS.receiveDataMessage connection
36 | receiver
37 |
38 | shutdown :: SomeException -> IO ()
39 | shutdown _ =
40 | return ()
41 | in
42 | do _pid <- forkIO (receiver `catch` shutdown)
43 | pinger 1 `catch` shutdown
44 |
--------------------------------------------------------------------------------
/reactor/check.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import os
4 | import sys
5 |
6 |
7 | ## FIGURE OUT NEW MODIFICATION TIME
8 |
9 | def mostRecentModification(directory):
10 | mostRecent = 0
11 |
12 | for dirpath, dirs, files in os.walk(directory):
13 | for f in files:
14 | lastModified = os.path.getmtime(dirpath + '/' + f)
15 | mostRecent = max(int(lastModified), mostRecent)
16 |
17 | return mostRecent
18 |
19 |
20 | srcTime = mostRecentModification('ui/src')
21 | assetTime = mostRecentModification('ui/assets')
22 | mostRecent = max(srcTime, assetTime)
23 |
24 |
25 | ## FIGURE OUT OLD MODIFICATION TIME
26 |
27 | with open('ui/last-modified', 'a') as handle:
28 | pass
29 |
30 |
31 | prevMostRecent = 0
32 |
33 |
34 | with open('ui/last-modified', 'r+') as handle:
35 | line = handle.read()
36 | prevMostRecent = int(line) if line else 0
37 |
38 |
39 | ## TOUCH FILES IF NECESSARY
40 |
41 | if mostRecent > prevMostRecent:
42 | print "+------------------------------------------------------------+"
43 | print "| Some ui/ code changed. Touching src/Reactor/StaticFiles.hs |"
44 | print "| to trigger a recompilation of the Template Haskell stuff. |"
45 | print "+------------------------------------------------------------+"
46 | os.utime('src/Reactor/StaticFiles.hs', None)
47 | with open('ui/last-modified', 'w') as handle:
48 | handle.write(str(mostRecent))
49 |
--------------------------------------------------------------------------------
/reactor/src/Index/Skeleton.elm:
--------------------------------------------------------------------------------
1 | module Index.Skeleton exposing
2 | ( box
3 | , readmeBox
4 | )
5 |
6 | import Html exposing (..)
7 | import Html.Attributes exposing (..)
8 | import Markdown
9 |
10 | import Index.Icon as Icon
11 |
12 |
13 |
14 | -- VIEW BOXES
15 |
16 |
17 | type alias BoxArgs msg =
18 | { title : String
19 | , items : List (List (Html msg))
20 | , footer : Maybe (String, String)
21 | }
22 |
23 |
24 | box : BoxArgs msg -> Html msg
25 | box { title, items, footer } =
26 | let
27 | realItems =
28 | List.map (div [ class "box-item" ]) items
29 | in
30 | boxHelp title realItems footer
31 |
32 |
33 | readmeBox : String -> Html msg
34 | readmeBox markdown =
35 | let
36 | readme =
37 | Markdown.toHtml [ class "box-item" ] markdown
38 | in
39 | boxHelp "README" [readme] Nothing
40 |
41 |
42 | boxHelp : String -> List (Html msg) -> Maybe (String, String) -> Html msg
43 | boxHelp boxTitle items footer =
44 | div [ class "box" ] <|
45 | div [ class "box-header" ] [ text boxTitle ]
46 | :: items
47 | ++ [ boxFooter footer ]
48 |
49 |
50 | boxFooter : Maybe (String, String) -> Html msg
51 | boxFooter maybeFooter =
52 | case maybeFooter of
53 | Nothing ->
54 | text ""
55 |
56 | Just (path, description) ->
57 | a [ href path
58 | , title description
59 | ]
60 | [ div [ class "box-footer" ] [ Icon.plus ]
61 | ]
62 |
--------------------------------------------------------------------------------
/installers/mac/Distribution.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Elm Platform
4 |
5 |
6 |
7 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | binaries.pkg
31 |
32 |
--------------------------------------------------------------------------------
/terminal/src/Develop/StaticFiles/Build.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | module Develop.StaticFiles.Build
3 | ( readAsset
4 | , compile
5 | )
6 | where
7 |
8 |
9 | import qualified Data.ByteString as BS
10 | import qualified System.Directory as Dir
11 | import System.FilePath ((>))
12 |
13 | import qualified Elm.Project as Project
14 | import qualified Generate.Output as Output
15 | import qualified Reporting.Task as Task
16 | import qualified Reporting.Progress.Terminal as Terminal
17 |
18 |
19 |
20 | -- ASSETS
21 |
22 |
23 | readAsset :: FilePath -> IO BS.ByteString
24 | readAsset path =
25 | BS.readFile ("reactor" > "assets" > path)
26 |
27 |
28 |
29 | -- COMPILE
30 |
31 |
32 | compile :: IO BS.ByteString
33 | compile =
34 | Dir.withCurrentDirectory "reactor" $
35 | do reporter <- Terminal.create
36 | Task.run reporter $
37 | do summary <- Project.getRoot
38 | let jsOutput = Just (Output.JavaScript Nothing tempFileName)
39 | Project.compile Output.Prod Output.Client jsOutput Nothing summary rootPaths
40 |
41 | result <- BS.readFile tempFileName
42 | seq (BS.length result) (Dir.removeFile tempFileName)
43 | return result
44 |
45 |
46 | tempFileName :: FilePath
47 | tempFileName =
48 | "elm.js"
49 |
50 |
51 | rootPaths :: [FilePath]
52 | rootPaths =
53 | [ "src" > "Errors.elm"
54 | , "src" > "Index.elm"
55 | , "src" > "NotFound.elm"
56 | ]
57 |
--------------------------------------------------------------------------------
/installers/npm/README.md:
--------------------------------------------------------------------------------
1 | npm install elm [](http://travis-ci.org/elm-lang/elm-platform) [](https://ci.appveyor.com/project/rtfeldman/elm-platform/branch/master)
2 | ===============
3 |
4 | Install the [Elm Platform](https://github.com/elm-lang/elm-platform) via [`npm`](https://www.npmjs.com).
5 |
6 | ## Installing
7 |
8 | Run this to get the binaries:
9 |
10 | ```
11 | $ npm install -g elm
12 | ```
13 |
14 | ## Installing behind a proxy server
15 |
16 | If you are behind a proxy server, set the environment variable "HTTPS_PROXY".
17 |
18 | ```
19 | $ export HTTPS_PROXY=$YourProxyServer$
20 | $ npm install -g elm
21 | ```
22 |
23 | Or on Windows:
24 |
25 | ```
26 | $ set HTTPS_PROXY=$YourProxyServer$
27 | $ npm install -g elm
28 | ```
29 |
30 | ## Troubleshooting
31 |
32 | 1. [Troubleshooting npm](https://github.com/npm/npm/wiki/Troubleshooting)
33 | 2. On Debian/Ubuntu systems, you may have to install the nodejs-legacy package: `apt-get install nodejs-legacy`.
34 | 3. If the installer says that it cannot find any usable binaries for your operating system and architecture, check the [Build from Source](https://github.com/elm-lang/elm-platform/blob/master/README.md#build-from-source) documentation.
35 |
36 | ## Getting Started
37 |
38 | Once everything has installed successfully, head over to the [Get Started](http://elm-lang.org/Get-Started.elm) page!
39 |
--------------------------------------------------------------------------------
/reactor/src/Index/Navigator.elm:
--------------------------------------------------------------------------------
1 | module Index.Navigator exposing (view)
2 |
3 |
4 | import Html exposing (..)
5 | import Html.Attributes exposing (..)
6 | import Index.Icon as Icon
7 |
8 |
9 |
10 | -- VIEW
11 |
12 |
13 | view : String -> List String -> Html msg
14 | view root dirs =
15 | div
16 | [ style "font-size" "2em"
17 | , style "padding" "20px 0"
18 | , style "display" "flex"
19 | , style "align-items" "center"
20 | , style "height" "40px"
21 | ]
22 | (makeLinks root dirs "/" [])
23 |
24 |
25 | makeLinks : String -> List String -> String -> List (Html msg) -> List (Html msg)
26 | makeLinks root dirs oldPath revAnchors =
27 | case dirs of
28 | dir :: otherDirs ->
29 | let
30 | newPath =
31 | oldPath ++ "/" ++ dir
32 |
33 | anchor =
34 | a [ href newPath ] [ text dir ]
35 | in
36 | makeLinks root otherDirs newPath (anchor :: revAnchors)
37 |
38 | [] ->
39 | let
40 | home =
41 | a [ href "/"
42 | , title root
43 | , style "display" "inherit"
44 | ]
45 | [ Icon.home
46 | ]
47 | in
48 | case revAnchors of
49 | [] ->
50 | [home]
51 |
52 | lastAnchor :: otherRevAnchors ->
53 | home :: slash :: List.foldl addSlash [lastAnchor] otherRevAnchors
54 |
55 |
56 | addSlash : Html msg -> List (Html msg) -> List (Html msg)
57 | addSlash front back =
58 | front :: slash :: back
59 |
60 |
61 | slash : Html msg
62 | slash =
63 | span [ style "padding" "0 8px" ] [ text "/" ]
64 |
--------------------------------------------------------------------------------
/compiler/src/Elm/Header.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module Elm.Header
3 | ( Tag(..)
4 | , parse
5 | )
6 | where
7 |
8 |
9 | import qualified Data.ByteString as B
10 |
11 | import qualified AST.Source as Src
12 | import qualified Elm.Compiler.Module as M
13 | import qualified Elm.Name as N
14 | import qualified Elm.Package as Pkg
15 | import qualified Parse.Primitives as Parser
16 | import qualified Parse.Module as Module
17 | import qualified Reporting.Annotation as A
18 | import qualified Reporting.Error as Error
19 |
20 |
21 |
22 | -- HEADER TAGS
23 |
24 |
25 | data Tag = Normal | Effect | Port
26 |
27 |
28 |
29 | -- PARSE
30 |
31 |
32 | parse :: Pkg.Name -> B.ByteString -> Either Error.Error (Maybe (Tag, M.Raw), [M.Raw])
33 | parse pkgName sourceCode =
34 | let
35 | headerParser =
36 | Module.module_ pkgName (return ())
37 | in
38 | case Parser.run headerParser sourceCode of
39 | Right (Src.Module header imports _) ->
40 | Right
41 | ( fmap simplifyHeader header
42 | , map getName imports
43 | )
44 |
45 | Left err ->
46 | Left (Error.Syntax err)
47 |
48 |
49 | getName :: Src.Import -> N.Name
50 | getName (Src.Import (A.At _ name) _ _) =
51 | name
52 |
53 |
54 |
55 | -- TO HEADER
56 |
57 |
58 | simplifyHeader :: Src.Header -> (Tag, N.Name)
59 | simplifyHeader (Src.Header name effects _ _) =
60 | ( toTag effects, name )
61 |
62 |
63 | toTag :: Src.Effects -> Tag
64 | toTag effects =
65 | case effects of
66 | Src.NoEffects ->
67 | Normal
68 |
69 | Src.Ports _ ->
70 | Port
71 |
72 | Src.Manager _ _ ->
73 | Effect
74 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012-present, Evan Czaplicki
2 |
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 |
11 | * Redistributions in binary form must reproduce the above
12 | copyright notice, this list of conditions and the following
13 | disclaimer in the documentation and/or other materials provided
14 | with the distribution.
15 |
16 | * Neither the name of Evan Czaplicki nor the names of other
17 | contributors may be used to endorse or promote products derived
18 | from this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 |
--------------------------------------------------------------------------------
/compiler/src/Reporting/Error.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Reporting.Error
4 | ( Error(..)
5 | , toReports
6 | )
7 | where
8 |
9 |
10 | import qualified Reporting.Error.Canonicalize as Canonicalize
11 | import qualified Reporting.Error.Docs as Docs
12 | import qualified Reporting.Error.Main as Main
13 | import qualified Reporting.Error.Pattern as Pattern
14 | import qualified Reporting.Error.Syntax as Syntax
15 | import qualified Reporting.Error.Type as Type
16 | import qualified Reporting.Render.Code as Code
17 | import qualified Reporting.Render.Type.Localizer as L
18 | import qualified Reporting.Report as Report
19 |
20 |
21 |
22 | -- ALL POSSIBLE ERRORS
23 |
24 |
25 | data Error
26 | = Syntax Syntax.Error
27 | | Canonicalize Canonicalize.Error
28 | | Type L.Localizer [Type.Error]
29 | | Main L.Localizer Main.Error
30 | | Pattern [Pattern.Error]
31 | | Docs Docs.Error
32 |
33 |
34 |
35 | -- TO REPORT
36 |
37 |
38 | toReports :: Code.Source -> Error -> [Report.Report]
39 | toReports source err =
40 | case err of
41 | Syntax syntaxError ->
42 | [Syntax.toReport source syntaxError]
43 |
44 | Canonicalize canonicalizeError ->
45 | [Canonicalize.toReport source canonicalizeError]
46 |
47 | Type localizer typeErrors ->
48 | map (Type.toReport source localizer) typeErrors
49 |
50 | Main localizer mainError ->
51 | [Main.toReport localizer source mainError]
52 |
53 | Pattern patternErrors ->
54 | map (Pattern.toReport source) patternErrors
55 |
56 | Docs docsError ->
57 | [Docs.toReport source docsError]
58 |
--------------------------------------------------------------------------------
/compiler/src/Parse/Parse.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module Parse.Parse
3 | ( program
4 | )
5 | where
6 |
7 |
8 | import qualified Data.ByteString as B
9 |
10 | import qualified AST.Source as Src
11 | import qualified AST.Valid as Valid
12 | import qualified Elm.Package as Pkg
13 | import qualified Parse.Declaration as Decl
14 | import qualified Parse.Module as Module
15 | import qualified Parse.Primitives as P
16 | import qualified Reporting.Error.Syntax as Error
17 | import qualified Reporting.Result as Result
18 | import qualified Validate
19 |
20 |
21 |
22 | -- PROGRAM
23 |
24 |
25 | program :: Pkg.Name -> B.ByteString -> Result.Result i w Error.Error Valid.Module
26 | program pkg src =
27 | let
28 | bodyParser =
29 | if Pkg.isKernel pkg then
30 | chompDeclarations =<< chompInfixes []
31 | else
32 | chompDeclarations []
33 |
34 | parser =
35 | Module.module_ pkg bodyParser <* P.endOfFile
36 | in
37 | case P.run parser src of
38 | Right modul ->
39 | Validate.validate modul
40 |
41 | Left syntaxError ->
42 | Result.throw syntaxError
43 |
44 |
45 |
46 | -- CHOMP DECLARATIONS
47 |
48 |
49 | chompDeclarations :: [Src.Decl] -> P.Parser [Src.Decl]
50 | chompDeclarations decls =
51 | do (decl, _, pos) <- Decl.declaration
52 | P.oneOf
53 | [ do P.checkFreshLine pos
54 | chompDeclarations (decl:decls)
55 | , return (reverse (decl:decls))
56 | ]
57 |
58 |
59 | chompInfixes :: [Src.Decl] -> P.Parser [Src.Decl]
60 | chompInfixes decls =
61 | P.oneOf
62 | [ do decl <- Decl.infix_
63 | chompInfixes (decl:decls)
64 | , return decls
65 | ]
66 |
--------------------------------------------------------------------------------
/compiler/src/Reporting/Region.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | module Reporting.Region
3 | ( Region(..)
4 | , Position(..)
5 | , zero
6 | , one
7 | , merge
8 | , encode
9 | )
10 | where
11 |
12 |
13 | import qualified Json.Encode as Json
14 | import Data.Binary (Binary, get, put)
15 |
16 |
17 |
18 | -- REGION
19 |
20 |
21 | data Region =
22 | Region
23 | { _start :: !Position
24 | , _end :: !Position
25 | }
26 | deriving (Eq, Ord)
27 |
28 |
29 | data Position =
30 | Position
31 | { _line :: !Int
32 | , _column :: !Int
33 | }
34 | deriving (Eq, Ord)
35 |
36 |
37 | merge :: Region -> Region -> Region
38 | merge (Region start _) (Region _ end) =
39 | Region start end
40 |
41 |
42 | {-# NOINLINE zero #-}
43 | zero :: Region
44 | zero =
45 | Region (Position 0 0) (Position 0 0)
46 |
47 |
48 | {-# NOINLINE one #-}
49 | one :: Region
50 | one =
51 | Region (Position 1 1) (Position 1 1)
52 |
53 |
54 |
55 | -- JSON
56 |
57 |
58 | encode :: Region -> Json.Value
59 | encode (Region start end) =
60 | Json.object
61 | [ ("start", encodePosition start)
62 | , ("end", encodePosition end)
63 | ]
64 |
65 |
66 | encodePosition :: Position -> Json.Value
67 | encodePosition (Position line column) =
68 | Json.object
69 | [ ("line", Json.int line)
70 | , ("column", Json.int column)
71 | ]
72 |
73 |
74 |
75 | -- BINARY
76 |
77 |
78 | instance Binary Region where
79 | get =
80 | Region <$> get <*> get
81 |
82 | put (Region start end) =
83 | do put start
84 | put end
85 |
86 |
87 | instance Binary Position where
88 | get =
89 | Position <$> get <*> get
90 |
91 | put (Position line column) =
92 | do put line
93 | put column
94 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Elm
2 |
3 | Thanks helping with the development of Elm! This document describes the basic
4 | standards for opening pull requests and making the review process as smooth as
5 | possible.
6 |
7 | ## Licensing
8 |
9 | You need to sign the [contributor agreement](ContributorAgreement.pdf)
10 | and send it to before opening your pull request.
11 |
12 | ## Style Guide
13 |
14 | * Haskell — conform to [these guidelines][haskell]
15 | * JavaScript — use [Google's JS style guide][js]
16 |
17 | [haskell]: https://gist.github.com/evancz/0a1f3717c92fe71702be
18 | [js]: https://google.github.io/styleguide/javascriptguide.xml
19 |
20 | ## Branches
21 |
22 | * [The master branch][master] is the home of the next release of the compiler
23 | so new features and improvements get merged there. Most pull requests
24 | should target this branch!
25 |
26 | * [The stable branch][stable] is for tagging releases and critical bug fixes.
27 | This branch is handy for folks who want to build the most recent public
28 | release from source.
29 |
30 | [master]: http://github.com/elm-lang/elm/tree/master
31 | [stable]: http://github.com/elm-lang/elm/tree/stable
32 |
33 | If you are working on a fairly large feature, we will probably want to merge it
34 | in as its own branch and do some testing before bringing it into the master
35 | branch. This way we can keep releases of the master branch independent of new
36 | features.
37 |
38 | Note that the master branch of the compiler should always be in sync with the
39 | master branch of the [website][], and the stable branch of the compiler should
40 | always be in sync with the stable branch of the [website][]. Make sure that
41 | your changes maintain this compatibility.
42 |
43 | [website]: https://github.com/elm-lang/elm-lang.org
44 |
--------------------------------------------------------------------------------
/terminal/src/Develop/Generate/Help.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | {-# LANGUAGE QuasiQuotes #-}
4 | module Develop.Generate.Help
5 | ( makePageHtml
6 | , makeCodeHtml
7 | )
8 | where
9 |
10 |
11 | import qualified Data.ByteString.Builder as B
12 | import Data.Monoid ((<>))
13 | import Text.RawString.QQ (r)
14 |
15 | import qualified Elm.Name as N
16 | import qualified Json.Encode as Encode
17 |
18 |
19 |
20 | -- PAGES
21 |
22 |
23 | makePageHtml :: N.Name -> Maybe Encode.Value -> B.Builder
24 | makePageHtml moduleName maybeFlags =
25 | [r|
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
36 |
37 |
38 | |]
39 |
40 |
41 |
42 | -- CODE
43 |
44 |
45 | makeCodeHtml :: FilePath -> B.Builder -> B.Builder
46 | makeCodeHtml title code =
47 | [r|
48 |
49 |
50 |
51 | |] <> B.stringUtf8 title <> [r|
52 |
57 |
58 |
59 |
60 |
61 |
62 | |] <> code <> [r|
63 |
64 |
65 | |]
66 |
--------------------------------------------------------------------------------
/compiler/src/Data/Bag.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module Data.Bag
3 | ( Bag(..)
4 | , empty
5 | , one
6 | , append
7 | , map
8 | , toList
9 | , fromList
10 | )
11 | where
12 |
13 |
14 | import Prelude hiding (map)
15 | import qualified Data.List as List
16 |
17 |
18 |
19 | -- BAGS
20 |
21 |
22 | data Bag a
23 | = Empty
24 | | One a
25 | | Two (Bag a) (Bag a)
26 |
27 |
28 |
29 | -- HELPERS
30 |
31 |
32 | empty :: Bag a
33 | empty =
34 | Empty
35 |
36 |
37 | one :: a -> Bag a
38 | one =
39 | One
40 |
41 |
42 | append :: Bag a -> Bag a -> Bag a
43 | append left right =
44 | case (left, right) of
45 | (other, Empty) ->
46 | other
47 |
48 | (Empty, other) ->
49 | other
50 |
51 | (_, _) ->
52 | Two left right
53 |
54 |
55 |
56 | -- MAP
57 |
58 |
59 | map :: (a -> b) -> Bag a -> Bag b
60 | map func bag =
61 | case bag of
62 | Empty ->
63 | Empty
64 |
65 | One a ->
66 | One (func a)
67 |
68 | Two left right ->
69 | Two (map func left) (map func right)
70 |
71 |
72 |
73 | -- TO LIST
74 |
75 |
76 | toList :: Bag a -> [a]
77 | toList bag =
78 | toListHelp bag []
79 |
80 |
81 | toListHelp :: Bag a -> [a] -> [a]
82 | toListHelp bag list =
83 | case bag of
84 | Empty ->
85 | list
86 |
87 | One x ->
88 | x : list
89 |
90 | Two a b ->
91 | toListHelp a (toListHelp b list)
92 |
93 |
94 |
95 | -- FROM LIST
96 |
97 |
98 | fromList :: (a -> b) -> [a] -> Bag b
99 | fromList func list =
100 | case list of
101 | [] ->
102 | Empty
103 |
104 | first : rest ->
105 | List.foldl' (add func) (One (func first)) rest
106 |
107 |
108 | add :: (a -> b) -> Bag b -> a -> Bag b
109 | add func bag value =
110 | Two (One (func value)) bag
111 |
--------------------------------------------------------------------------------
/compiler/src/Reporting/Report.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | module Reporting.Report
3 | ( Report(..)
4 | , toDoc
5 | , toCodeSnippet
6 | , toCodePair
7 | )
8 | where
9 |
10 |
11 | import qualified Reporting.Doc as D
12 | import qualified Reporting.Region as R
13 | import qualified Reporting.Render.Code as Code
14 |
15 |
16 |
17 | -- BUILD REPORTS
18 |
19 |
20 | data Report =
21 | Report
22 | { _title :: String
23 | , _region :: R.Region
24 | , _sgstns :: [String]
25 | , _message :: D.Doc
26 | }
27 |
28 |
29 | toDoc :: FilePath -> Report -> D.Doc
30 | toDoc filePath (Report title _ _ message) =
31 | D.vcat
32 | [ messageBar title filePath
33 | , ""
34 | , message
35 | , ""
36 | ]
37 |
38 |
39 | messageBar :: String -> FilePath -> D.Doc
40 | messageBar title filePath =
41 | let
42 | usedSpace =
43 | 4 + length title + 1 + length filePath
44 | in
45 | D.dullcyan $ D.fromString $
46 | "-- " ++ title
47 | ++ " " ++ replicate (max 1 (80 - usedSpace)) '-'
48 | ++ " " ++ filePath
49 |
50 |
51 |
52 | -- CODE FORMATTING
53 |
54 |
55 | toCodeSnippet :: Code.Source -> R.Region -> Maybe R.Region -> (D.Doc, D.Doc) -> D.Doc
56 | toCodeSnippet source region highlight (preHint, postHint) =
57 | D.vcat
58 | [ preHint
59 | , ""
60 | , Code.render source region highlight
61 | , postHint
62 | ]
63 |
64 |
65 | toCodePair :: Code.Source -> R.Region -> R.Region -> (D.Doc, D.Doc) -> (D.Doc, D.Doc, D.Doc) -> D.Doc
66 | toCodePair source r1 r2 (oneStart, oneEnd) (twoStart, twoMiddle, twoEnd) =
67 | case Code.renderPair source r1 r2 of
68 | Code.OneLine codeDocs ->
69 | D.vcat
70 | [ oneStart
71 | , ""
72 | , codeDocs
73 | , oneEnd
74 | ]
75 |
76 | Code.TwoChunks code1 code2 ->
77 | D.vcat
78 | [ twoStart
79 | , ""
80 | , code1
81 | , twoMiddle
82 | , ""
83 | , code2
84 | , twoEnd
85 | ]
86 |
--------------------------------------------------------------------------------
/compiler/src/Elm/Compiler/Imports.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Elm.Compiler.Imports
4 | ( addDefaults
5 | )
6 | where
7 |
8 |
9 | import qualified AST.Source as Src
10 | import qualified AST.Module.Name as Module
11 | import qualified Elm.Name as N
12 | import qualified Elm.Package as Pkg
13 | import qualified Reporting.Annotation as A
14 | import qualified Reporting.Region as R
15 |
16 |
17 |
18 | -- ADD DEFAULTS
19 |
20 |
21 | addDefaults :: Pkg.Name -> [Src.Import] -> [Src.Import]
22 | addDefaults pkgName imports =
23 | if pkgName == Pkg.core
24 | then imports
25 | else defaults ++ imports
26 |
27 |
28 |
29 | -- DEFAULTS
30 |
31 |
32 | defaults :: [Src.Import]
33 | defaults =
34 | [ import_ Module.basics Nothing Src.Open
35 | , import_ Module.debug Nothing closed
36 | , import_ Module.list Nothing (operator "::")
37 | , import_ Module.maybe Nothing (typeOpen N.maybe)
38 | , import_ Module.result Nothing (typeOpen N.result)
39 | , import_ Module.string Nothing (typeClosed N.string)
40 | , import_ Module.char Nothing (typeClosed N.char)
41 | , import_ Module.tuple Nothing closed
42 | , import_ Module.platform Nothing (typeClosed N.program)
43 | , import_ Module.cmd (Just N.cmd) (typeClosed N.cmd)
44 | , import_ Module.sub (Just N.sub) (typeClosed N.sub)
45 | ]
46 |
47 |
48 | import_ :: Module.Canonical -> Maybe N.Name -> Src.Exposing -> Src.Import
49 | import_ (Module.Canonical _ name) maybeAlias exposing =
50 | Src.Import (A.At R.zero name) maybeAlias exposing
51 |
52 |
53 |
54 | -- EXPOSING
55 |
56 |
57 | closed :: Src.Exposing
58 | closed =
59 | Src.Explicit []
60 |
61 |
62 | typeOpen :: N.Name -> Src.Exposing
63 | typeOpen name =
64 | Src.Explicit [ A.At R.zero (Src.Upper name Src.Public) ]
65 |
66 |
67 | typeClosed :: N.Name -> Src.Exposing
68 | typeClosed name =
69 | Src.Explicit [ A.At R.zero (Src.Upper name Src.Private) ]
70 |
71 |
72 | operator :: N.Name -> Src.Exposing
73 | operator op =
74 | Src.Explicit [ A.At R.zero (Src.Operator op) ]
75 |
--------------------------------------------------------------------------------
/installers/npm/bin/elm:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | // This file exists for the benefit of npm users who have --ignore-scripts
4 | // enabled. (Enabling this flag globally is a good security measure.)
5 | // Since they won't run the post-install hook, the binaries won't be downloaded
6 | // and installed.
7 | //
8 | // Since this file is included in "bin" in package.json, npm will install
9 | // it automatically in a place that should be on the PATH. All the file does
10 | // is to download the appropriate binary (just like the post-install hook would
11 | // have), replace this file with that binary, and run the binary.
12 | //
13 | // In this way, the first time a user with --ignore-scripts enabled runs this
14 | // binary, it will download and install itself, and then run as normal. From
15 | // then on, it will run as normal without re-downloading.
16 |
17 | var install = require("..").install;
18 | var child_process = require("child_process");
19 | var path = require("path");
20 | var fs = require("fs");
21 |
22 | // Make sure we get the right path even if we're executing from the symlinked
23 | // node_modules/.bin/ executable
24 | var interpreter = fs.realpathSync(process.argv[0]);
25 | var targetPath = fs.realpathSync(process.argv[1]);
26 |
27 | // Figure out the binary name as we'll eventually want to execute
28 | // this. Re-executing this script doesn't always work because of varying
29 | // permissions and modes of operation across platforms (for example, Windows has
30 | // some interesting edge cases here.)
31 | var binaryName = path.join(
32 | __dirname,
33 | "..",
34 | "unpacked_bin",
35 | path.basename(targetPath)
36 | );
37 | if (process.platform === "win") {
38 | binaryName += ".exe";
39 | }
40 |
41 | // cd into the directory above bin/ so install() puts bin/ in the right place.
42 | process.chdir(path.join(path.dirname(targetPath), ".."));
43 |
44 | install(process.platform, process.arch).then(function() {
45 | child_process
46 | .spawn(binaryName, process.argv.slice(2), { stdio: "inherit" })
47 | .on("exit", process.exit);
48 | });
49 |
--------------------------------------------------------------------------------
/compiler/src/Type/Instantiate.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Type.Instantiate
4 | ( FreeVars
5 | , fromSrcType
6 | )
7 | where
8 |
9 |
10 | import qualified Data.Map.Strict as Map
11 | import Data.Map.Strict ((!))
12 |
13 | import qualified AST.Canonical as Can
14 | import qualified Elm.Name as N
15 | import Type.Type
16 |
17 |
18 |
19 | -- FREE VARS
20 |
21 |
22 | type FreeVars =
23 | Map.Map N.Name Type
24 |
25 |
26 |
27 | -- FROM SOURCE TYPE
28 |
29 |
30 | fromSrcType :: Map.Map N.Name Type -> Can.Type -> IO Type
31 | fromSrcType freeVars sourceType =
32 | case sourceType of
33 | Can.TLambda arg result ->
34 | FunN
35 | <$> fromSrcType freeVars arg
36 | <*> fromSrcType freeVars result
37 |
38 | Can.TVar name ->
39 | return (freeVars ! name)
40 |
41 | Can.TType home name args ->
42 | AppN home name <$> traverse (fromSrcType freeVars) args
43 |
44 | Can.TAlias home name args aliasedType ->
45 | do targs <- traverse (traverse (fromSrcType freeVars)) args
46 | AliasN home name targs <$>
47 | case aliasedType of
48 | Can.Filled realType ->
49 | fromSrcType freeVars realType
50 |
51 | Can.Holey realType ->
52 | fromSrcType (Map.fromList targs) realType
53 |
54 | Can.TTuple a b maybeC ->
55 | TupleN
56 | <$> fromSrcType freeVars a
57 | <*> fromSrcType freeVars b
58 | <*> traverse (fromSrcType freeVars) maybeC
59 |
60 | Can.TUnit ->
61 | return UnitN
62 |
63 | Can.TRecord fields maybeExt ->
64 | RecordN
65 | <$> traverse (fromSrcFieldType freeVars) fields
66 | <*>
67 | case maybeExt of
68 | Nothing ->
69 | return EmptyRecordN
70 |
71 | Just ext ->
72 | return (freeVars ! ext)
73 |
74 |
75 | fromSrcFieldType :: Map.Map N.Name Type -> Can.FieldType -> IO Type
76 | fromSrcFieldType freeVars (Can.FieldType _ tipe) =
77 | fromSrcType freeVars tipe
78 |
--------------------------------------------------------------------------------
/terminal/src/Terminal/Args/Internal.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE GADTs #-}
2 | module Terminal.Args.Internal
3 | ( Interface(..)
4 | , toName
5 | , Summary(..)
6 | , Flags(..)
7 | , Flag(..)
8 | , Parser(..)
9 | , Args(..)
10 | , CompleteArgs(..)
11 | , RequiredArgs(..)
12 | )
13 | where
14 |
15 |
16 | import Text.PrettyPrint.ANSI.Leijen (Doc)
17 |
18 |
19 |
20 | -- INTERFACE
21 |
22 |
23 | data Interface where
24 | Interface
25 | :: String
26 | -> Summary
27 | -> String
28 | -> Doc
29 | -> Args args
30 | -> Flags flags
31 | -> (args -> flags -> IO ())
32 | -> Interface
33 |
34 |
35 | toName :: Interface -> String
36 | toName (Interface name _ _ _ _ _ _) =
37 | name
38 |
39 |
40 |
41 | {-| The information that shows when you run the executable with no arguments.
42 | If you say it is `Common`, you need to tell people what it does. Try to keep
43 | it to two or three lines. If you say it is `Uncommon` you can rely on `Details`
44 | for a more complete explanation.
45 | -}
46 | data Summary = Common String | Uncommon
47 |
48 |
49 |
50 | -- FLAGS
51 |
52 |
53 | data Flags a where
54 | FDone :: a -> Flags a
55 | FMore :: Flags (a -> b) -> Flag a -> Flags b
56 |
57 |
58 | data Flag a where
59 | Flag :: String -> Parser a -> String -> Flag (Maybe a)
60 | OnOff :: String -> String -> Flag Bool
61 |
62 |
63 |
64 | -- PARSERS
65 |
66 |
67 | data Parser a =
68 | Parser
69 | { _singular :: String
70 | , _plural :: String
71 | , _parser :: String -> Maybe a
72 | , _suggest :: String -> IO [String]
73 | , _examples :: String -> IO [String]
74 | }
75 |
76 |
77 |
78 | -- ARGS
79 |
80 |
81 | newtype Args a =
82 | Args [CompleteArgs a]
83 |
84 |
85 | data CompleteArgs args where
86 | Exactly :: RequiredArgs args -> CompleteArgs args
87 | Multiple :: RequiredArgs ([a] -> args) -> Parser a -> CompleteArgs args
88 | Optional :: RequiredArgs (Maybe a -> args) -> Parser a -> CompleteArgs args
89 |
90 |
91 | data RequiredArgs a where
92 | Done :: a -> RequiredArgs a
93 | Required :: RequiredArgs (a -> b) -> Parser a -> RequiredArgs b
94 |
--------------------------------------------------------------------------------
/compiler/src/Parse/Primitives/Shader.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE BangPatterns #-}
3 | module Parse.Primitives.Shader
4 | ( block
5 | , failure
6 | )
7 | where
8 |
9 |
10 | import qualified Data.ByteString.Internal as B
11 | import qualified Data.Text as Text
12 | import qualified Data.Text.Encoding as Text
13 | import Foreign.ForeignPtr (ForeignPtr)
14 | import GHC.Word (Word8)
15 |
16 | import Parse.Primitives.Internals (Parser(..), State(..), noError)
17 | import qualified Parse.Primitives.Internals as I
18 | import qualified Parse.Primitives.Symbol as Symbol
19 | import qualified Reporting.Error.Syntax as E
20 |
21 |
22 |
23 | -- SHADER
24 |
25 |
26 | failure :: Int -> Int -> Text.Text -> Parser a
27 | failure row col msg =
28 | Parser $ \_ _ cerr _ _ ->
29 | cerr (E.ParseError row col (E.BadShader msg))
30 |
31 |
32 | block :: Parser Text.Text
33 | block =
34 | do Symbol.shaderBlockOpen
35 | Parser $ \(State fp offset terminal indent row col ctx) cok cerr _ _ ->
36 | case eatShader fp offset terminal row col of
37 | Err ->
38 | cerr (E.ParseError row col E.EndOfFile_Shader)
39 |
40 | Ok newOffset newRow newCol ->
41 | let
42 | !size = newOffset - offset
43 | !shader = Text.decodeUtf8 (B.PS fp offset size)
44 | !newState = State fp (newOffset + 2) terminal indent newRow newCol ctx
45 | in
46 | cok shader newState noError
47 |
48 |
49 | data Result
50 | = Err
51 | | Ok Int Int Int
52 |
53 |
54 | eatShader :: ForeignPtr Word8 -> Int -> Int -> Int -> Int -> Result
55 | eatShader fp offset terminal row col =
56 | if offset >= terminal then
57 | Err
58 |
59 | else
60 | let !word = I.unsafeIndex fp offset in
61 | if word == 0x007C {- | -} && I.isWord fp (offset + 1) terminal 0x5D {- ] -} then
62 | Ok offset row (col + 2)
63 |
64 | else if word == 0x0A {- \n -} then
65 | eatShader fp (offset + 1) terminal (row + 1) 1
66 |
67 | else
68 | let !newOffset = offset + I.getCharWidth fp offset terminal word in
69 | eatShader fp newOffset terminal row (col + 1)
70 |
--------------------------------------------------------------------------------
/compiler/src/Elm/Compiler/Module.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Elm.Compiler.Module
4 | -- interfaces
5 | ( I.Interface
6 | , I.Interfaces
7 |
8 | -- module names
9 | , Raw
10 | , nameToString
11 | , nameToSlashPath
12 | , nameToHyphenPath
13 | , fromHyphenPath
14 | , encode
15 | , decoder
16 |
17 | -- canonical names
18 | , ModuleName.Canonical(..)
19 | )
20 | where
21 |
22 |
23 | import qualified Data.Char as Char
24 | import qualified Data.List as List
25 | import qualified Data.Text as Text
26 | import System.FilePath ((>))
27 |
28 | import qualified AST.Module.Name as ModuleName
29 | import qualified Elm.Interface as I
30 | import qualified Elm.Name as N
31 | import qualified Json.Decode as Decode
32 | import qualified Json.Encode as Encode
33 |
34 |
35 |
36 | -- NAMES
37 |
38 |
39 | type Raw = N.Name
40 |
41 |
42 | nameToString :: Raw -> String
43 | nameToString =
44 | N.toString
45 |
46 |
47 | nameToSlashPath :: Raw -> FilePath
48 | nameToSlashPath name =
49 | List.foldl1 (>) (map Text.unpack (Text.splitOn "." (N.toText name)))
50 |
51 |
52 | nameToHyphenPath :: Raw -> FilePath
53 | nameToHyphenPath name =
54 | Text.unpack (Text.replace "." "-" (N.toText name))
55 |
56 |
57 | fromHyphenPath :: Text.Text -> Maybe Raw
58 | fromHyphenPath txt =
59 | if all isGoodChunk (Text.splitOn "-" txt)
60 | then Just (N.fromText (Text.replace "-" "." txt))
61 | else Nothing
62 |
63 |
64 |
65 | -- JSON
66 |
67 |
68 | encode :: Raw -> Encode.Value
69 | encode =
70 | Encode.name
71 |
72 |
73 | decoder :: Decode.Decoder Text.Text Raw
74 | decoder =
75 | do txt <- Decode.text
76 | let chunks = Text.splitOn "." txt
77 | if all isGoodChunk chunks
78 | then Decode.succeed (N.fromText txt)
79 | else Decode.fail txt
80 |
81 |
82 | isGoodChunk :: Text.Text -> Bool
83 | isGoodChunk chunk =
84 | case Text.uncons chunk of
85 | Nothing ->
86 | False
87 |
88 | Just (first, rest) ->
89 | Char.isUpper first && Text.all isGoodChar rest
90 |
91 |
92 | isGoodChar :: Char -> Bool
93 | isGoodChar char =
94 | Char.isAlphaNum char || char == '_'
95 |
96 |
97 |
--------------------------------------------------------------------------------
/compiler/src/Type/Occurs.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Type.Occurs
4 | ( occurs
5 | )
6 | where
7 |
8 |
9 | import Data.Foldable (foldrM)
10 | import qualified Data.Map.Strict as Map
11 |
12 | import Type.Type as Type
13 | import qualified Type.UnionFind as UF
14 |
15 |
16 |
17 | -- OCCURS
18 |
19 |
20 | occurs :: Type.Variable -> IO Bool
21 | occurs var =
22 | occursHelp [] var False
23 |
24 |
25 | occursHelp :: [Type.Variable] -> Type.Variable -> Bool -> IO Bool
26 | occursHelp seen var foundCycle =
27 | if elem var seen then
28 | return True
29 |
30 | else
31 | do (Descriptor content _ _ _) <- UF.get var
32 | case content of
33 | FlexVar _ ->
34 | return foundCycle
35 |
36 | FlexSuper _ _ ->
37 | return foundCycle
38 |
39 | RigidVar _ ->
40 | return foundCycle
41 |
42 | RigidSuper _ _ ->
43 | return foundCycle
44 |
45 | Structure term ->
46 | let newSeen = var : seen in
47 | case term of
48 | App1 _ _ args ->
49 | foldrM (occursHelp newSeen) foundCycle args
50 |
51 | Fun1 a b ->
52 | occursHelp newSeen a =<<
53 | occursHelp newSeen b foundCycle
54 |
55 | EmptyRecord1 ->
56 | return foundCycle
57 |
58 | Record1 fields ext ->
59 | occursHelp newSeen ext =<<
60 | foldrM (occursHelp newSeen) foundCycle (Map.elems fields)
61 |
62 | Unit1 ->
63 | return foundCycle
64 |
65 | Tuple1 a b maybeC ->
66 | case maybeC of
67 | Nothing ->
68 | occursHelp newSeen a =<<
69 | occursHelp newSeen b foundCycle
70 |
71 | Just c ->
72 | occursHelp newSeen a =<<
73 | occursHelp newSeen b =<<
74 | occursHelp newSeen c foundCycle
75 |
76 | Alias _ _ args _ ->
77 | foldrM (occursHelp (var:seen)) foundCycle (map snd args)
78 |
79 | Error ->
80 | return foundCycle
81 |
--------------------------------------------------------------------------------
/docs/elm.json/application.md:
--------------------------------------------------------------------------------
1 | # `elm.json` for applications
2 |
3 | This is a decent baseline for pretty much any applications made with Elm. You will need these dependencies or more.
4 |
5 | ```json
6 | {
7 | "type": "application",
8 | "source-directories": [
9 | "src"
10 | ],
11 | "elm-version": "0.19.0",
12 | "dependencies": {
13 | "direct": {
14 | "elm/browser": "1.0.0",
15 | "elm/core": "1.0.0",
16 | "elm/html": "1.0.0",
17 | "elm/json": "1.0.0"
18 | },
19 | "indirect": {
20 | "elm/time": "1.0.0",
21 | "elm/url": "1.0.0",
22 | "elm/virtual-dom": "1.0.0"
23 | }
24 | },
25 | "test-dependencies": {
26 | "direct": {},
27 | "indirect": {}
28 | }
29 | }
30 | ```
31 |
32 |
33 |
34 |
35 | ## `"type"`
36 |
37 | Either `"application"` or `"package"`. All the other fields are based on this choice!
38 |
39 |
40 |
41 |
42 | ## `"source-directories"`
43 |
44 | A list of directories where Elm code lives. Most projects just use `"src"` for everything.
45 |
46 |
47 |
48 |
49 | ## `"elm-version"`
50 |
51 | The exact version of Elm this builds with. Should be `"0.19.0"` for most people!
52 |
53 |
54 |
55 |
56 | ## `"dependencies"`
57 |
58 | All the packages you depend upon. We use exact versions, so your `elm.json` file doubles as a "lock file" that ensures reliable builds.
59 |
60 | You can use modules from any `"direct"` dependency in your code. Some `"direct"` dependencies have their own dependencies that folks typically do not care about. These are the `"indirect"` dependencies. They are listed explicitly so that (1) builds are reproducible and (2) you can easily review the quantity and quality of dependencies.
61 |
62 | **Note:** We plan to eventually have a screen in `reactor` that helps add, remove, and upgrade packages. It can sometimes be tricky to keep all of the constraints happy, so we think having a UI will help a lot. If you get into trouble in the meantime, adding things back one-by-one often helps, and I hope you do not get into trouble!
63 |
64 |
65 |
66 |
67 | ## `"test-dependencies"`
68 |
69 | All the packages that you use in `tests/` with `elm-test` but not in the application you actually want to ship. This also uses exact versions to make tests more reliable.
70 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # The Elm Compiler
2 |
3 | Learn about the Elm programming language at [elm-lang.org](http://elm-lang.org/).
4 |
5 | This is a fork of the Elm Compiler maintained by CurrySoftware GmbH.
6 | We merged a few bug fixes by the community and also expanded the reactor.
7 |
8 | ## Build
9 |
10 | With Stack:
11 |
12 | 1. Install Stack (https://www.haskellstack.org)
13 | 2. Clone the repo
14 | 3. `cd compiler`
15 | 4. `stack init`
16 | 5. `stack build`
17 |
18 |
19 | ## Install
20 |
21 |
22 | The built Elm executable will reside inside `.stack-work/dist///build/elm/elm`.
23 | To install it link or copy it to the `/usr/bin/` folder.
24 |
25 | For example:
26 |
27 | `copy .stack-work/dist/x86_64-linux-tinfo6/Cabal-2.0.1.0/build/elm/elm /usr/bin/elm-dev`
28 |
29 | or
30 | `ln -s .stack-work/dist/x86_64-linux-tinfo6/Cabal-2.0.1.0/build/elm/elm /usr/bin/elm-dev`
31 |
32 |
33 | ## Using the new Features in Elm Reactor
34 |
35 | This Elm-fork contains two new features in the Elm reactor:
36 |
37 | 1. The reactor can now emit compiled JavaScript instead of Html with the `output=js` parameter:
38 |
39 | ```html
40 |
41 | ```
42 |
43 | 2. The reactor can now emit code compiled in debug mode with the `debug=true` parameter:
44 |
45 | `http://localhost:8000/src/Main.elm?debug=true`
46 |
47 | or in combination with JavaScript output:
48 | ```html
49 |
50 | ```
51 |
52 |
53 | For a working example check out [elm-reactor-example](https://github.com/CurrySoftware/elm-reactor-example).
54 |
55 |
56 | ## Future
57 |
58 | We are currently exploring the idea of building a private Elm package repository to facilitate usage of Elm in corporate environments.
59 |
60 | Currently, it is not possible to use either local packages or another package repository than `package.elm-lang.org`.
61 |
62 | We want to change that!
63 |
64 | If you are a corporate Elm-user and want to support us or if you are interested in a closed beta please contact us through [elm@curry-software.com](mailto:elm@curry-software.com).
65 |
66 | ## Support
67 |
68 | Please contact [elm@curry-software.com](mailto:elm@curry-software.com) for support.
69 |
70 | We also offer [Elm Consulting Services](https://www.curry-software.com/en/elm-services/) for general help with Elm.
71 |
--------------------------------------------------------------------------------
/compiler/src/Elm/Compiler.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | module Elm.Compiler
3 | ( version
4 | , Compile.DocsFlag(..)
5 | , Compile.Artifacts(..)
6 | , compile
7 | , Error.Error
8 | , errorsToDoc
9 | , errorsToJson
10 | , Warning.Warning
11 | )
12 | where
13 |
14 |
15 | import qualified Data.ByteString as BS
16 | import qualified Data.Map as Map
17 | import qualified Data.Text as Text
18 |
19 | import qualified Compile
20 | import qualified Elm.Compiler.Module as M
21 | import qualified Elm.Compiler.Version
22 | import qualified Elm.Package as Pkg
23 | import qualified Json.Encode as Encode
24 | import qualified Reporting.Doc as D
25 | import qualified Reporting.Error as Error
26 | import qualified Reporting.Render.Code as Code
27 | import qualified Reporting.Region as Region
28 | import qualified Reporting.Report as Report
29 | import qualified Reporting.Result as Result
30 | import qualified Reporting.Warning as Warning
31 |
32 |
33 |
34 | -- VERSION
35 |
36 |
37 | version :: Pkg.Version
38 | version =
39 | Elm.Compiler.Version.version
40 |
41 |
42 |
43 | -- COMPILE
44 |
45 |
46 | compile
47 | :: Compile.DocsFlag
48 | -> Pkg.Name
49 | -> Map.Map M.Raw M.Canonical
50 | -> M.Interfaces
51 | -> BS.ByteString
52 | -> ( [Warning.Warning], Either [Error.Error] Compile.Artifacts )
53 | compile docsFlag pkg importDict interfaces source =
54 | Result.run $ Compile.compile docsFlag pkg importDict interfaces source
55 |
56 |
57 |
58 | -- ERRORS TO DOC
59 |
60 |
61 | errorsToDoc :: FilePath -> Text.Text -> [Error.Error] -> D.Doc
62 | errorsToDoc filePath source errors =
63 | let
64 | reports =
65 | concatMap (Error.toReports (Code.toSource source)) errors
66 | in
67 | D.vcat $ map (Report.toDoc filePath) reports
68 |
69 |
70 |
71 | -- ERRORS TO JSON
72 |
73 |
74 | errorsToJson :: M.Raw -> FilePath -> Text.Text -> [Error.Error] -> Encode.Value
75 | errorsToJson moduleName filePath source errors =
76 | let
77 | reports =
78 | concatMap (Error.toReports (Code.toSource source)) errors
79 | in
80 | Encode.object
81 | [ ("path", Encode.text (Text.pack filePath))
82 | , ("name", Encode.name moduleName)
83 | , ("problems", Encode.array (map reportToJson reports))
84 | ]
85 |
86 |
87 | reportToJson :: Report.Report -> Encode.Value
88 | reportToJson (Report.Report title region _sgstns message) =
89 | Encode.object
90 | [ ("title", Encode.text (Text.pack title))
91 | , ("region", Region.encode region)
92 | , ("message", D.encode message)
93 | ]
--------------------------------------------------------------------------------
/terminal/src/Make.hs:
--------------------------------------------------------------------------------
1 | module Make
2 | ( Flags(..)
3 | , run
4 | , ReportType(..)
5 | , reportType
6 | , docsFile
7 | )
8 | where
9 |
10 |
11 | import qualified System.FilePath as FP
12 |
13 | import qualified Elm.Project as Project
14 | import qualified Generate.Output as Output
15 | import qualified Reporting.Exit as Exit
16 | import qualified Reporting.Exit.Make as E
17 | import qualified Reporting.Task as Task
18 | import qualified Reporting.Progress as Progress
19 | import qualified Reporting.Progress.Json as Json
20 | import qualified Reporting.Progress.Terminal as Terminal
21 | import Terminal.Args (Parser(..))
22 |
23 |
24 |
25 | -- RUN
26 |
27 |
28 | data Flags =
29 | Flags
30 | { _debug :: Bool
31 | , _optimize :: Bool
32 | , _output :: Maybe Output.Output
33 | , _report :: Maybe ReportType
34 | , _docs :: Maybe FilePath
35 | }
36 |
37 |
38 | run :: [FilePath] -> Flags -> IO ()
39 | run paths (Flags debug optimize output report docs) =
40 | do reporter <- toReporter report
41 | Task.run reporter $
42 | do mode <- toMode debug optimize
43 | summary <- Project.getRoot
44 | Project.compile mode Output.Client output docs summary paths
45 |
46 |
47 | toMode :: Bool -> Bool -> Task.Task Output.Mode
48 | toMode debug optimize =
49 | case (debug, optimize) of
50 | (True , True ) -> Task.throw $ Exit.Make E.CannotOptimizeAndDebug
51 | (False, True ) -> return Output.Prod
52 | (False, False) -> return Output.Dev
53 | (True , False) -> return Output.Debug
54 |
55 |
56 | toReporter :: Maybe ReportType -> IO Progress.Reporter
57 | toReporter report =
58 | case report of
59 | Nothing -> Terminal.create
60 | Just Json -> return Json.reporter
61 |
62 |
63 |
64 | -- REPORT
65 |
66 |
67 | data ReportType
68 | = Json
69 |
70 |
71 | reportType :: Parser ReportType
72 | reportType =
73 | Parser
74 | { _singular = "report type"
75 | , _plural = "report types"
76 | , _parser = \string -> if string == "json" then Just Json else Nothing
77 | , _suggest = \_ -> return ["json"]
78 | , _examples = \_ -> return ["json"]
79 | }
80 |
81 |
82 |
83 | -- DOCS
84 |
85 |
86 | docsFile :: Parser FilePath
87 | docsFile =
88 | Parser
89 | { _singular = "json file"
90 | , _plural = "json files"
91 | , _parser = \string -> if FP.takeExtension string == ".json" then Just string else Nothing
92 | , _suggest = \_ -> return []
93 | , _examples = \_ -> return ["docs.json","documentation.json"]
94 | }
95 |
--------------------------------------------------------------------------------
/compiler/src/Generate/JavaScript/Mode.hs:
--------------------------------------------------------------------------------
1 | module Generate.JavaScript.Mode
2 | ( Mode(..)
3 | , Target(..)
4 | , debug
5 | , dev
6 | , prod
7 | , isDebug
8 | , isServer
9 | )
10 | where
11 |
12 |
13 | import qualified Data.List as List
14 | import qualified Data.Map as Map
15 | import qualified Data.Maybe as Maybe
16 |
17 | import qualified AST.Optimized as Opt
18 | import qualified Elm.Interface as I
19 | import qualified Elm.Name as N
20 | import qualified Generate.JavaScript.Name as Name
21 |
22 |
23 |
24 | -- MODE
25 |
26 |
27 | data Mode
28 | = Dev Target (Maybe I.Interfaces)
29 | | Prod Target ShortFieldNames
30 |
31 |
32 | data Target = Client | Server
33 |
34 |
35 | debug :: Target -> I.Interfaces -> Mode
36 | debug target interfaces =
37 | Dev target (Just interfaces)
38 |
39 |
40 | dev :: Target -> Mode
41 | dev target =
42 | Dev target Nothing
43 |
44 |
45 | prod :: Target -> Opt.Graph -> Mode
46 | prod target (Opt.Graph _ _ fieldCounts) =
47 | Prod target (shortenFieldNames fieldCounts)
48 |
49 |
50 |
51 | -- IS DEBUG?
52 |
53 |
54 | isDebug :: Mode -> Bool
55 | isDebug mode =
56 | case mode of
57 | Dev _ mi -> Maybe.isJust mi
58 | Prod _ _ -> False
59 |
60 |
61 | -- IS SERVER?
62 |
63 |
64 | isServer :: Mode -> Bool
65 | isServer mode =
66 | case mode of
67 | Dev target _ -> isServerHelp target
68 | Prod target _ -> isServerHelp target
69 |
70 |
71 | isServerHelp :: Target -> Bool
72 | isServerHelp target =
73 | case target of
74 | Client -> False
75 | Server -> True
76 |
77 |
78 |
79 | -- SHORTEN FIELD NAMES
80 |
81 |
82 | type ShortFieldNames =
83 | Map.Map N.Name Name.Name
84 |
85 |
86 | shortenFieldNames :: Map.Map N.Name Int -> ShortFieldNames
87 | shortenFieldNames frequencies =
88 | Map.foldr addToShortNames Map.empty $
89 | Map.foldrWithKey addToBuckets Map.empty frequencies
90 |
91 |
92 | addToBuckets :: N.Name -> Int -> Map.Map Int [N.Name] -> Map.Map Int [N.Name]
93 | addToBuckets field frequency buckets =
94 | -- TODO try using an IntMap for buckets
95 | Map.insertWith (++) frequency [field] buckets
96 |
97 |
98 | addToShortNames :: [N.Name] -> ShortFieldNames -> ShortFieldNames
99 | addToShortNames fields shortNames =
100 | List.foldl' addField shortNames fields
101 |
102 |
103 | addField :: ShortFieldNames -> N.Name -> ShortFieldNames
104 | addField shortNames field =
105 | let rename = Name.fromInt (Map.size shortNames) in
106 | Map.insert field rename shortNames
107 |
--------------------------------------------------------------------------------
/compiler/src/Elm/Compiler/Objects.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module Elm.Compiler.Objects
3 | ( JS.Output(..)
4 | , JS.generate
5 | , JS.generateForRepl
6 | , Opt.Graph
7 | , empty
8 | , union
9 | , unions
10 | , Kernel(..)
11 | , fromKernels
12 | )
13 | where
14 |
15 |
16 | import qualified Data.List as List
17 | import qualified Data.Map as Map
18 |
19 | import qualified AST.Optimized as Opt
20 | import qualified AST.Module.Name as ModuleName
21 | import qualified Elm.Name as N
22 | import qualified Elm.Package as Pkg
23 | import qualified Generate.JavaScript as JS
24 |
25 |
26 |
27 | -- COMBINE GRAPHS
28 |
29 |
30 | {-# NOINLINE empty #-}
31 | empty :: Opt.Graph
32 | empty =
33 | Opt.Graph Map.empty Map.empty Map.empty
34 |
35 |
36 | union :: Opt.Graph -> Opt.Graph -> Opt.Graph
37 | union (Opt.Graph mains1 graph1 fields1) (Opt.Graph mains2 graph2 fields2) =
38 | Opt.Graph
39 | (Map.union mains1 mains2)
40 | (Map.union graph1 graph2)
41 | (Map.union fields1 fields2)
42 |
43 |
44 | unions :: [Opt.Graph] -> Opt.Graph
45 | unions graphs =
46 | case graphs of
47 | [] ->
48 | empty
49 |
50 | g:gs ->
51 | List.foldl' union g gs
52 |
53 |
54 |
55 | -- KERNEL GRAPHS
56 |
57 |
58 | data Kernel =
59 | Kernel
60 | { _client :: Opt.KContent
61 | , _server :: Maybe Opt.KContent
62 | }
63 |
64 |
65 | fromKernels :: Map.Map N.Name Kernel -> Opt.Graph
66 | fromKernels kernels =
67 | Opt.Graph
68 | Map.empty
69 | (Map.mapKeys toGlobal (Map.map toNode kernels))
70 | (Map.foldl' addKernel Map.empty kernels)
71 |
72 |
73 |
74 | -- KERNEL TO NODES
75 |
76 |
77 | toGlobal :: N.Name -> Opt.Global
78 | toGlobal home =
79 | Opt.Global (ModuleName.Canonical Pkg.kernel (ModuleName.getKernel home)) N.dollar
80 |
81 |
82 | toNode :: Kernel -> Opt.Node
83 | toNode (Kernel client server) =
84 | Opt.Kernel client server
85 |
86 |
87 |
88 | -- KERNEL TO ELM FIELDS
89 |
90 |
91 | addKernel :: Map.Map N.Name Int -> Kernel -> Map.Map N.Name Int
92 | addKernel fields (Kernel client maybeServer) =
93 | addContent (maybe fields (addContent fields) maybeServer) client
94 |
95 |
96 | addContent :: Map.Map N.Name Int -> Opt.KContent -> Map.Map N.Name Int
97 | addContent fields (Opt.KContent chunks _) =
98 | List.foldl' addChunk fields chunks
99 |
100 |
101 | addChunk :: Map.Map N.Name Int -> Opt.KChunk -> Map.Map N.Name Int
102 | addChunk fields chunk =
103 | case chunk of
104 | Opt.ElmField name ->
105 | Map.insertWith (+) name 1 fields
106 |
107 | _ ->
108 | fields
109 |
--------------------------------------------------------------------------------
/terminal/src/Develop/StaticFiles.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | {-# LANGUAGE TemplateHaskell #-}
4 | module Develop.StaticFiles
5 | ( lookup
6 | , cssPath
7 | , elmPath
8 | , waitingPath
9 | )
10 | where
11 |
12 | import Prelude hiding (lookup)
13 | import qualified Data.ByteString as BS
14 | import Data.FileEmbed (bsToExp)
15 | import qualified Data.HashMap.Strict as HM
16 | import Language.Haskell.TH (runIO)
17 | import System.FilePath ((>))
18 |
19 | import qualified Develop.StaticFiles.Build as Build
20 |
21 |
22 |
23 | -- FILE LOOKUP
24 |
25 |
26 | type MimeType =
27 | BS.ByteString
28 |
29 |
30 | lookup :: FilePath -> Maybe (BS.ByteString, MimeType)
31 | lookup path =
32 | HM.lookup path dict
33 |
34 |
35 | dict :: HM.HashMap FilePath (BS.ByteString, MimeType)
36 | dict =
37 | HM.fromList
38 | [ faviconPath ==> (favicon , "image/x-icon")
39 | , waitingPath ==> (waiting , "image/gif")
40 | , elmPath ==> (elm , "application/javascript")
41 | , cssPath ==> (css , "text/css")
42 | , codeFontPath ==> (codeFont, "font/ttf")
43 | , sansFontPath ==> (sansFont, "font/ttf")
44 | ]
45 |
46 |
47 | (==>) :: a -> b -> (a,b)
48 | (==>) a b =
49 | (a, b)
50 |
51 |
52 |
53 | -- PATHS
54 |
55 |
56 | faviconPath :: FilePath
57 | faviconPath =
58 | "favicon.ico"
59 |
60 |
61 | waitingPath :: FilePath
62 | waitingPath =
63 | "_elm" > "waiting.gif"
64 |
65 |
66 | elmPath :: FilePath
67 | elmPath =
68 | "_elm" > "elm.js"
69 |
70 |
71 | cssPath :: FilePath
72 | cssPath =
73 | "_elm" > "styles.css"
74 |
75 |
76 | codeFontPath :: FilePath
77 | codeFontPath =
78 | "_elm" > "source-code-pro.ttf"
79 |
80 |
81 | sansFontPath :: FilePath
82 | sansFontPath =
83 | "_elm" > "source-sans-pro.ttf"
84 |
85 |
86 |
87 | -- ELM
88 |
89 |
90 | elm :: BS.ByteString
91 | elm =
92 | $(bsToExp =<< runIO Build.compile)
93 |
94 |
95 |
96 | -- CSS
97 |
98 |
99 | css :: BS.ByteString
100 | css =
101 | $(bsToExp =<< runIO (Build.readAsset "styles.css"))
102 |
103 |
104 |
105 | -- FONTS
106 |
107 |
108 | codeFont :: BS.ByteString
109 | codeFont =
110 | $(bsToExp =<< runIO (Build.readAsset "source-code-pro.ttf"))
111 |
112 |
113 | sansFont :: BS.ByteString
114 | sansFont =
115 | $(bsToExp =<< runIO (Build.readAsset "source-sans-pro.ttf"))
116 |
117 |
118 |
119 | -- IMAGES
120 |
121 |
122 | favicon :: BS.ByteString
123 | favicon =
124 | $(bsToExp =<< runIO (Build.readAsset "favicon.ico"))
125 |
126 |
127 | waiting :: BS.ByteString
128 | waiting =
129 | $(bsToExp =<< runIO (Build.readAsset "waiting.gif"))
130 |
--------------------------------------------------------------------------------
/compiler/src/Reporting/Result.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE Rank2Types #-}
3 | module Reporting.Result
4 | ( Result(..)
5 | , run
6 | , ok
7 | , warn
8 | , throw
9 | , mapError
10 | )
11 | where
12 |
13 |
14 | import qualified Data.OneOrMore as OneOrMore
15 | import qualified Reporting.Warning as Warning
16 |
17 |
18 |
19 | -- RESULT
20 |
21 |
22 | newtype Result info warnings error a =
23 | Result (
24 | forall result.
25 | info
26 | -> warnings
27 | -> (info -> warnings -> OneOrMore.OneOrMore error -> result)
28 | -> (info -> warnings -> a -> result)
29 | -> result
30 | )
31 |
32 |
33 | run :: Result () [w] e a -> ([w], Either [e] a)
34 | run (Result k) =
35 | k () []
36 | (\() w e -> (reverse w, Left (OneOrMore.toList e)))
37 | (\() w a -> (reverse w, Right a))
38 |
39 |
40 |
41 | -- HELPERS
42 |
43 |
44 | ok :: a -> Result i w e a
45 | ok a =
46 | Result $ \i w _ good ->
47 | good i w a
48 |
49 |
50 | warn :: Warning.Warning -> Result i [Warning.Warning] e ()
51 | warn warning =
52 | Result $ \i warnings _ good ->
53 | good i (warning:warnings) ()
54 |
55 |
56 | throw :: e -> Result i w e a
57 | throw e =
58 | Result $ \i w bad _ ->
59 | bad i w (OneOrMore.one e)
60 |
61 |
62 | mapError :: (e -> e') -> Result i w e a -> Result i w e' a
63 | mapError func (Result k) =
64 | Result $ \i w bad good ->
65 | let
66 | bad1 i1 w1 e1 =
67 | bad i1 w1 (OneOrMore.map func e1)
68 | in
69 | k i w bad1 good
70 |
71 |
72 |
73 | -- FANCY INSTANCE STUFF
74 |
75 |
76 | instance Functor (Result i w e) where
77 | fmap func (Result k) =
78 | Result $ \i w bad good ->
79 | let
80 | good1 i1 w1 value =
81 | good i1 w1 (func value)
82 | in
83 | k i w bad good1
84 |
85 |
86 | instance Applicative (Result i w e) where
87 | pure = ok
88 |
89 | (<*>) (Result kf) (Result kv) =
90 | Result $ \i w bad good ->
91 | let
92 | bad1 i1 w1 e1 =
93 | let
94 | bad2 i2 w2 e2 = bad i2 w2 (OneOrMore.more e1 e2)
95 | good2 i2 w2 _value = bad i2 w2 e1
96 | in
97 | kv i1 w1 bad2 good2
98 |
99 | good1 i1 w1 func =
100 | let
101 | bad2 i2 w2 e2 = bad i2 w2 e2
102 | good2 i2 w2 value = good i2 w2 (func value)
103 | in
104 | kv i1 w1 bad2 good2
105 | in
106 | kf i w bad1 good1
107 |
108 |
109 | instance Monad (Result i w e) where
110 | return = ok
111 |
112 | (>>=) (Result ka) callback =
113 | Result $ \i w bad good ->
114 | let
115 | good1 i1 w1 a =
116 | case callback a of
117 | Result kb -> kb i1 w1 bad good
118 | in
119 | ka i w bad good1
120 |
--------------------------------------------------------------------------------
/terminal/src/Terminal/Args/Helpers.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | module Terminal.Args.Helpers
3 | ( version
4 | , elmFile
5 | , package
6 | )
7 | where
8 |
9 |
10 | import qualified Data.Char as Char
11 | import qualified Data.Text as Text
12 | import qualified System.FilePath as FP
13 |
14 | import Terminal.Args (Parser(..))
15 | import qualified Elm.Package as Pkg
16 |
17 |
18 |
19 | -- VERSION
20 |
21 |
22 | version :: Parser Pkg.Version
23 | version =
24 | Parser
25 | { _singular = "version"
26 | , _plural = "versions"
27 | , _parser = parseVersion
28 | , _suggest = suggestVersion
29 | , _examples = return . exampleVersions
30 | }
31 |
32 |
33 | parseVersion :: String -> Maybe Pkg.Version
34 | parseVersion str =
35 | Pkg.versionFromText (Text.pack str)
36 |
37 |
38 | suggestVersion :: String -> IO [String]
39 | suggestVersion _ =
40 | return []
41 |
42 |
43 | exampleVersions :: String -> [String]
44 | exampleVersions string =
45 | let
46 | chunks = map Text.unpack (Text.splitOn "." (Text.pack string))
47 | isNumber str = not (null str) && all Char.isDigit str
48 | in
49 | if all isNumber chunks then
50 | case chunks of
51 | [x] -> [ x ++ ".0.0" ]
52 | [x,y] -> [ x ++ "." ++ y ++ ".0" ]
53 | x:y:z:_ -> [ x ++ "." ++ y ++ "." ++ z ]
54 | _ -> ["1.0.0", "2.0.3"]
55 |
56 | else
57 | ["1.0.0", "2.0.3"]
58 |
59 |
60 |
61 | -- ELM FILE
62 |
63 |
64 | elmFile :: Parser FilePath
65 | elmFile =
66 | Parser
67 | { _singular = "elm file"
68 | , _plural = "elm files"
69 | , _parser = parseElmFile
70 | , _suggest = \_ -> return []
71 | , _examples = exampleElmFiles
72 | }
73 |
74 |
75 | parseElmFile :: String -> Maybe FilePath
76 | parseElmFile string =
77 | if FP.takeExtension string == ".elm" then
78 | Just string
79 | else
80 | Nothing
81 |
82 |
83 | exampleElmFiles :: String -> IO [String]
84 | exampleElmFiles _ =
85 | return ["Main.elm","src/Main.elm"]
86 |
87 |
88 |
89 | -- PACKAGE
90 |
91 |
92 | package :: Parser Pkg.Name
93 | package =
94 | Parser
95 | { _singular = "package"
96 | , _plural = "packages"
97 | , _parser = parsePackage
98 | , _suggest = suggestPackages
99 | , _examples = examplePackages
100 | }
101 |
102 |
103 | parsePackage :: String -> Maybe Pkg.Name
104 | parsePackage string =
105 | either (const Nothing) Just $
106 | Pkg.fromText (Text.pack string)
107 |
108 |
109 | suggestPackages :: String -> IO [String]
110 | suggestPackages _ =
111 | return []
112 |
113 |
114 | examplePackages :: String -> IO [String]
115 | examplePackages string =
116 | case Pkg.fromText (Text.pack string) of
117 | Left (_, suggestions@(_:_)) ->
118 | return suggestions
119 |
120 | _ ->
121 | return
122 | [ "elm/http"
123 | , "elm/json"
124 | , "elm-community/random-extra"
125 | ]
126 |
--------------------------------------------------------------------------------
/compiler/src/AST/Utils/Type.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module AST.Utils.Type
4 | ( delambda
5 | , dealias
6 | , deepDealias
7 | , iteratedDealias
8 | )
9 | where
10 |
11 |
12 | import qualified Data.Map as Map
13 |
14 | import AST.Canonical (Type(..), AliasType(..), FieldType(..))
15 | import qualified Elm.Name as N
16 |
17 |
18 |
19 | -- DELAMBDA
20 |
21 |
22 | delambda :: Type -> [Type]
23 | delambda tipe =
24 | case tipe of
25 | TLambda arg result ->
26 | arg : delambda result
27 |
28 | _ ->
29 | [tipe]
30 |
31 |
32 |
33 | -- DEALIAS
34 |
35 |
36 | dealias :: [(N.Name, Type)] -> AliasType -> Type
37 | dealias args aliasType =
38 | case aliasType of
39 | Holey tipe ->
40 | dealiasHelp (Map.fromList args) tipe
41 |
42 | Filled tipe ->
43 | tipe
44 |
45 |
46 | dealiasHelp :: Map.Map N.Name Type -> Type -> Type
47 | dealiasHelp typeTable tipe =
48 | case tipe of
49 | TLambda a b ->
50 | TLambda
51 | (dealiasHelp typeTable a)
52 | (dealiasHelp typeTable b)
53 |
54 | TVar x ->
55 | Map.findWithDefault tipe x typeTable
56 |
57 | TRecord fields ext ->
58 | TRecord (Map.map (dealiasField typeTable) fields) ext
59 |
60 | TAlias home name args t' ->
61 | TAlias home name (map (fmap (dealiasHelp typeTable)) args) t'
62 |
63 | TType home name args ->
64 | TType home name (map (dealiasHelp typeTable) args)
65 |
66 | TUnit ->
67 | TUnit
68 |
69 | TTuple a b maybeC ->
70 | TTuple
71 | (dealiasHelp typeTable a)
72 | (dealiasHelp typeTable b)
73 | (fmap (dealiasHelp typeTable) maybeC)
74 |
75 |
76 | dealiasField :: Map.Map N.Name Type -> FieldType -> FieldType
77 | dealiasField typeTable (FieldType index tipe) =
78 | FieldType index (dealiasHelp typeTable tipe)
79 |
80 |
81 |
82 | -- DEEP DEALIAS
83 |
84 |
85 | deepDealias :: Type -> Type
86 | deepDealias tipe =
87 | case tipe of
88 | TLambda a b ->
89 | TLambda (deepDealias a) (deepDealias b)
90 |
91 | TVar _ ->
92 | tipe
93 |
94 | TRecord fields ext ->
95 | TRecord (Map.map deepDealiasField fields) ext
96 |
97 | TAlias _ _ args tipe' ->
98 | deepDealias (dealias args tipe')
99 |
100 | TType home name args ->
101 | TType home name (map deepDealias args)
102 |
103 | TUnit ->
104 | TUnit
105 |
106 | TTuple a b c ->
107 | TTuple (deepDealias a) (deepDealias b) (fmap deepDealias c)
108 |
109 |
110 | deepDealiasField :: FieldType -> FieldType
111 | deepDealiasField (FieldType index tipe) =
112 | FieldType index (deepDealias tipe)
113 |
114 |
115 |
116 | -- ITERATED DEALIAS
117 |
118 |
119 | iteratedDealias :: Type -> Type
120 | iteratedDealias tipe =
121 | case tipe of
122 | TAlias _ _ args realType ->
123 | iteratedDealias (dealias args realType)
124 |
125 | _ ->
126 | tipe
127 |
--------------------------------------------------------------------------------
/compiler/src/Data/Index.hs:
--------------------------------------------------------------------------------
1 | module Data.Index
2 | ( ZeroBased
3 | , first
4 | , second
5 | , third
6 | , next
7 | , toMachine
8 | , toHuman
9 | , indexedMap
10 | , indexedTraverse
11 | , indexedForA
12 | , VerifiedList(..)
13 | , indexedZipWith
14 | , indexedZipWithA
15 | )
16 | where
17 |
18 |
19 | import Control.Monad (liftM)
20 | import Data.Binary
21 |
22 |
23 |
24 | -- ZERO BASED
25 |
26 |
27 | newtype ZeroBased = ZeroBased Int
28 | deriving (Eq, Ord)
29 |
30 |
31 | first :: ZeroBased
32 | first =
33 | ZeroBased 0
34 |
35 |
36 | second :: ZeroBased
37 | second =
38 | ZeroBased 1
39 |
40 |
41 | third :: ZeroBased
42 | third =
43 | ZeroBased 2
44 |
45 |
46 | {-# INLINE next #-}
47 | next :: ZeroBased -> ZeroBased
48 | next (ZeroBased i) =
49 | ZeroBased (i + 1)
50 |
51 |
52 |
53 | -- DESTRUCT
54 |
55 |
56 | toMachine :: ZeroBased -> Int
57 | toMachine (ZeroBased index) =
58 | index
59 |
60 |
61 | toHuman :: ZeroBased -> Int
62 | toHuman (ZeroBased index) =
63 | index + 1
64 |
65 |
66 |
67 | -- INDEXED MAP
68 |
69 |
70 | {-# INLINE indexedMap #-}
71 | indexedMap :: (ZeroBased -> a -> b) -> [a] -> [b]
72 | indexedMap func xs =
73 | zipWith func (map ZeroBased [0 .. length xs]) xs
74 |
75 |
76 | {-# INLINE indexedTraverse #-}
77 | indexedTraverse :: (Applicative f) => (ZeroBased -> a -> f b) -> [a] -> f [b]
78 | indexedTraverse func xs =
79 | sequenceA (indexedMap func xs)
80 |
81 |
82 | {-# INLINE indexedForA #-}
83 | indexedForA :: (Applicative f) => [a] -> (ZeroBased -> a -> f b) -> f [b]
84 | indexedForA xs func =
85 | sequenceA (indexedMap func xs)
86 |
87 |
88 |
89 | -- VERIFIED/INDEXED ZIP
90 |
91 |
92 | data VerifiedList a
93 | = LengthMatch [a]
94 | | LengthMismatch Int Int
95 |
96 |
97 | indexedZipWith :: (ZeroBased -> a -> b -> c) -> [a] -> [b] -> VerifiedList c
98 | indexedZipWith func listX listY =
99 | indexedZipWithHelp func 0 listX listY []
100 |
101 |
102 | indexedZipWithHelp :: (ZeroBased -> a -> b -> c) -> Int -> [a] -> [b] -> [c] -> VerifiedList c
103 | indexedZipWithHelp func index listX listY revListZ =
104 | case (listX, listY) of
105 | ([], []) ->
106 | LengthMatch (reverse revListZ)
107 |
108 | (x:xs, y:ys) ->
109 | indexedZipWithHelp func (index + 1) xs ys $
110 | func (ZeroBased index) x y : revListZ
111 |
112 | (_, _) ->
113 | LengthMismatch (index + length listX) (index + length listY)
114 |
115 |
116 | indexedZipWithA :: (Applicative f) => (ZeroBased -> a -> b -> f c) -> [a] -> [b] -> f (VerifiedList c)
117 | indexedZipWithA func listX listY =
118 | case indexedZipWith func listX listY of
119 | LengthMatch xs ->
120 | LengthMatch <$> sequenceA xs
121 |
122 | LengthMismatch x y ->
123 | pure (LengthMismatch x y)
124 |
125 |
126 |
127 | -- BINARY
128 |
129 |
130 | instance Binary ZeroBased where
131 | get = liftM ZeroBased get
132 | put (ZeroBased n) = put n
133 |
--------------------------------------------------------------------------------
/reactor/assets/styles.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 |
4 | /* FONTS */
5 |
6 | @font-face {
7 | font-family: 'Source Code Pro';
8 | font-style: normal;
9 | font-weight: 400;
10 | src: local('Source Code Pro'), local('SourceCodePro-Regular'), url(/_elm/source-code-pro.ttf) format('truetype');
11 | }
12 |
13 | @font-face {
14 | font-family: 'Source Sans Pro';
15 | font-style: normal;
16 | font-weight: 400;
17 | src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url(/_elm/source-sans-pro.ttf) format('truetype');
18 | }
19 |
20 |
21 | /* GENERIC STUFF */
22 |
23 | html, head, body {
24 | margin: 0;
25 | height: 100%;
26 | }
27 |
28 | body {
29 | font-family: 'Source Sans Pro', 'Trebuchet MS', 'Lucida Grande', 'Bitstream Vera Sans', 'Helvetica Neue', sans-serif;
30 | color: #293c4b;
31 | }
32 |
33 | a {
34 | color: #60B5CC;
35 | text-decoration: none;
36 | }
37 |
38 | a:hover {
39 | text-decoration: underline;
40 | }
41 |
42 |
43 | /* INDEX */
44 |
45 | .header {
46 | width: 100%;
47 | background-color: #60B5CC;
48 | height: 8px;
49 | }
50 |
51 | .content {
52 | width: 960px;
53 | margin-left: auto;
54 | margin-right: auto;
55 | }
56 |
57 |
58 | /* COLUMNS */
59 |
60 | .left-column {
61 | float: left;
62 | width: 600px;
63 | padding-bottom: 80px;
64 | }
65 |
66 | .right-column {
67 | float: right;
68 | width: 300px;
69 | padding-bottom: 80px;
70 | }
71 |
72 |
73 | /* BOXES */
74 |
75 | .box {
76 | border: 1px solid #c7c7c7;
77 | border-radius: 5px;
78 | margin-bottom: 40px;
79 | }
80 |
81 | .box-header {
82 | display: block;
83 | overflow: hidden;
84 | padding: 7px 12px;
85 | background-color: #fafafa;
86 | text-align: center;
87 | border-radius: 5px;
88 | }
89 |
90 | .box-item {
91 | display: block;
92 | overflow: hidden;
93 | padding: 7px 12px;
94 | border-top: 1px solid #e1e1e1;
95 | }
96 |
97 | .box-footer {
98 | display: block;
99 | overflow: hidden;
100 | padding: 2px 12px;
101 | border-top: 1px solid #e1e1e1;
102 | text-align: center;
103 | background-color: #fafafa;
104 | height: 16px;
105 | }
106 |
107 |
108 | /* ICONS */
109 |
110 | .icon {
111 | display: inline-block;
112 | vertical-align: middle;
113 | padding-right: 0.5em;
114 | }
115 |
116 |
117 | /* PAGES */
118 |
119 | .page-name {
120 | float: left;
121 | }
122 |
123 | .page-size {
124 | float: right;
125 | color: #293c4b;
126 | }
127 |
128 | .page-size:hover {
129 | color: #60B5CC;
130 | }
131 |
132 |
133 | /* WAITING */
134 |
135 | .waiting {
136 | width: 100%;
137 | height: 100%;
138 | display: flex;
139 | flex-direction: column;
140 | justify-content: center;
141 | align-items: center;
142 | color: #9A9A9A;
143 | }
144 |
145 |
146 | /* NOT FOUND */
147 |
148 | .not-found {
149 | width: 100%;
150 | height: 100%;
151 | display: flex;
152 | flex-direction: column;
153 | justify-content: center;
154 | align-items: center;
155 | background-color: #F5F5F5;
156 | color: #9A9A9A;
157 | }
158 |
--------------------------------------------------------------------------------
/compiler/src/Parse/Repl.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall -fno-warn-unused-do-bind #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Parse.Repl
4 | ( Entry(..)
5 | , parseEntry
6 | )
7 | where
8 |
9 |
10 | import qualified Data.ByteString.UTF8 as Utf8
11 | import qualified Data.Text as Text
12 | import Data.Text (Text)
13 |
14 | import qualified AST.Source as Src
15 | import qualified Elm.Name as N
16 | import qualified Parse.Module as Module
17 | import Parse.Primitives
18 | import qualified Parse.Primitives.Keyword as Keyword
19 | import qualified Parse.Primitives.Symbol as Symbol
20 | import qualified Parse.Primitives.Variable as Var
21 | import qualified Parse.Pattern as Pattern
22 | import qualified Reporting.Annotation as A
23 |
24 |
25 |
26 | -- ENTRY
27 |
28 |
29 | data Entry
30 | = Import N.Name (Maybe N.Name) Src.Exposing Text
31 | | Type N.Name Text
32 | | Def (Maybe N.Name) Text
33 | | Other Text
34 | | Annotation
35 | | Port
36 |
37 |
38 |
39 | -- PARSE
40 |
41 |
42 | parseEntry :: String -> Entry
43 | parseEntry rawEntry =
44 | let
45 | source =
46 | Text.pack rawEntry
47 | in
48 | case run (entryParser source) (Utf8.fromString rawEntry) of
49 | Right entry ->
50 | entry
51 |
52 | Left _ ->
53 | Other source
54 |
55 |
56 | entryParser :: Text -> Parser Entry
57 | entryParser source =
58 | oneOf
59 | [ do Keyword.import_
60 | spaces
61 | name <- Var.moduleName
62 | alias <- tryAlias
63 | exposing <- tryExposing
64 | return (Import name alias exposing source)
65 |
66 | , do Keyword.port_
67 | return Port
68 |
69 | , do Keyword.type_
70 | spaces
71 | oneOf
72 | [ do Keyword.alias_
73 | spaces
74 | , return ()
75 | ]
76 | name <- Var.upper
77 | return (Type name source)
78 |
79 | , do root <- Pattern.term
80 | spaces
81 | case A.toValue root of
82 | Src.PVar name ->
83 | oneOf
84 | [ do Symbol.hasType
85 | return Annotation
86 | , do chompArgs
87 | return (Def (Just name) source)
88 | ]
89 |
90 | _ ->
91 | do Symbol.equals
92 | return (Def Nothing source)
93 | ]
94 |
95 |
96 | chompArgs :: Parser ()
97 | chompArgs =
98 | oneOf
99 | [ do Pattern.term
100 | spaces
101 | chompArgs
102 | , do Symbol.equals
103 | return ()
104 | ]
105 |
106 |
107 | tryAlias :: Parser (Maybe N.Name)
108 | tryAlias =
109 | oneOf
110 | [ try $
111 | do spaces
112 | Keyword.as_
113 | spaces
114 | Just <$> Var.upper
115 | , return Nothing
116 | ]
117 |
118 |
119 | tryExposing :: Parser Src.Exposing
120 | tryExposing =
121 | oneOf
122 | [ try $
123 | do spaces
124 | Keyword.exposing_
125 | spaces
126 | Module.exposing
127 | , return (Src.Explicit [])
128 | ]
129 |
--------------------------------------------------------------------------------
/compiler/src/Canonicalize/Environment/Dups.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Canonicalize.Environment.Dups
4 | ( detect
5 | , checkFields
6 | , checkFields'
7 | , Dict
8 | , none
9 | , one
10 | , insert
11 | , union
12 | , unions
13 | )
14 | where
15 |
16 |
17 | import qualified Data.Map as Map
18 |
19 | import qualified Data.OneOrMore as OneOrMore
20 | import qualified Elm.Name as N
21 | import qualified Reporting.Annotation as A
22 | import qualified Reporting.Error.Canonicalize as Error
23 | import qualified Reporting.Region as R
24 | import qualified Reporting.Result as Result
25 |
26 |
27 |
28 | -- DUPLICATE TRACKER
29 |
30 |
31 | type Dict value =
32 | Map.Map N.Name (OneOrMore.OneOrMore (Info value))
33 |
34 |
35 | data Info value =
36 | Info
37 | { _region :: R.Region
38 | , _value :: value
39 | }
40 |
41 |
42 |
43 | -- DETECT
44 |
45 |
46 | type ToError =
47 | N.Name -> R.Region -> R.Region -> Error.Error
48 |
49 |
50 | detect :: ToError -> Dict a -> Result.Result i w Error.Error (Map.Map N.Name a)
51 | detect toError dict =
52 | Map.traverseWithKey (detectHelp toError) dict
53 |
54 |
55 | detectHelp :: ToError -> N.Name -> OneOrMore.OneOrMore (Info a) -> Result.Result i w Error.Error a
56 | detectHelp toError name values =
57 | case values of
58 | OneOrMore.One (Info _ value) ->
59 | return value
60 |
61 | OneOrMore.More _ _ ->
62 | let (Info r1 _ : Info r2 _ : _) = OneOrMore.toList values in
63 | Result.throw (toError name r1 r2)
64 |
65 |
66 |
67 | -- CHECK FIELDS
68 |
69 |
70 | checkFields :: [(A.Located N.Name, a)] -> Result.Result i w Error.Error (Map.Map N.Name a)
71 | checkFields fields =
72 | detect Error.DuplicateField (foldr addField none fields)
73 |
74 |
75 | addField :: (A.Located N.Name, a) -> Dict a -> Dict a
76 | addField (A.At region name, value) dups =
77 | Map.insertWith OneOrMore.more name (OneOrMore.one (Info region value)) dups
78 |
79 |
80 | checkFields' :: (R.Region -> a -> b) -> [(A.Located N.Name, a)] -> Result.Result i w Error.Error (Map.Map N.Name b)
81 | checkFields' toValue fields =
82 | detect Error.DuplicateField (foldr (addField' toValue) none fields)
83 |
84 |
85 | addField' :: (R.Region -> a -> b) -> (A.Located N.Name, a) -> Dict b -> Dict b
86 | addField' toValue (A.At region name, value) dups =
87 | Map.insertWith OneOrMore.more name (OneOrMore.one (Info region (toValue region value))) dups
88 |
89 |
90 |
91 | -- BUILDING DICTIONARIES
92 |
93 |
94 | none :: Dict a
95 | none =
96 | Map.empty
97 |
98 |
99 | one :: N.Name -> R.Region -> value -> Dict value
100 | one name region value =
101 | Map.singleton name (OneOrMore.one (Info region value))
102 |
103 |
104 | insert :: N.Name -> R.Region -> a -> Dict a -> Dict a
105 | insert name region value dict =
106 | Map.insertWith OneOrMore.more name (OneOrMore.one (Info region value)) dict
107 |
108 |
109 | union :: Dict a -> Dict a -> Dict a
110 | union a b =
111 | Map.unionWith OneOrMore.more a b
112 |
113 |
114 | unions :: [Dict a] -> Dict a
115 | unions dicts =
116 | Map.unionsWith OneOrMore.more dicts
117 |
--------------------------------------------------------------------------------
/terminal/src/Init.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | module Init
3 | ( run
4 | )
5 | where
6 |
7 |
8 | import Prelude hiding (init)
9 | import Control.Monad.Trans (liftIO)
10 | import qualified Data.Map as Map
11 | import qualified System.Directory as Dir
12 |
13 | import qualified Deps.Cache as Cache
14 | import qualified Deps.Explorer as Explorer
15 | import qualified Deps.Solver as Solver
16 | import qualified Elm.Compiler.Version as Compiler
17 | import qualified Elm.Package as Pkg
18 | import qualified Elm.Project.Constraint as Con
19 | import qualified Elm.Project.Json as Project
20 | import qualified Reporting.Doc as D
21 | import qualified Reporting.Exit as Exit
22 | import qualified Reporting.Exit.Init as E
23 | import qualified Reporting.Task as Task
24 | import qualified Reporting.Progress.Terminal as Terminal
25 |
26 |
27 |
28 | -- RUN
29 |
30 |
31 | run :: () -> () -> IO ()
32 | run () () =
33 | do reporter <- Terminal.create
34 | exists <- Dir.doesFileExist "elm.json"
35 | Task.run reporter $
36 | if exists then
37 | Task.throw (Exit.Init E.AlreadyStarted)
38 | else
39 | do approved <- Task.getApproval question
40 | if approved
41 | then
42 | do init
43 | liftIO $ putStrLn "Okay, I created it. Now read that link!"
44 | else
45 | liftIO $ putStrLn "Okay, I did not make any changes!"
46 |
47 |
48 | question :: D.Doc
49 | question =
50 | D.stack
51 | [ D.fillSep
52 | ["Hello!"
53 | ,"Elm","projects","always","start","with","an",D.green "elm.json","file."
54 | ,"I","can","create","them!"
55 | ]
56 | , D.reflow
57 | "Now you may be wondering, what will be in this file? How do I add Elm files to\
58 | \ my project? How do I see it in the browser? How will my code grow? Do I need\
59 | \ more directories? What about tests? Etc."
60 | , D.fillSep
61 | ["Check","out",D.cyan (D.fromString (D.makeLink "init"))
62 | ,"for","all","the","answers!"
63 | ]
64 | , "Knowing all that, would you like me to create an elm.json file now? [Y/n]: "
65 | ]
66 |
67 |
68 |
69 | -- INIT
70 |
71 |
72 | init :: Task.Task ()
73 | init =
74 | do registry <- Cache.optionalUpdate
75 |
76 | maybeSolution <-
77 | Explorer.run registry $ Solver.run $ Solver.solve defaults
78 |
79 | case maybeSolution of
80 | Just solution ->
81 | let
82 | directs = Map.intersection solution defaults
83 | indirects = Map.difference solution defaults
84 | in
85 | liftIO $
86 | do Dir.createDirectoryIfMissing True "src"
87 | Project.write "." $ Project.App $
88 | Project.AppInfo Compiler.version ["src"] directs indirects Map.empty Map.empty
89 |
90 | Nothing ->
91 | Task.throw (Exit.Init (E.NoSolution (Map.keys defaults)))
92 |
93 |
94 | defaults :: Map.Map Pkg.Name Con.Constraint
95 | defaults =
96 | Map.fromList
97 | [ (Pkg.core, Con.anything)
98 | , (Pkg.browser, Con.anything)
99 | , (Pkg.html, Con.anything)
100 | ]
101 |
--------------------------------------------------------------------------------
/compiler/src/AST/Valid.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module AST.Valid
4 | ( Expr, Expr_(..)
5 | , Def(..)
6 | , Module(..)
7 | , Decl(..)
8 | , Union(..)
9 | , Alias(..)
10 | , Binop(..)
11 | , Effects(..)
12 | , Port(..)
13 | , Manager(..)
14 | , defaultModule
15 | )
16 | where
17 |
18 |
19 | import qualified Data.Map as Map
20 | import Data.Text (Text)
21 |
22 | import qualified AST.Utils.Binop as Binop
23 | import qualified AST.Source as Src
24 | import qualified AST.Utils.Shader as Shader
25 | import qualified Elm.Name as N
26 | import qualified Reporting.Annotation as A
27 | import qualified Reporting.Region as R
28 |
29 |
30 |
31 | -- EXPRESSIONS
32 |
33 |
34 | type Expr = A.Located Expr_
35 |
36 |
37 | data Expr_
38 | = Chr Text
39 | | Str Text
40 | | Int Int
41 | | Float Double
42 | | Var Src.VarType N.Name
43 | | VarQual Src.VarType N.Name N.Name
44 | | List [Expr]
45 | | Op N.Name
46 | | Negate Expr
47 | | Binops [(Expr, A.Located N.Name)] Expr
48 | | Lambda [Src.Pattern] Expr
49 | | Call Expr [Expr]
50 | | If [(Expr, Expr)] Expr
51 | | Let [Def] Expr
52 | | Case Expr [(Src.Pattern, Expr)]
53 | | Accessor N.Name
54 | | Access Expr (A.Located N.Name)
55 | | Update (A.Located N.Name) [(A.Located N.Name, Expr)]
56 | | Record [(A.Located N.Name, Expr)]
57 | | Unit
58 | | Tuple Expr Expr [Expr]
59 | | Shader Text Text Shader.Shader
60 |
61 |
62 |
63 | -- DEFINITIONS
64 |
65 |
66 | data Def
67 | = Define R.Region (A.Located N.Name) [Src.Pattern] Expr (Maybe Src.Type)
68 | | Destruct R.Region Src.Pattern Expr
69 |
70 |
71 |
72 | -- MODULE
73 |
74 |
75 | data Module =
76 | Module
77 | { _name :: N.Name
78 | , _overview :: Src.Docs
79 | , _docs :: Map.Map N.Name Text
80 | , _exports :: A.Located Src.Exposing
81 | , _imports :: [Src.Import]
82 | , _decls :: [A.Located Decl]
83 | , _unions :: [Union]
84 | , _aliases :: [Alias]
85 | , _binop :: [Binop]
86 | , _effects :: Effects
87 | }
88 |
89 |
90 | data Decl = Decl (A.Located N.Name) [Src.Pattern] Expr (Maybe Src.Type)
91 | data Union = Union R.Region (A.Located N.Name) [A.Located N.Name] [(A.Located N.Name, [Src.Type])]
92 | data Alias = Alias R.Region (A.Located N.Name) [A.Located N.Name] Src.Type
93 | data Binop = Binop N.Name Binop.Associativity Binop.Precedence N.Name
94 |
95 |
96 | data Effects
97 | = NoEffects
98 | | Ports [Port]
99 | | Manager R.Region Manager
100 |
101 |
102 | data Manager
103 | = Cmd (A.Located N.Name)
104 | | Sub (A.Located N.Name)
105 | | Fx (A.Located N.Name) (A.Located N.Name)
106 |
107 |
108 | data Port = Port (A.Located N.Name) Src.Type
109 |
110 |
111 | defaultModule :: Map.Map N.Name Text -> [Src.Import] -> [A.Located Decl] -> [Union] -> [Alias] -> [Binop] -> Module
112 | defaultModule docs imports decls unions aliases binop =
113 | Module
114 | { _name = "Main"
115 | , _overview = Src.NoDocs R.one
116 | , _docs = docs
117 | , _exports = A.At R.one Src.Open
118 | , _imports = imports
119 | , _decls = decls
120 | , _unions = unions
121 | , _aliases = aliases
122 | , _binop = binop
123 | , _effects = NoEffects
124 | }
125 |
--------------------------------------------------------------------------------
/compiler/src/Parse/Primitives/Kernel.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall -fno-warn-unused-do-bind #-}
2 | {-# LANGUAGE BangPatterns, UnboxedTuples, OverloadedStrings #-}
3 | module Parse.Primitives.Kernel
4 | ( Special(..)
5 | , chunk
6 | )
7 | where
8 |
9 |
10 | import Prelude hiding (length)
11 | import qualified Data.ByteString.Internal as B
12 | import Data.Word (Word8)
13 | import Foreign.ForeignPtr (ForeignPtr)
14 |
15 | import qualified Elm.Name as N
16 | import Parse.Primitives.Internals (Parser(..), State(..), noError)
17 | import qualified Parse.Primitives.Internals as I
18 | import qualified Parse.Primitives.Variable as Var
19 |
20 |
21 |
22 | -- SPECIAL
23 |
24 |
25 | data Special
26 | = Enum Word8 N.Name
27 | | Prod
28 | | Debug
29 | | Import N.Name
30 | | JsField N.Name
31 | | ElmField N.Name
32 |
33 |
34 |
35 | -- CHUNK
36 |
37 |
38 | chunk :: Parser (B.ByteString, Maybe Special)
39 | chunk =
40 | Parser $ \(State fp offset terminal indent row col ctx) cok _ _ _ ->
41 | let
42 | (# maybeSpecial, jsOffset, newOffset, newRow, newCol #) =
43 | chompChunk fp offset terminal row col
44 |
45 | !javascript = B.PS fp offset (jsOffset - offset)
46 | !newState = State fp newOffset terminal indent newRow newCol ctx
47 | in
48 | cok (javascript, maybeSpecial) newState noError
49 |
50 |
51 |
52 | -- CHOMP CHUNK
53 |
54 |
55 | chompChunk :: ForeignPtr Word8 -> Int -> Int -> Int -> Int -> (# Maybe Special, Int, Int, Int, Int #)
56 | chompChunk fp offset terminal row col =
57 | if offset >= terminal then
58 | (# Nothing, offset, offset, row, col #)
59 |
60 | else
61 | let !word = I.unsafeIndex fp offset in
62 | if word == 0x5F {- _ -} then
63 |
64 | let
65 | !offset1 = offset + 1
66 | !offset3 = offset + 3
67 | in
68 | if offset3 <= terminal && I.unsafeIndex fp offset1 == 0x5F {- _ -} then
69 | chompSpecial fp offset3 terminal row (col + 3) offset
70 | else
71 | chompChunk fp offset1 terminal row (col + 1)
72 |
73 | else if word == 0x0A {- \n -} then
74 | chompChunk fp (offset + 1) terminal (row + 1) 1
75 |
76 | else
77 | let !newOffset = offset + I.getCharWidth fp offset terminal word in
78 | chompChunk fp newOffset terminal row (col + 1)
79 |
80 |
81 |
82 | -- CHOMP TAG
83 |
84 |
85 | -- relies on external checks in chompChunk
86 | chompSpecial :: ForeignPtr Word8 -> Int -> Int -> Int -> Int -> Int -> (# Maybe Special, Int, Int, Int, Int #)
87 | chompSpecial fp offset terminal row col jsOffset =
88 | let
89 | (# newOffset, newCol #) =
90 | Var.chompInnerChars fp offset terminal col
91 |
92 | !tagOffset = offset - 1
93 | !word = I.unsafeIndex fp tagOffset
94 |
95 | !special =
96 | if word == 0x24 {- $ -} then
97 | ElmField (N.fromForeignPtr fp offset (newOffset - offset))
98 |
99 | else
100 | let !name = N.fromForeignPtr fp tagOffset (newOffset - tagOffset) in
101 | if 0x30 <= word && word <= 0x39 then
102 | Enum (fromIntegral (word - 0x30)) name
103 |
104 | else if 0x61 {- a -} <= word && word <= 0x7A {- z -} then
105 | JsField name
106 |
107 | else if name == "DEBUG" then
108 | Debug
109 |
110 | else if name == "PROD" then
111 | Prod
112 |
113 | else
114 | Import name
115 | in
116 | (# Just special, jsOffset, newOffset, row, newCol #)
117 |
--------------------------------------------------------------------------------
/compiler/src/Compile.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall -fno-warn-unused-do-bind #-}
2 | module Compile
3 | ( DocsFlag(..)
4 | , compile
5 | , Artifacts(..)
6 | )
7 | where
8 |
9 |
10 | import qualified Data.ByteString as BS
11 | import qualified Data.Map as Map
12 |
13 | import qualified AST.Canonical as Can
14 | import qualified AST.Optimized as Opt
15 | import qualified AST.Module.Name as ModuleName
16 | import qualified Canonicalize.Module as Canonicalize
17 | import qualified Elm.Docs as Docs
18 | import qualified Elm.Interface as I
19 | import qualified Elm.Name as N
20 | import qualified Elm.Package as Pkg
21 | import qualified Nitpick.PatternMatches as PatternMatches
22 | import qualified Optimize.Module as Optimize
23 | import qualified Parse.Parse as Parse
24 | import qualified Reporting.Error as Error
25 | import qualified Reporting.Render.Type.Localizer as L
26 | import qualified Reporting.Result as Result
27 | import qualified Reporting.Warning as Warning
28 | import qualified Type.Constrain.Module as Type
29 | import qualified Type.Solve as Type
30 |
31 | import System.IO.Unsafe (unsafePerformIO)
32 |
33 |
34 |
35 | -- COMPILE
36 |
37 |
38 | type Result i a =
39 | Result.Result i [Warning.Warning] Error.Error a
40 |
41 |
42 | type ImportDict =
43 | Map.Map N.Name ModuleName.Canonical
44 |
45 |
46 | data Artifacts =
47 | Artifacts
48 | { _elmi :: I.Interface
49 | , _elmo :: Opt.Graph
50 | , _docs :: Maybe Docs.Module
51 | }
52 |
53 |
54 | compile :: DocsFlag -> Pkg.Name -> ImportDict -> I.Interfaces -> BS.ByteString -> Result i Artifacts
55 | compile flag pkg importDict interfaces source =
56 | do
57 | valid <- Result.mapError Error.Syntax $
58 | Parse.program pkg source
59 |
60 | canonical <- Result.mapError Error.Canonicalize $
61 | Canonicalize.canonicalize pkg importDict interfaces valid
62 |
63 | let localizer = L.fromModule valid -- TODO should this be strict for GC?
64 |
65 | annotations <-
66 | runTypeInference localizer canonical
67 |
68 | () <-
69 | exhaustivenessCheck canonical
70 |
71 | graph <- Result.mapError (Error.Main localizer) $
72 | Optimize.optimize annotations canonical
73 |
74 | documentation <-
75 | genarateDocs flag canonical
76 |
77 | Result.ok $
78 | Artifacts
79 | { _elmi = I.fromModule annotations canonical
80 | , _elmo = graph
81 | , _docs = documentation
82 | }
83 |
84 |
85 |
86 | -- TYPE INFERENCE
87 |
88 |
89 | runTypeInference :: L.Localizer -> Can.Module -> Result i (Map.Map N.Name Can.Annotation)
90 | runTypeInference localizer canonical =
91 | case unsafePerformIO (Type.run =<< Type.constrain canonical) of
92 | Right annotations ->
93 | Result.ok annotations
94 |
95 | Left errors ->
96 | Result.throw (Error.Type localizer errors)
97 |
98 |
99 |
100 | -- EXHAUSTIVENESS CHECK
101 |
102 |
103 | exhaustivenessCheck :: Can.Module -> Result i ()
104 | exhaustivenessCheck canonical =
105 | case PatternMatches.check canonical of
106 | Left errors ->
107 | Result.throw (Error.Pattern errors)
108 |
109 | Right () ->
110 | Result.ok ()
111 |
112 |
113 |
114 | -- DOCUMENTATION
115 |
116 |
117 | data DocsFlag = YesDocs | NoDocs
118 |
119 |
120 | genarateDocs :: DocsFlag -> Can.Module -> Result.Result i w Error.Error (Maybe Docs.Module)
121 | genarateDocs flag modul =
122 | case flag of
123 | NoDocs ->
124 | Result.ok Nothing
125 |
126 | YesDocs ->
127 | Just <$> Docs.fromModule modul
128 |
--------------------------------------------------------------------------------
/docs/elm.json/package.md:
--------------------------------------------------------------------------------
1 | # `elm.json` for packages
2 |
3 | This is roughly `elm.json` for the `elm/json` package:
4 |
5 | ```json
6 | {
7 | "type": "package",
8 | "name": "elm/json",
9 | "summary": "Encode and decode JSON values",
10 | "license": "BSD-3-Clause",
11 | "version": "1.0.0",
12 | "exposed-modules": [
13 | "Json.Decode",
14 | "Json.Encode"
15 | ],
16 | "elm-version": "0.19.0 <= v < 0.20.0",
17 | "dependencies": {
18 | "elm/core": "1.0.0 <= v < 2.0.0"
19 | },
20 | "test-dependencies": {}
21 | }
22 | ```
23 |
24 |
25 |
26 |
27 | ## `"type"`
28 |
29 | Either `"application"` or `"package"`. All the other fields are based on this choice.
30 |
31 |
32 |
33 |
34 | ## `"name"`
35 |
36 | The name of a GitHub repo like `"elm-lang/core"` or `"rtfeldman/elm-css"`.
37 |
38 | > **Note:** We currently only support GitHub repos to ensure that there are no author name collisions. This seems like a pretty tricky problem to solve in a pleasant way. For example, do we have to keep an author name registry and give them out as we see them? But if someone is the same person on two platforms? And how to make this all happen in a way this is really nice for typical Elm users? Etc. So adding other hosting endpoints is harder than it sounds.
39 |
40 |
41 |
42 |
43 | ## `"summary"`
44 |
45 | A short summary that will appear on [`package.elm-lang.org`](https://package.elm-lang.org/) that describes what the package is for. Must be under 80 characters.
46 |
47 |
48 |
49 |
50 | ## `"license"`
51 |
52 | An OSI approved SPDX code like `"BSD-3-Clause"` or `"MIT"`. These are the two most common licenses in the Elm ecosystem, but you can see the full list of options [here](https://spdx.org/licenses/).
53 |
54 |
55 |
56 |
57 | ## `"version"`
58 |
59 | All packages start at `"1.0.0"` and from there, Elm automatically enforces semantic versioning by comparing API changes.
60 |
61 | So if you make a PATCH change and call `elm bump` it will update you to `"1.0.1"`. And if you then decide to remove a function (a MAJOR change) and call `elm bump` it will update you to `"2.0.0"`. Etc.
62 |
63 |
64 |
65 |
66 | ## `"exposed-modules"`
67 |
68 | A list of modules that will be exposed to people using your package. The order you list them will be the order they appear on [`package.elm-lang.org`](https://package.elm-lang.org/).
69 |
70 | **Note:** If you have five or more modules, you can use a labelled list like [this](https://github.com/elm-lang/core/blob/master/elm.json). We show the labels on the package website to help people sort through larger packages with distinct categories. Labels must be under 20 characters.
71 |
72 |
73 |
74 |
75 | ## `"elm-version"`
76 |
77 | The range of Elm compilers that work with your package. Right now `"0.19.0 <= v < 0.20.0"` is always what you want for this.
78 |
79 |
80 |
81 |
82 | ## `"dependencies"`
83 |
84 | A list of packages that you depend upon. In each application, there can only be one version of each package, so wide ranges are great. Fewer dependencies is even better though!
85 |
86 | > **Note:** Dependency ranges should only express _tested_ ranges. It is not nice to use optimistic ranges and end up causing build failures for your users down the line. Eventually we would like to have an automated system that tries to build and test packages as new packages come out. If it all works, we could send a PR to the author widening the range.
87 |
88 |
89 |
90 |
91 | ## `"test-dependencies"`
92 |
93 | Dependencies that are only used in the `tests/` directory by `elm test`. Values from these packages will not appear in any final build artifacts.
94 |
--------------------------------------------------------------------------------
/compiler/src/Reporting/Render/Type/Localizer.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Reporting.Render.Type.Localizer
4 | ( Localizer
5 | , toDoc
6 | , toString
7 | , empty
8 | , fromNames
9 | , fromModule
10 | , replEmpty
11 | , replAdd
12 | )
13 | where
14 |
15 |
16 | import qualified Data.Map as Map
17 | import qualified Data.Set as Set
18 |
19 | import qualified AST.Source as Src
20 | import qualified AST.Module.Name as ModuleName
21 | import qualified AST.Valid as Valid
22 | import qualified Elm.Compiler.Imports as Imports
23 | import qualified Elm.Name as N
24 | import qualified Elm.Package as Pkg
25 | import Reporting.Doc ((<>))
26 | import qualified Reporting.Doc as D
27 | import qualified Reporting.Annotation as A
28 |
29 |
30 |
31 | -- LOCALIZER
32 |
33 |
34 | newtype Localizer =
35 | Localizer (Map.Map N.Name Import)
36 |
37 |
38 | data Import =
39 | Import
40 | { _alias :: Maybe N.Name
41 | , _exposing :: Exposing
42 | }
43 |
44 |
45 | data Exposing
46 | = All
47 | | Only (Set.Set N.Name)
48 |
49 |
50 | empty :: Localizer
51 | empty =
52 | Localizer Map.empty
53 |
54 |
55 |
56 | -- LOCALIZE
57 |
58 |
59 | toDoc :: Localizer -> ModuleName.Canonical -> N.Name -> D.Doc
60 | toDoc localizer home name =
61 | D.fromString (toString localizer home name)
62 |
63 |
64 | toString :: Localizer -> ModuleName.Canonical -> N.Name -> String
65 | toString (Localizer localizer) moduleName@(ModuleName.Canonical _ home) name =
66 | case Map.lookup home localizer of
67 | Nothing ->
68 | N.toString home <> "." <> N.toString name
69 |
70 | Just (Import alias exposing) ->
71 | case exposing of
72 | All ->
73 | N.toString name
74 |
75 | Only set ->
76 | if Set.member name set then
77 | N.toString name
78 | else if name == N.list && moduleName == ModuleName.list then
79 | "List"
80 | else
81 | N.toString (maybe home id alias) <> "." <> N.toString name
82 |
83 |
84 |
85 | -- FROM NAMES
86 |
87 |
88 | fromNames :: Map.Map N.Name a -> Localizer
89 | fromNames names =
90 | Localizer $ Map.map (\_ -> Import Nothing All) names
91 |
92 |
93 |
94 | -- FROM MODULE
95 |
96 |
97 | fromModule :: Valid.Module -> Localizer
98 | fromModule (Valid.Module name _ _ _ imports _ _ _ _ _) =
99 | Localizer $ Map.fromList $
100 | (name, Import Nothing All) : map toPair imports
101 |
102 |
103 | toPair :: Src.Import -> (N.Name, Import)
104 | toPair (Src.Import (A.At _ name) alias exposing) =
105 | ( name
106 | , Import alias (toExposing exposing)
107 | )
108 |
109 |
110 | toExposing :: Src.Exposing -> Exposing
111 | toExposing exposing =
112 | case exposing of
113 | Src.Open ->
114 | All
115 |
116 | Src.Explicit exposedList ->
117 | Only (foldr addType Set.empty exposedList)
118 |
119 |
120 | addType :: A.Located Src.Exposed -> Set.Set N.Name -> Set.Set N.Name
121 | addType (A.At _ exposed) types =
122 | case exposed of
123 | Src.Lower _ -> types
124 | Src.Upper name _ -> Set.insert name types
125 | Src.Operator _ -> types
126 |
127 |
128 |
129 | -- REPL STUFF
130 |
131 |
132 | replEmpty :: Localizer
133 | replEmpty =
134 | Localizer $
135 | Map.insert N.replModule (Import Nothing All) $
136 | Map.fromList $ map toPair $ Imports.addDefaults Pkg.dummyName []
137 |
138 |
139 | replAdd :: N.Name -> Maybe N.Name -> Src.Exposing -> Localizer -> Localizer
140 | replAdd name alias exposing (Localizer localizer) =
141 | Localizer $ Map.insert name (Import alias (toExposing exposing)) localizer
142 |
143 |
--------------------------------------------------------------------------------
/compiler/src/Reporting/Warning.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Reporting.Warning
4 | ( Warning(..)
5 | , Context(..)
6 | , toReport
7 | )
8 | where
9 |
10 |
11 | import Data.Monoid ((<>))
12 |
13 | import qualified AST.Canonical as Can
14 | import qualified AST.Utils.Type as Type
15 | import qualified Elm.Name as N
16 | import qualified Reporting.Doc as D
17 | import qualified Reporting.Region as R
18 | import qualified Reporting.Report as Report
19 | import qualified Reporting.Render.Code as Code
20 | import qualified Reporting.Render.Type as RT
21 | import qualified Reporting.Render.Type.Localizer as L
22 |
23 |
24 |
25 | -- ALL POSSIBLE WARNINGS
26 |
27 |
28 | data Warning
29 | = UnusedImport R.Region N.Name
30 | | UnusedVariable R.Region Context N.Name
31 | | MissingTypeAnnotation R.Region N.Name Can.Type
32 |
33 |
34 | data Context = Def | Pattern
35 |
36 |
37 |
38 | -- TO REPORT
39 |
40 |
41 | toReport :: L.Localizer -> Code.Source -> Warning -> Report.Report
42 | toReport localizer source warning =
43 | case warning of
44 | UnusedImport region moduleName ->
45 | Report.Report "unused import" region [] $
46 | Report.toCodeSnippet source region Nothing
47 | (
48 | D.reflow $
49 | "Nothing from the `" <> N.toString moduleName <> "` module is used in this file."
50 | ,
51 | "I recommend removing unused imports."
52 | )
53 |
54 | UnusedVariable region context name ->
55 | let title = defOrPat context "unused definition" "unused variable" in
56 | Report.Report title region [] $
57 | Report.toCodeSnippet source region Nothing
58 | (
59 | D.reflow $
60 | "You are not using `" <> N.toString name <> "` anywhere."
61 | ,
62 | D.stack
63 | [ D.reflow $
64 | "Is there a typo? Maybe you intended to use `" <> N.toString name
65 | <> "` somewhere but typed another name instead?"
66 | , D.reflow $
67 | defOrPat context
68 | ( "If you are sure there is no typo, remove the definition.\
69 | \ This way future readers will not have to wonder why it is there!"
70 | )
71 | ( "If you are sure there is no typo, replace `" <> N.toString name
72 | <> "` with _ so future readers will not have to wonder why it is there!"
73 | )
74 | ]
75 | )
76 |
77 | MissingTypeAnnotation region name inferredType ->
78 | Report.Report "missing type annotation" region [] $
79 | Report.toCodeSnippet source region Nothing
80 | (
81 | D.reflow $
82 | case Type.deepDealias inferredType of
83 | Can.TLambda _ _ ->
84 | "The `" <> N.toString name <> "` function has no type annotation."
85 |
86 | _ ->
87 | "The `" <> N.toString name <> "` definition has no type annotation."
88 | ,
89 | D.stack
90 | [ "I inferred the type annotation myself though! You can copy it into your code:"
91 | , D.green $ D.hang 4 $ D.sep $
92 | [ D.fromName name <> " :"
93 | , RT.canToDoc localizer RT.None inferredType
94 | ]
95 | ]
96 | )
97 |
98 |
99 | defOrPat :: Context -> a -> a -> a
100 | defOrPat context def pat =
101 | case context of
102 | Def -> def
103 | Pattern -> pat
104 |
105 |
--------------------------------------------------------------------------------
/compiler/src/Parse/Shader.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Parse.Shader
4 | ( shader
5 | )
6 | where
7 |
8 |
9 | import qualified Data.List as List
10 | import qualified Data.Map as Map
11 | import qualified Data.Text as Text
12 | import qualified Language.GLSL.Parser as GLP
13 | import qualified Language.GLSL.Syntax as GLS
14 | import qualified Text.Parsec as Parsec
15 | import qualified Text.Parsec.Error as Parsec
16 |
17 | import qualified AST.Source as Src
18 | import qualified AST.Utils.Shader as Shader
19 | import qualified Elm.Name as N
20 | import qualified Reporting.Annotation as A
21 | import qualified Reporting.Region as R
22 | import Parse.Primitives (Parser, getPosition)
23 | import qualified Parse.Primitives.Shader as Shader
24 |
25 |
26 |
27 | -- SHADERS
28 |
29 |
30 | shader :: R.Position -> Parser Src.Expr
31 | shader start@(R.Position row col) =
32 | do block <- Shader.block
33 | shdr <- parseSource row col (Text.unpack block)
34 | end@(R.Position row2 col2) <- getPosition
35 | let uid = List.intercalate ":" (map show [row, col, row2, col2])
36 | let src = Text.replace "\n" "\\n" (Text.replace "\r\n" "\\n" block)
37 | return (A.at start end (Src.Shader (Text.pack uid) src shdr))
38 |
39 |
40 | parseSource :: Int -> Int -> String -> Parser Shader.Shader
41 | parseSource startRow startCol src =
42 | case GLP.parse src of
43 | Right (GLS.TranslationUnit decls) ->
44 | return (foldr addInput emptyShader (concatMap extractInputs decls))
45 |
46 | Left err ->
47 | let
48 | pos = Parsec.errorPos err
49 | row = Parsec.sourceLine pos
50 | col = Parsec.sourceColumn pos
51 | msg =
52 | Parsec.showErrorMessages
53 | "or"
54 | "unknown parse error"
55 | "expecting"
56 | "unexpected"
57 | "end of input"
58 | (Parsec.errorMessages err)
59 | in
60 | if row == 1 then
61 | Shader.failure startRow (startCol + 6 + col) (Text.pack msg)
62 | else
63 | Shader.failure (startRow + row - 1) col (Text.pack msg)
64 |
65 |
66 | emptyShader :: Shader.Shader
67 | emptyShader =
68 | Shader.Shader Map.empty Map.empty Map.empty
69 |
70 |
71 | addInput :: (GLS.StorageQualifier, Shader.Type, String) -> Shader.Shader -> Shader.Shader
72 | addInput (qual, tipe, name) glDecls =
73 | case qual of
74 | GLS.Attribute -> glDecls { Shader._attribute = Map.insert (N.fromString name) tipe (Shader._attribute glDecls) }
75 | GLS.Uniform -> glDecls { Shader._uniform = Map.insert (N.fromString name) tipe (Shader._uniform glDecls) }
76 | GLS.Varying -> glDecls { Shader._varying = Map.insert (N.fromString name) tipe (Shader._varying glDecls) }
77 | _ -> error "Should never happen due to `extractInputs` function"
78 |
79 |
80 | extractInputs :: GLS.ExternalDeclaration -> [(GLS.StorageQualifier, Shader.Type, String)]
81 | extractInputs decl =
82 | case decl of
83 | GLS.Declaration
84 | (GLS.InitDeclaration
85 | (GLS.TypeDeclarator
86 | (GLS.FullType
87 | (Just (GLS.TypeQualSto qual))
88 | (GLS.TypeSpec _prec (GLS.TypeSpecNoPrecision tipe _mexpr1))))
89 | [GLS.InitDecl name _mexpr2 _mexpr3]
90 | ) ->
91 | case elem qual [GLS.Attribute, GLS.Varying, GLS.Uniform] of
92 | False -> []
93 | True ->
94 | case tipe of
95 | GLS.Vec2 -> [(qual, Shader.V2, name)]
96 | GLS.Vec3 -> [(qual, Shader.V3, name)]
97 | GLS.Vec4 -> [(qual, Shader.V4, name)]
98 | GLS.Mat4 -> [(qual, Shader.M4, name)]
99 | GLS.Int -> [(qual, Shader.Int, name)]
100 | GLS.Float -> [(qual, Shader.Float, name)]
101 | GLS.Sampler2D -> [(qual, Shader.Texture, name)]
102 | _ -> []
103 | _ -> []
104 |
--------------------------------------------------------------------------------
/compiler/src/AST/Source.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module AST.Source
3 | ( Expr, Expr_(..), VarType(..)
4 | , Decl, Decl_(..)
5 | , Def(..)
6 | , Pattern, Pattern_(..)
7 | , Type, Type_(..)
8 | , Module(..)
9 | , Header(..)
10 | , Docs(..)
11 | , Import(..)
12 | , Effects(..)
13 | , Manager(..)
14 | , Exposing(..)
15 | , Exposed(..)
16 | , Privacy(..)
17 | )
18 | where
19 |
20 |
21 | import qualified Data.ByteString as B
22 | import Data.Text (Text)
23 |
24 | import qualified AST.Utils.Binop as Binop
25 | import qualified AST.Utils.Shader as Shader
26 | import qualified Elm.Name as N
27 | import qualified Reporting.Annotation as A
28 | import qualified Reporting.Region as R
29 |
30 |
31 |
32 | -- EXPRESSIONS
33 |
34 |
35 | type Expr = A.Located Expr_
36 |
37 |
38 | data Expr_
39 | = Chr Text
40 | | Str Text
41 | | Int Int
42 | | Float Double
43 | | Var VarType N.Name
44 | | VarQual VarType N.Name N.Name
45 | | List [Expr]
46 | | Op N.Name
47 | | Negate Expr
48 | | Binops [(Expr, A.Located N.Name)] Expr
49 | | Lambda [Pattern] Expr
50 | | Call Expr [Expr]
51 | | If [(Expr, Expr)] Expr
52 | | Let [A.Located Def] Expr
53 | | Case Expr [(Pattern, Expr)]
54 | | Accessor N.Name
55 | | Access Expr (A.Located N.Name)
56 | | Update (A.Located N.Name) [(A.Located N.Name, Expr)]
57 | | Record [(A.Located N.Name, Expr)]
58 | | Unit
59 | | Tuple Expr Expr [Expr]
60 | | Shader Text Text Shader.Shader
61 |
62 |
63 | data VarType = Value | Ctor
64 |
65 |
66 |
67 | -- DEFINITIONS
68 |
69 |
70 | data Def
71 | = Annotate N.Name Type
72 | | Define (A.Located N.Name) [Pattern] Expr
73 | | Destruct Pattern Expr
74 |
75 |
76 |
77 | -- PATTERN
78 |
79 |
80 | type Pattern = A.Located Pattern_
81 |
82 |
83 | data Pattern_
84 | = PAnything
85 | | PVar N.Name
86 | | PRecord [A.Located N.Name]
87 | | PAlias Pattern (A.Located N.Name)
88 | | PUnit
89 | | PTuple Pattern Pattern [Pattern]
90 | | PCtor R.Region N.Name [Pattern]
91 | | PCtorQual R.Region N.Name N.Name [Pattern]
92 | | PList [Pattern]
93 | | PCons Pattern Pattern
94 | | PChr Text
95 | | PStr Text
96 | | PInt Int
97 |
98 |
99 |
100 | -- TYPE
101 |
102 |
103 | type Type =
104 | A.Located Type_
105 |
106 |
107 | data Type_
108 | = TLambda Type Type
109 | | TVar N.Name
110 | | TType R.Region N.Name [Type]
111 | | TTypeQual R.Region N.Name N.Name [Type]
112 | | TRecord [(A.Located N.Name, Type)] (Maybe (A.Located N.Name))
113 | | TUnit
114 | | TTuple Type Type [Type]
115 |
116 |
117 |
118 | -- DECLARATIONS
119 |
120 |
121 | type Decl = A.Located Decl_
122 |
123 |
124 | data Decl_
125 | = Union (A.Located N.Name) [A.Located N.Name] [(A.Located N.Name, [Type])]
126 | | Alias (A.Located N.Name) [A.Located N.Name] Type
127 | | Binop N.Name Binop.Associativity Binop.Precedence N.Name
128 | | Port (A.Located N.Name) Type
129 | | Docs Text
130 | | Annotation (A.Located N.Name) Type
131 | | Definition (A.Located N.Name) [Pattern] Expr
132 |
133 |
134 |
135 | -- MODULE
136 |
137 |
138 | data Module decls =
139 | Module (Maybe Header) [Import] decls
140 |
141 |
142 | data Header
143 | = Header
144 | { _name :: N.Name
145 | , _effects :: Effects
146 | , _exports :: A.Located Exposing
147 | , _docs :: Docs
148 | }
149 |
150 |
151 | data Import =
152 | Import
153 | { _import :: A.Located N.Name
154 | , _alias :: Maybe N.Name
155 | , _exposing :: Exposing
156 | }
157 |
158 |
159 | data Docs
160 | = NoDocs R.Region
161 | | YesDocs R.Region B.ByteString
162 |
163 |
164 | data Effects
165 | = NoEffects
166 | | Ports R.Region
167 | | Manager R.Region Manager
168 |
169 |
170 | data Manager
171 | = Cmd (A.Located N.Name)
172 | | Sub (A.Located N.Name)
173 | | Fx (A.Located N.Name) (A.Located N.Name)
174 |
175 |
176 |
177 | -- EXPOSING
178 |
179 |
180 | data Exposing
181 | = Open
182 | | Explicit ![A.Located Exposed]
183 |
184 |
185 | data Exposed
186 | = Lower !N.Name
187 | | Upper !N.Name !Privacy
188 | | Operator !N.Name
189 |
190 |
191 | data Privacy
192 | = Public
193 | | Private
194 |
--------------------------------------------------------------------------------
/terminal/src/Develop/Generate/Index.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Develop.Generate.Index
4 | ( get
5 | )
6 | where
7 |
8 |
9 | import Control.Monad (filterM)
10 | import qualified Data.ByteString.Builder as B
11 | import qualified Data.Map as Map
12 | import qualified Data.Text as Text
13 | import qualified Data.Text.IO as Text
14 | import qualified System.Directory as Dir
15 | import System.FilePath ((>), splitDirectories, takeExtension)
16 |
17 | import qualified Develop.Generate.Help as Help
18 | import qualified Elm.Package as Pkg
19 | import qualified Elm.Project.Json as Project
20 | import qualified Elm.Project.Summary as Summary
21 | import qualified Json.Encode as E
22 | import qualified Reporting.Progress as Progress
23 | import qualified Reporting.Task as Task
24 | import qualified Stuff.Verify as Verify
25 |
26 |
27 |
28 | -- GET
29 |
30 |
31 | get :: FilePath -> FilePath -> IO B.Builder
32 | get root pwd =
33 | do flags <- getFlags root pwd
34 | return $ Help.makePageHtml "Index" (Just (encode flags))
35 |
36 |
37 |
38 | -- FLAGS
39 |
40 |
41 | data Flags =
42 | Flags
43 | { _root :: FilePath
44 | , _pwd :: [String]
45 | , _dirs :: [FilePath]
46 | , _files :: [(FilePath, Bool)]
47 | , _readme :: Maybe Text.Text
48 | , _project :: Maybe Project.Project
49 | , _exactDeps :: Map.Map Pkg.Name Pkg.Version
50 | }
51 |
52 |
53 |
54 | -- JSON
55 |
56 |
57 | encode :: Flags -> E.Value
58 | encode (Flags root pwd dirs files readme project exactDeps) =
59 | E.object
60 | [ ( "root", encodeFilePath root )
61 | , ( "pwd", E.list encodeFilePath pwd )
62 | , ( "dirs", E.list encodeFilePath dirs )
63 | , ( "files", E.list encodeFile files )
64 | , ( "readme", maybe E.null E.text readme )
65 | , ( "project", maybe E.null Project.encode project )
66 | , ( "exactDeps", E.dict Pkg.toText Pkg.encodeVersion exactDeps)
67 | ]
68 |
69 |
70 | encodeFilePath :: FilePath -> E.Value
71 | encodeFilePath filePath =
72 | E.text (Text.pack filePath)
73 |
74 |
75 | encodeFile :: (FilePath, Bool) -> E.Value
76 | encodeFile (file, hasMain) =
77 | E.object
78 | [ ("name", encodeFilePath file)
79 | , ("runnable", E.bool hasMain)
80 | ]
81 |
82 |
83 |
84 | -- GET FLAGS
85 |
86 |
87 | getFlags :: FilePath -> FilePath -> IO Flags
88 | getFlags root pwd =
89 | do (dirs, files) <- getDirsAndFiles pwd
90 | readme <- getReadme pwd
91 | exists <- Dir.doesFileExist (root > "elm.json")
92 |
93 | maybeSummary <-
94 | if exists then
95 | Task.try Progress.silentReporter $
96 | Verify.verify root =<< Project.read (root > "elm.json")
97 | else
98 | return Nothing
99 |
100 | return $
101 | Flags
102 | { _root = root
103 | , _pwd = dropWhile ("." ==) (splitDirectories pwd)
104 | , _dirs = dirs
105 | , _files = files
106 | , _readme = readme
107 | , _project = fmap Summary._project maybeSummary
108 | , _exactDeps = maybe Map.empty (Map.map fst . Summary._depsGraph) maybeSummary
109 | }
110 |
111 |
112 | getReadme :: FilePath -> IO (Maybe Text.Text)
113 | getReadme dir =
114 | do let readmePath = dir > "README.md"
115 | exists <- Dir.doesFileExist readmePath
116 | if exists
117 | then Just <$> Text.readFile readmePath
118 | else return Nothing
119 |
120 |
121 | getDirsAndFiles :: FilePath -> IO ([FilePath], [(FilePath, Bool)])
122 | getDirsAndFiles pwd =
123 | do contents <- Dir.getDirectoryContents pwd
124 | dirs <- filterM (Dir.doesDirectoryExist . (pwd >)) contents
125 | filePaths <- filterM (Dir.doesFileExist . (pwd >)) contents
126 | files <- mapM (inspectFile pwd) filePaths
127 | return (dirs, files)
128 |
129 |
130 | inspectFile :: FilePath -> FilePath -> IO (FilePath, Bool)
131 | inspectFile pwd path =
132 | if takeExtension path == ".elm" then
133 | do source <- Text.readFile (pwd > path)
134 | let hasMain = Text.isInfixOf "\nmain " source
135 | return (path, hasMain)
136 |
137 | else
138 | return (path, False)
139 |
--------------------------------------------------------------------------------
/compiler/src/AST/Module/Name.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module AST.Module.Name
4 | ( Canonical(..)
5 | , basics, char, string
6 | , maybe, result, list, array, dict, tuple
7 | , platform, cmd, sub
8 | , virtualDom, debug, bitwise
9 | , jsonDecode, jsonEncode
10 | , webgl, texture, vector2, vector3, vector4, matrix4
11 | , isKernel, getKernel, canonicalIsKernel
12 | )
13 | where
14 |
15 |
16 | import Prelude hiding (maybe)
17 | import Control.Monad (liftM2)
18 | import Data.Binary
19 |
20 | import qualified Elm.Name as N
21 | import qualified Elm.Package as Pkg
22 |
23 |
24 |
25 | -- NAMES
26 |
27 |
28 | data Canonical =
29 | Canonical
30 | { _package :: !Pkg.Name
31 | , _module :: !N.Name
32 | }
33 | deriving (Ord)
34 |
35 |
36 | instance Eq Canonical where
37 | (==) (Canonical pkg home) (Canonical pkg' home') =
38 | home == home' && pkg == pkg'
39 |
40 |
41 |
42 | -- PRIMITIVES
43 |
44 |
45 | {-# NOINLINE basics #-}
46 | basics :: Canonical
47 | basics = Canonical Pkg.core "Basics"
48 |
49 |
50 | {-# NOINLINE char #-}
51 | char :: Canonical
52 | char = Canonical Pkg.core N.char
53 |
54 |
55 | {-# NOINLINE string #-}
56 | string :: Canonical
57 | string = Canonical Pkg.core N.string
58 |
59 |
60 |
61 | -- CONTAINERS
62 |
63 |
64 | {-# NOINLINE maybe #-}
65 | maybe :: Canonical
66 | maybe = Canonical Pkg.core N.maybe
67 |
68 |
69 | {-# NOINLINE result #-}
70 | result :: Canonical
71 | result = Canonical Pkg.core N.result
72 |
73 |
74 | {-# NOINLINE list #-}
75 | list :: Canonical
76 | list = Canonical Pkg.core N.list
77 |
78 |
79 | {-# NOINLINE array #-}
80 | array :: Canonical
81 | array = Canonical Pkg.core N.array
82 |
83 |
84 | {-# NOINLINE dict #-}
85 | dict :: Canonical
86 | dict = Canonical Pkg.core N.dict
87 |
88 |
89 | {-# NOINLINE tuple #-}
90 | tuple :: Canonical
91 | tuple = Canonical Pkg.core N.tuple
92 |
93 |
94 |
95 | -- EFFECTS
96 |
97 |
98 | {-# NOINLINE platform #-}
99 | platform :: Canonical
100 | platform = Canonical Pkg.core N.platform
101 |
102 |
103 | {-# NOINLINE cmd #-}
104 | cmd :: Canonical
105 | cmd = Canonical Pkg.core "Platform.Cmd"
106 |
107 |
108 | {-# NOINLINE sub #-}
109 | sub :: Canonical
110 | sub = Canonical Pkg.core "Platform.Sub"
111 |
112 |
113 |
114 | -- MISC
115 |
116 |
117 | {-# NOINLINE virtualDom #-}
118 | virtualDom :: Canonical
119 | virtualDom = Canonical Pkg.virtualDom N.virtualDom
120 |
121 |
122 | {-# NOINLINE debug #-}
123 | debug :: Canonical
124 | debug = Canonical Pkg.core N.debug
125 |
126 |
127 | {-# NOINLINE bitwise #-}
128 | bitwise :: Canonical
129 | bitwise = Canonical Pkg.core N.bitwise
130 |
131 |
132 |
133 | -- JSON
134 |
135 |
136 | {-# NOINLINE jsonDecode #-}
137 | jsonDecode :: Canonical
138 | jsonDecode = Canonical Pkg.json "Json.Decode"
139 |
140 |
141 | {-# NOINLINE jsonEncode #-}
142 | jsonEncode :: Canonical
143 | jsonEncode = Canonical Pkg.json "Json.Encode"
144 |
145 |
146 |
147 | -- WEBGL
148 |
149 |
150 | {-# NOINLINE webgl #-}
151 | webgl :: Canonical
152 | webgl = Canonical Pkg.webgl "WebGL"
153 |
154 |
155 | {-# NOINLINE texture #-}
156 | texture :: Canonical
157 | texture = Canonical Pkg.webgl "WebGL.Texture"
158 |
159 |
160 | {-# NOINLINE vector2 #-}
161 | vector2 :: Canonical
162 | vector2 = Canonical Pkg.linearAlgebra "Math.Vector2"
163 |
164 |
165 | {-# NOINLINE vector3 #-}
166 | vector3 :: Canonical
167 | vector3 = Canonical Pkg.linearAlgebra "Math.Vector3"
168 |
169 |
170 | {-# NOINLINE vector4 #-}
171 | vector4 :: Canonical
172 | vector4 = Canonical Pkg.linearAlgebra "Math.Vector4"
173 |
174 |
175 | {-# NOINLINE matrix4 #-}
176 | matrix4 :: Canonical
177 | matrix4 = Canonical Pkg.linearAlgebra "Math.Matrix4"
178 |
179 |
180 |
181 | -- IS KERNEL
182 |
183 |
184 | isKernel :: N.Name -> Bool
185 | isKernel name =
186 | N.startsWith "Elm.Kernel." name
187 |
188 |
189 | getKernel :: N.Name -> N.Name
190 | getKernel name =
191 | N.drop 11 name
192 |
193 |
194 | canonicalIsKernel :: Canonical -> Bool
195 | canonicalIsKernel (Canonical _ name) =
196 | isKernel name
197 |
198 |
199 |
200 | -- BINARY
201 |
202 |
203 | instance Binary Canonical where
204 | put (Canonical a b) =
205 | put a >> put b
206 |
207 | get =
208 | liftM2 Canonical get get
209 |
--------------------------------------------------------------------------------
/reactor/src/Index/Icon.elm:
--------------------------------------------------------------------------------
1 | module Index.Icon exposing
2 | ( home
3 | , image
4 | , file
5 | , gift
6 | , folder
7 | , package
8 | , plus
9 | , lookup
10 | )
11 |
12 | import Dict
13 | import Html exposing (Html)
14 | import Svg exposing (..)
15 | import Svg.Attributes exposing (class, width, height, viewBox, d, fill)
16 |
17 |
18 |
19 | -- ICON
20 |
21 |
22 | icon : String -> String -> String -> Html msg
23 | icon color size pathString =
24 | svg
25 | [ class "icon"
26 | , width size
27 | , height size
28 | , viewBox "0 0 1792 1792"
29 | ]
30 | [ path [ fill color, d pathString ] []
31 | ]
32 |
33 |
34 |
35 | -- NECESSARY ICONS
36 |
37 |
38 | home : Html msg
39 | home =
40 | icon "#babdb6" "36px" "M1472 992v480q0 26-19 45t-45 19h-384v-384h-256v384h-384q-26 0-45-19t-19-45v-480q0-1 .5-3t.5-3l575-474 575 474q1 2 1 6zm223-69l-62 74q-8 9-21 11h-3q-13 0-21-7l-692-577-692 577q-12 8-24 7-13-2-21-11l-62-74q-8-10-7-23.5t11-21.5l719-599q32-26 76-26t76 26l244 204v-195q0-14 9-23t23-9h192q14 0 23 9t9 23v408l219 182q10 8 11 21.5t-7 23.5z"
41 |
42 |
43 | image : Html msg
44 | image =
45 | icon "#babdb6" "16px" "M1596 380q28 28 48 76t20 88v1152q0 40-28 68t-68 28h-1344q-40 0-68-28t-28-68v-1600q0-40 28-68t68-28h896q40 0 88 20t76 48zm-444-244v376h376q-10-29-22-41l-313-313q-12-12-41-22zm384 1528v-1024h-416q-40 0-68-28t-28-68v-416h-768v1536h1280zm-128-448v320h-1024v-192l192-192 128 128 384-384zm-832-192q-80 0-136-56t-56-136 56-136 136-56 136 56 56 136-56 136-136 56z"
46 |
47 |
48 | file : Html msg
49 | file =
50 | icon "#babdb6" "16px" "M1596 380q28 28 48 76t20 88v1152q0 40-28 68t-68 28h-1344q-40 0-68-28t-28-68v-1600q0-40 28-68t68-28h896q40 0 88 20t76 48zm-444-244v376h376q-10-29-22-41l-313-313q-12-12-41-22zm384 1528v-1024h-416q-40 0-68-28t-28-68v-416h-768v1536h1280zm-1024-864q0-14 9-23t23-9h704q14 0 23 9t9 23v64q0 14-9 23t-23 9h-704q-14 0-23-9t-9-23v-64zm736 224q14 0 23 9t9 23v64q0 14-9 23t-23 9h-704q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h704zm0 256q14 0 23 9t9 23v64q0 14-9 23t-23 9h-704q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h704z"
51 |
52 |
53 | gift : Html msg
54 | gift =
55 | icon "#babdb6" "16px" "M1056 1356v-716h-320v716q0 25 18 38.5t46 13.5h192q28 0 46-13.5t18-38.5zm-456-844h195l-126-161q-26-31-69-31-40 0-68 28t-28 68 28 68 68 28zm688-96q0-40-28-68t-68-28q-43 0-69 31l-125 161h194q40 0 68-28t28-68zm376 256v320q0 14-9 23t-23 9h-96v416q0 40-28 68t-68 28h-1088q-40 0-68-28t-28-68v-416h-96q-14 0-23-9t-9-23v-320q0-14 9-23t23-9h440q-93 0-158.5-65.5t-65.5-158.5 65.5-158.5 158.5-65.5q107 0 168 77l128 165 128-165q61-77 168-77 93 0 158.5 65.5t65.5 158.5-65.5 158.5-158.5 65.5h440q14 0 23 9t9 23z"
56 |
57 |
58 | folder : Html msg
59 | folder =
60 | icon "#babdb6" "16px" "M1728 608v704q0 92-66 158t-158 66h-1216q-92 0-158-66t-66-158v-960q0-92 66-158t158-66h320q92 0 158 66t66 158v32h672q92 0 158 66t66 158z"
61 |
62 |
63 | package : Html msg
64 | package =
65 | icon "#babdb6" "16px" "M1088 832q0-26-19-45t-45-19h-256q-26 0-45 19t-19 45 19 45 45 19h256q26 0 45-19t19-45zm576-192v960q0 26-19 45t-45 19h-1408q-26 0-45-19t-19-45v-960q0-26 19-45t45-19h1408q26 0 45 19t19 45zm64-448v256q0 26-19 45t-45 19h-1536q-26 0-45-19t-19-45v-256q0-26 19-45t45-19h1536q26 0 45 19t19 45z"
66 |
67 |
68 | plus : Html msg
69 | plus =
70 | icon "#babdb6" "16px" "M1600 736v192q0 40-28 68t-68 28h-416v416q0 40-28 68t-68 28h-192q-40 0-68-28t-28-68v-416h-416q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h416v-416q0-40 28-68t68-28h192q40 0 68 28t28 68v416h416q40 0 68 28t28 68z"
71 |
72 |
73 |
74 | -- LOOKUP
75 |
76 |
77 | lookup : String -> Html msg
78 | lookup fileName =
79 | let
80 | extension =
81 | getExtension fileName
82 | in
83 | Maybe.withDefault file (Dict.get extension extensionIcons)
84 |
85 |
86 | extensionIcons : Dict.Dict String (Html msg)
87 | extensionIcons =
88 | Dict.fromList
89 | [ ("jpg" , image)
90 | , ("jpeg", image)
91 | , ("png" , image)
92 | , ("gif" , image)
93 | ]
94 |
95 |
96 | getExtension : String -> String
97 | getExtension str =
98 | getExtensionHelp (String.split "." str)
99 |
100 |
101 | getExtensionHelp : List String -> String
102 | getExtensionHelp segments =
103 | case segments of
104 | [] ->
105 | ""
106 |
107 | [ext] ->
108 | String.toLower ext
109 |
110 | _ :: rest ->
111 | getExtensionHelp rest
112 |
--------------------------------------------------------------------------------
/compiler/src/Reporting/Render/Code.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Reporting.Render.Code
4 | ( Source
5 | , toSource
6 | , render
7 | , CodePair(..)
8 | , renderPair
9 | )
10 | where
11 |
12 |
13 | import qualified Data.List as List
14 | import qualified Data.Text as Text
15 |
16 | import Reporting.Doc (Doc, (<>))
17 | import qualified Reporting.Doc as D
18 | import qualified Reporting.Region as R
19 |
20 |
21 |
22 | -- CODE
23 |
24 |
25 | newtype Source =
26 | Source [(Int, Text.Text)]
27 |
28 |
29 | toSource :: Text.Text -> Source
30 | toSource source =
31 | Source $ zip [1..] $
32 | Text.lines source ++ [Text.empty]
33 |
34 |
35 |
36 | -- RENDER
37 |
38 |
39 | (|>) :: a -> (a -> b) -> b
40 | (|>) a f =
41 | f a
42 |
43 |
44 | render :: Source -> R.Region -> Maybe R.Region -> Doc
45 | render (Source sourceLines) region@(R.Region start end) maybeSubRegion =
46 | let
47 | (R.Position startLine _) = start
48 | (R.Position endLine _) = end
49 |
50 | relevantLines =
51 | sourceLines
52 | |> drop (startLine - 1)
53 | |> take (1 + endLine - startLine)
54 |
55 | width =
56 | length (show (fst (last relevantLines)))
57 |
58 | smallerRegion =
59 | maybe region id maybeSubRegion
60 | in
61 | case makeUnderline width endLine smallerRegion of
62 | Nothing ->
63 | drawLines True width smallerRegion relevantLines D.empty
64 |
65 | Just underline ->
66 | drawLines False width smallerRegion relevantLines underline
67 |
68 |
69 | makeUnderline :: Int -> Int -> R.Region -> Maybe Doc
70 | makeUnderline width realEndLine (R.Region (R.Position start c1) (R.Position end c2)) =
71 | if start /= end || end < realEndLine then
72 | Nothing
73 |
74 | else
75 | let
76 | spaces = replicate (c1 + width + 1) ' '
77 | zigzag = replicate (max 1 (c2 - c1)) '^'
78 | in
79 | Just (D.fromString spaces <> D.dullred (D.fromString zigzag))
80 |
81 |
82 | drawLines :: Bool -> Int -> R.Region -> [(Int, Text.Text)] -> Doc -> Doc
83 | drawLines addZigZag width (R.Region start end) sourceLines finalLine =
84 | let
85 | (R.Position startLine _) = start
86 | (R.Position endLine _) = end
87 | in
88 | D.vcat $
89 | map (drawLine addZigZag width startLine endLine) sourceLines
90 | ++ [finalLine]
91 |
92 |
93 | drawLine :: Bool -> Int -> Int -> Int -> (Int, Text.Text) -> Doc
94 | drawLine addZigZag width startLine endLine (n, line) =
95 | addLineNumber addZigZag width startLine endLine n (D.fromText line)
96 |
97 |
98 | addLineNumber :: Bool -> Int -> Int -> Int -> Int -> Doc -> Doc
99 | addLineNumber addZigZag width start end n line =
100 | let
101 | number =
102 | if n < 0 then " " else show n
103 |
104 | lineNumber =
105 | replicate (width - length number) ' ' ++ number ++ "|"
106 |
107 | spacer =
108 | if addZigZag && start <= n && n <= end then
109 | D.dullred ">"
110 | else
111 | " "
112 | in
113 | D.fromString lineNumber <> spacer <> line
114 |
115 |
116 |
117 | -- RENDER PAIR
118 |
119 |
120 | data CodePair
121 | = OneLine Doc
122 | | TwoChunks Doc Doc
123 |
124 |
125 | renderPair :: Source -> R.Region -> R.Region -> CodePair
126 | renderPair source@(Source sourceLines) region1 region2 =
127 | let
128 | (R.Region (R.Position startRow1 startCol1) (R.Position endRow1 endCol1)) = region1
129 | (R.Region (R.Position startRow2 startCol2) (R.Position endRow2 endCol2)) = region2
130 | in
131 | if startRow1 == endRow1 && endRow1 == startRow2 && startRow2 == endRow2 then
132 | let
133 | lineNumber = show startRow1
134 | spaces1 = replicate (startCol1 + length lineNumber + 1) ' '
135 | zigzag1 = replicate (endCol1 - startCol1) '^'
136 | spaces2 = replicate (startCol2 - endCol1) ' '
137 | zigzag2 = replicate (endCol2 - startCol2) '^'
138 |
139 | (Just line) = List.lookup startRow1 sourceLines
140 | in
141 | OneLine $
142 | D.vcat
143 | [ D.fromString lineNumber <> "| " <> D.fromText line
144 | , D.fromString spaces1 <> D.dullred (D.fromString zigzag1) <>
145 | D.fromString spaces2 <> D.dullred (D.fromString zigzag2)
146 | ]
147 |
148 | else
149 | TwoChunks
150 | (render source region1 Nothing)
151 | (render source region2 Nothing)
152 |
--------------------------------------------------------------------------------
/compiler/src/Reporting/Error/Main.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Reporting.Error.Main
4 | ( Error(..)
5 | , toReport
6 | )
7 | where
8 |
9 |
10 | import qualified AST.Canonical as Can
11 | import qualified Elm.Name as N
12 | import qualified Reporting.Doc as D
13 | import qualified Reporting.Error.Canonicalize as E
14 | import qualified Reporting.Region as R
15 | import qualified Reporting.Render.Code as Code
16 | import qualified Reporting.Render.Type as RT
17 | import qualified Reporting.Render.Type.Localizer as L
18 | import qualified Reporting.Report as Report
19 |
20 |
21 |
22 | -- ERROR
23 |
24 |
25 | data Error
26 | = BadType R.Region Can.Type
27 | | BadCycle R.Region [N.Name]
28 | | BadFlags R.Region Can.Type E.InvalidPayload
29 |
30 |
31 |
32 | -- TO REPORT
33 |
34 |
35 | toReport :: L.Localizer -> Code.Source -> Error -> Report.Report
36 | toReport localizer source err =
37 | case err of
38 | BadType region tipe ->
39 | Report.Report "BAD MAIN TYPE" region [] $
40 | Report.toCodeSnippet source region Nothing
41 | (
42 | "I cannot handle this type of `main` value:"
43 | ,
44 | D.stack
45 | [ "The type of `main` value I am seeing is:"
46 | , D.indent 4 $ D.dullyellow $ RT.canToDoc localizer RT.None tipe
47 | , D.reflow $
48 | "I only know how to handle Html, Svg, and Programs\
49 | \ though. Modify `main` to be one of those types of values!"
50 | ]
51 | )
52 |
53 | BadCycle region cycleNames ->
54 | Report.Report "BAD MAIN" region [] $
55 | Report.toCodeSnippet source region Nothing
56 | (
57 | "A `main` definition cannot be defined in terms of itself."
58 | ,
59 | D.stack
60 | [ D.reflow $
61 | "It should be a boring value with no recursion. But\
62 | \ instead it is involved in this cycle of definitions:"
63 | , D.cycle 4 cycleNames
64 | ]
65 | )
66 |
67 | BadFlags region _badType invalidPayload ->
68 | let
69 | formatDetails (aBadKindOfThing, butThatIsNoGood) =
70 | Report.Report "BAD FLAGS" region [] $
71 | Report.toCodeSnippet source region Nothing
72 | (
73 | D.reflow $
74 | "Your `main` program wants " ++ aBadKindOfThing ++ " from JavaScript."
75 | ,
76 | butThatIsNoGood
77 | )
78 | in
79 | formatDetails $
80 | case invalidPayload of
81 | E.ExtendedRecord ->
82 | (
83 | "an extended record"
84 | ,
85 | D.reflow $
86 | "But the exact shape of the record must be known at compile time. No type variables!"
87 | )
88 |
89 | E.Function ->
90 | (
91 | "a function"
92 | ,
93 | D.reflow $
94 | "But if I allowed functions from JS, it would be possible to sneak\
95 | \ side-effects and runtime exceptions into Elm!"
96 | )
97 |
98 | E.TypeVariable name ->
99 | (
100 | "an unspecified type"
101 | ,
102 | D.reflow $
103 | "But type variables like `" ++ N.toString name ++ "` cannot be given as flags.\
104 | \ I need to know exactly what type of data I am getting, so I can guarantee that\
105 | \ unexpected data cannot sneak in and crash the Elm program."
106 | )
107 |
108 | E.UnsupportedType name ->
109 | (
110 | "a `" ++ N.toString name ++ "` value"
111 | ,
112 | D.stack
113 | [ D.reflow $ "I cannot handle that. The types that CAN be in flags include:"
114 | , D.indent 4 $
115 | D.reflow $
116 | "Ints, Floats, Bools, Strings, Maybes, Lists, Arrays,\
117 | \ tuples, records, and JSON values."
118 | , D.reflow $
119 | "Since JSON values can flow through, you can use JSON encoders and decoders\
120 | \ to allow other types through as well. More advanced users often just do\
121 | \ everything with encoders and decoders for more control and better errors."
122 | ]
123 | )
124 |
--------------------------------------------------------------------------------
/compiler/src/Type/UnionFind.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -funbox-strict-fields #-}
2 | {-# LANGUAGE BangPatterns #-}
3 | module Type.UnionFind
4 | ( Point
5 | , fresh
6 | , union
7 | , equivalent
8 | , redundant
9 | , get
10 | , set
11 | , modify
12 | )
13 | where
14 |
15 |
16 | {- This is based on the following implementations:
17 |
18 | - https://hackage.haskell.org/package/union-find-0.2/docs/src/Data-UnionFind-IO.html
19 | - http://yann.regis-gianas.org/public/mini/code_UnionFind.html
20 |
21 | It seems like the OCaml one came first, but I am not sure.
22 |
23 | Compared to the Haskell implementation, the major changes here include:
24 |
25 | 1. No more reallocating PointInfo when changing the weight
26 | 2. Using the strict modifyIORef
27 |
28 | -}
29 |
30 |
31 | import Control.Monad ( when )
32 | import Data.IORef (IORef, modifyIORef', newIORef, readIORef, writeIORef)
33 | import Data.Word (Word32)
34 |
35 |
36 |
37 | -- POINT
38 |
39 |
40 | newtype Point a =
41 | Pt (IORef (PointInfo a))
42 | deriving Eq
43 |
44 |
45 | data PointInfo a
46 | = Info {-# UNPACK #-} !(IORef Word32) {-# UNPACK #-} !(IORef a)
47 | | Link {-# UNPACK #-} !(Point a)
48 |
49 |
50 |
51 | -- HELPERS
52 |
53 |
54 | fresh :: a -> IO (Point a)
55 | fresh value =
56 | do weight <- newIORef 1
57 | desc <- newIORef value
58 | link <- newIORef (Info weight desc)
59 | return (Pt link)
60 |
61 |
62 | repr :: Point a -> IO (Point a)
63 | repr point@(Pt ref) =
64 | do pInfo <- readIORef ref
65 | case pInfo of
66 | Info _ _ ->
67 | return point
68 |
69 | Link point1@(Pt ref1) ->
70 | do point2 <- repr point1
71 | when (point2 /= point1) $
72 | do pInfo1 <- readIORef ref1
73 | writeIORef ref pInfo1
74 | return point2
75 |
76 |
77 | get :: Point a -> IO a
78 | get point@(Pt ref) =
79 | do pInfo <- readIORef ref
80 | case pInfo of
81 | Info _ descRef ->
82 | readIORef descRef
83 |
84 | Link (Pt ref1) ->
85 | do link' <- readIORef ref1
86 | case link' of
87 | Info _ descRef ->
88 | readIORef descRef
89 |
90 | Link _ ->
91 | get =<< repr point
92 |
93 |
94 | set :: Point a -> a -> IO ()
95 | set point@(Pt ref) newDesc =
96 | do pInfo <- readIORef ref
97 | case pInfo of
98 | Info _ descRef ->
99 | writeIORef descRef newDesc
100 |
101 | Link (Pt ref1) ->
102 | do link' <- readIORef ref1
103 | case link' of
104 | Info _ descRef ->
105 | writeIORef descRef newDesc
106 |
107 | Link _ ->
108 | do newPoint <- repr point
109 | set newPoint newDesc
110 |
111 |
112 | modify :: Point a -> (a -> a) -> IO ()
113 | modify point@(Pt ref) func =
114 | do pInfo <- readIORef ref
115 | case pInfo of
116 | Info _ descRef ->
117 | modifyIORef' descRef func
118 |
119 | Link (Pt ref1) ->
120 | do link' <- readIORef ref1
121 | case link' of
122 | Info _ descRef ->
123 | modifyIORef' descRef func
124 |
125 | Link _ ->
126 | do newPoint <- repr point
127 | modify newPoint func
128 |
129 |
130 | union :: Point a -> Point a -> a -> IO ()
131 | union p1 p2 newDesc =
132 | do point1@(Pt ref1) <- repr p1
133 | point2@(Pt ref2) <- repr p2
134 |
135 | Info w1 d1 <- readIORef ref1
136 | Info w2 d2 <- readIORef ref2
137 |
138 | if point1 == point2
139 | then writeIORef d1 newDesc
140 | else do
141 | weight1 <- readIORef w1
142 | weight2 <- readIORef w2
143 |
144 | let !newWeight = weight1 + weight2
145 |
146 | if weight1 >= weight2
147 | then
148 | do writeIORef ref2 (Link point1)
149 | writeIORef w1 newWeight
150 | writeIORef d1 newDesc
151 | else
152 | do writeIORef ref1 (Link point2)
153 | writeIORef w2 newWeight
154 | writeIORef d2 newDesc
155 |
156 |
157 | equivalent :: Point a -> Point a -> IO Bool
158 | equivalent p1 p2 =
159 | do v1 <- repr p1
160 | v2 <- repr p2
161 | return (v1 == v2)
162 |
163 |
164 | redundant :: Point a -> IO Bool
165 | redundant (Pt ref) =
166 | do pInfo <- readIORef ref
167 | case pInfo of
168 | Info _ _ ->
169 | return False
170 |
171 | Link _ ->
172 | return True
173 |
--------------------------------------------------------------------------------
/compiler/src/Parse/Primitives/Keyword.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE BangPatterns, OverloadedStrings #-}
3 | module Parse.Primitives.Keyword
4 | ( type_, alias_, port_
5 | , if_, then_, else_
6 | , case_, of_
7 | , let_, in_
8 | , infix_, left_, right_, non_
9 | , module_, import_, exposing_, as_, where_, effect_
10 | , command_, subscription_
11 | , jsonTrue, jsonFalse, jsonNull
12 | )
13 | where
14 |
15 |
16 | import Control.Exception (assert)
17 | import qualified Data.ByteString.Internal as B
18 | import qualified Data.ByteString.Char8 as Char8
19 |
20 | import Parse.Primitives.Internals (Parser(..), State(..), expect, noError)
21 | import qualified Parse.Primitives.Internals as I
22 | import qualified Parse.Primitives.Variable as Var
23 | import qualified Reporting.Error.Syntax as E
24 |
25 |
26 |
27 | -- PRIVATE IMPLEMENTATION
28 |
29 |
30 | {- We can some avoid allocation by declaring all available keywords here.
31 | That means the `keyword` function should only be used within this file on
32 | values tagged as NOINLINE.
33 | -}
34 | keyword :: B.ByteString -> Parser ()
35 | keyword kwd@(B.PS kwdFp kwdOffset kwdLength) =
36 | let
37 | !theory =
38 | assert
39 | (I.isNonNewlineAscii kwdFp kwdOffset (kwdOffset + kwdLength))
40 | (E.Keyword (Char8.unpack kwd))
41 | in
42 | Parser $ \(State fp offset terminal indent row col ctx) cok _ _ eerr ->
43 | if I.isSubstring kwdFp kwdOffset kwdLength fp offset terminal
44 | && Var.getInnerWidth fp (offset + kwdLength) terminal == 0
45 | then
46 | let
47 | !newState =
48 | State fp (offset + kwdLength) terminal indent row (col + kwdLength) ctx
49 | in
50 | cok () newState noError
51 |
52 | else
53 | eerr (expect row col ctx theory)
54 |
55 |
56 |
57 | -- DECLARATIONS
58 |
59 |
60 | {-# NOINLINE type_ #-}
61 | type_ :: Parser ()
62 | type_ =
63 | keyword "type"
64 |
65 |
66 | {-# NOINLINE alias_ #-}
67 | alias_ :: Parser ()
68 | alias_ =
69 | keyword "alias"
70 |
71 |
72 | {-# NOINLINE port_ #-}
73 | port_ :: Parser ()
74 | port_ =
75 | keyword "port"
76 |
77 |
78 |
79 | -- IF EXPRESSIONS
80 |
81 |
82 | {-# NOINLINE if_ #-}
83 | if_ :: Parser ()
84 | if_ =
85 | keyword "if"
86 |
87 |
88 | {-# NOINLINE then_ #-}
89 | then_ :: Parser ()
90 | then_ =
91 | keyword "then"
92 |
93 |
94 | {-# NOINLINE else_ #-}
95 | else_ :: Parser ()
96 | else_ =
97 | keyword "else"
98 |
99 |
100 |
101 | -- CASE EXPRESSIONS
102 |
103 |
104 | {-# NOINLINE case_ #-}
105 | case_ :: Parser ()
106 | case_ =
107 | keyword "case"
108 |
109 |
110 | {-# NOINLINE of_ #-}
111 | of_ :: Parser ()
112 | of_ =
113 | keyword "of"
114 |
115 |
116 |
117 | -- LET EXPRESSIONS
118 |
119 |
120 | {-# NOINLINE let_ #-}
121 | let_ :: Parser ()
122 | let_ =
123 | keyword "let"
124 |
125 |
126 | {-# NOINLINE in_ #-}
127 | in_ :: Parser ()
128 | in_ =
129 | keyword "in"
130 |
131 |
132 |
133 | -- INFIXES
134 |
135 |
136 | {-# NOINLINE infix_ #-}
137 | infix_ :: Parser ()
138 | infix_ =
139 | keyword "infix"
140 |
141 |
142 | {-# NOINLINE left_ #-}
143 | left_ :: Parser ()
144 | left_ =
145 | keyword "left"
146 |
147 |
148 | {-# NOINLINE right_ #-}
149 | right_ :: Parser ()
150 | right_ =
151 | keyword "right"
152 |
153 |
154 | {-# NOINLINE non_ #-}
155 | non_ :: Parser ()
156 | non_ =
157 | keyword "non"
158 |
159 |
160 |
161 | -- IMPORTS
162 |
163 |
164 | {-# NOINLINE module_ #-}
165 | module_ :: Parser ()
166 | module_ =
167 | keyword "module"
168 |
169 |
170 | {-# NOINLINE import_ #-}
171 | import_ :: Parser ()
172 | import_ =
173 | keyword "import"
174 |
175 |
176 | {-# NOINLINE exposing_ #-}
177 | exposing_ :: Parser ()
178 | exposing_ =
179 | keyword "exposing"
180 |
181 |
182 | {-# NOINLINE as_ #-}
183 | as_ :: Parser ()
184 | as_ =
185 | keyword "as"
186 |
187 |
188 | {-# NOINLINE where_ #-}
189 | where_ :: Parser ()
190 | where_ =
191 | keyword "where"
192 |
193 |
194 | {-# NOINLINE effect_ #-}
195 | effect_ :: Parser ()
196 | effect_ =
197 | keyword "effect"
198 |
199 |
200 |
201 | -- EFFECTS
202 |
203 |
204 | {-# NOINLINE command_ #-}
205 | command_ :: Parser ()
206 | command_ =
207 | keyword "command"
208 |
209 |
210 | {-# NOINLINE subscription_ #-}
211 | subscription_ :: Parser ()
212 | subscription_ =
213 | keyword "subscription"
214 |
215 |
216 |
217 | -- JSON
218 |
219 |
220 | {-# NOINLINE jsonTrue #-}
221 | jsonTrue :: Parser ()
222 | jsonTrue =
223 | keyword "true"
224 |
225 |
226 | {-# NOINLINE jsonFalse #-}
227 | jsonFalse :: Parser ()
228 | jsonFalse =
229 | keyword "false"
230 |
231 |
232 | {-# NOINLINE jsonNull #-}
233 | jsonNull :: Parser ()
234 | jsonNull =
235 | keyword "null"
236 |
--------------------------------------------------------------------------------
/compiler/src/Optimize/Case.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module Optimize.Case
3 | ( optimize
4 | )
5 | where
6 |
7 |
8 | import Control.Arrow (second)
9 | import qualified Data.Map as Map
10 | import Data.Map ((!))
11 | import qualified Data.Maybe as Maybe
12 |
13 | import qualified AST.Canonical as Can
14 | import qualified AST.Optimized as Opt
15 | import qualified Elm.Name as N
16 | import qualified Optimize.DecisionTree as DT
17 |
18 |
19 |
20 | -- OPTIMIZE A CASE EXPRESSION
21 |
22 |
23 | optimize :: N.Name -> N.Name -> [(Can.Pattern, Opt.Expr)] -> Opt.Expr
24 | optimize temp root optBranches =
25 | let
26 | (patterns, indexedBranches) =
27 | unzip (zipWith indexify [0..] optBranches)
28 |
29 | decider = treeToDecider (DT.compile patterns)
30 | targetCounts = countTargets decider
31 |
32 | (choices, maybeJumps) =
33 | unzip (map (createChoices targetCounts) indexedBranches)
34 | in
35 | Opt.Case temp root
36 | (insertChoices (Map.fromList choices) decider)
37 | (Maybe.catMaybes maybeJumps)
38 |
39 |
40 | indexify :: Int -> (a,b) -> ((a,Int), (Int,b))
41 | indexify index (pattern, branch) =
42 | ( (pattern, index)
43 | , (index, branch)
44 | )
45 |
46 |
47 |
48 | -- TREE TO DECIDER
49 | --
50 | -- Decision trees may have some redundancies, so we convert them to a Decider
51 | -- which has special constructs to avoid code duplication when possible.
52 |
53 |
54 | treeToDecider :: DT.DecisionTree -> Opt.Decider Int
55 | treeToDecider tree =
56 | case tree of
57 | DT.Match target ->
58 | Opt.Leaf target
59 |
60 | -- zero options
61 | DT.Decision _ [] Nothing ->
62 | error "compiler bug, somehow created an empty decision tree"
63 |
64 | -- one option
65 | DT.Decision _ [(_, subTree)] Nothing ->
66 | treeToDecider subTree
67 |
68 | DT.Decision _ [] (Just subTree) ->
69 | treeToDecider subTree
70 |
71 | -- two options
72 | DT.Decision path [(test, successTree)] (Just failureTree) ->
73 | toChain path test successTree failureTree
74 |
75 | DT.Decision path [(test, successTree), (_, failureTree)] Nothing ->
76 | toChain path test successTree failureTree
77 |
78 | -- many options
79 | DT.Decision path edges Nothing ->
80 | let
81 | (necessaryTests, fallback) =
82 | (init edges, snd (last edges))
83 | in
84 | Opt.FanOut
85 | path
86 | (map (second treeToDecider) necessaryTests)
87 | (treeToDecider fallback)
88 |
89 | DT.Decision path edges (Just fallback) ->
90 | Opt.FanOut path (map (second treeToDecider) edges) (treeToDecider fallback)
91 |
92 |
93 | toChain :: DT.Path -> DT.Test -> DT.DecisionTree -> DT.DecisionTree -> Opt.Decider Int
94 | toChain path test successTree failureTree =
95 | let
96 | failure =
97 | treeToDecider failureTree
98 | in
99 | case treeToDecider successTree of
100 | Opt.Chain testChain success subFailure | failure == subFailure ->
101 | Opt.Chain ((path, test) : testChain) success failure
102 |
103 | success ->
104 | Opt.Chain [(path, test)] success failure
105 |
106 |
107 |
108 | -- INSERT CHOICES
109 | --
110 | -- If a target appears exactly once in a Decider, the corresponding expression
111 | -- can be inlined. Whether things are inlined or jumps is called a "choice".
112 |
113 |
114 | countTargets :: Opt.Decider Int -> Map.Map Int Int
115 | countTargets decisionTree =
116 | case decisionTree of
117 | Opt.Leaf target ->
118 | Map.singleton target 1
119 |
120 | Opt.Chain _ success failure ->
121 | Map.unionWith (+) (countTargets success) (countTargets failure)
122 |
123 | Opt.FanOut _ tests fallback ->
124 | Map.unionsWith (+) (map countTargets (fallback : map snd tests))
125 |
126 |
127 | createChoices
128 | :: Map.Map Int Int
129 | -> (Int, Opt.Expr)
130 | -> ( (Int, Opt.Choice), Maybe (Int, Opt.Expr) )
131 | createChoices targetCounts (target, branch) =
132 | if targetCounts ! target == 1 then
133 | ( (target, Opt.Inline branch)
134 | , Nothing
135 | )
136 |
137 | else
138 | ( (target, Opt.Jump target)
139 | , Just (target, branch)
140 | )
141 |
142 |
143 | insertChoices
144 | :: Map.Map Int Opt.Choice
145 | -> Opt.Decider Int
146 | -> Opt.Decider Opt.Choice
147 | insertChoices choiceDict decider =
148 | let
149 | go =
150 | insertChoices choiceDict
151 | in
152 | case decider of
153 | Opt.Leaf target ->
154 | Opt.Leaf (choiceDict ! target)
155 |
156 | Opt.Chain testChain success failure ->
157 | Opt.Chain testChain (go success) (go failure)
158 |
159 | Opt.FanOut path tests fallback ->
160 | Opt.FanOut path (map (second go) tests) (go fallback)
161 |
162 |
--------------------------------------------------------------------------------
/compiler/src/Canonicalize/Type.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Canonicalize.Type
4 | ( toAnnotation
5 | , canonicalize
6 | )
7 | where
8 |
9 |
10 | import qualified Data.List as List
11 | import qualified Data.Map as Map
12 |
13 | import qualified AST.Canonical as Can
14 | import qualified AST.Source as Src
15 | import qualified Canonicalize.Environment as Env
16 | import qualified Canonicalize.Environment.Dups as Dups
17 | import qualified Elm.Name as N
18 | import qualified Reporting.Annotation as A
19 | import qualified Reporting.Error.Canonicalize as Error
20 | import qualified Reporting.Region as R
21 | import qualified Reporting.Result as Result
22 |
23 |
24 |
25 | -- RESULT
26 |
27 |
28 | type Result i w a =
29 | Result.Result i w Error.Error a
30 |
31 |
32 |
33 | -- TO ANNOTATION
34 |
35 |
36 | toAnnotation :: Env.Env -> Src.Type -> Result i w Can.Annotation
37 | toAnnotation env srcType =
38 | do tipe <- canonicalize env srcType
39 | Result.ok $ Can.Forall (addFreeVars Map.empty tipe) tipe
40 |
41 |
42 |
43 | -- CANONICALIZE TYPES
44 |
45 |
46 | canonicalize :: Env.Env -> Src.Type -> Result i w Can.Type
47 | canonicalize env (A.At typeRegion tipe) =
48 | case tipe of
49 | Src.TVar x ->
50 | Result.ok (Can.TVar x)
51 |
52 | Src.TType region name args ->
53 | canonicalizeType env typeRegion name args =<<
54 | Env.findType region env name
55 |
56 | Src.TTypeQual region home name args ->
57 | canonicalizeType env typeRegion name args =<<
58 | Env.findTypeQual region env home name
59 |
60 | Src.TLambda a b ->
61 | Can.TLambda
62 | <$> canonicalize env a
63 | <*> canonicalize env b
64 |
65 | Src.TRecord fields ext ->
66 | do cfields <- sequenceA =<< Dups.checkFields (canonicalizeFields env fields)
67 | return $ Can.TRecord cfields (fmap A.toValue ext)
68 |
69 | Src.TUnit ->
70 | Result.ok Can.TUnit
71 |
72 | Src.TTuple a b cs ->
73 | Can.TTuple
74 | <$> canonicalize env a
75 | <*> canonicalize env b
76 | <*>
77 | case cs of
78 | [] ->
79 | Result.ok Nothing
80 |
81 | [c] ->
82 | Just <$> canonicalize env c
83 |
84 | _ ->
85 | Result.throw $ Error.TupleLargerThanThree typeRegion
86 |
87 |
88 | canonicalizeFields :: Env.Env -> [(A.Located N.Name, Src.Type)] -> [(A.Located N.Name, Result i w Can.FieldType)]
89 | canonicalizeFields env fields =
90 | let
91 | len = fromIntegral (length fields)
92 | canonicalizeField index (name, srcType) =
93 | (name, Can.FieldType index <$> canonicalize env srcType)
94 | in
95 | zipWith canonicalizeField [0..len] fields
96 |
97 |
98 |
99 | -- CANONICALIZE TYPE
100 |
101 |
102 | canonicalizeType :: Env.Env -> R.Region -> N.Name -> [Src.Type] -> Env.Type -> Result i w Can.Type
103 | canonicalizeType env region name args info =
104 | do cargs <- traverse (canonicalize env) args
105 | case info of
106 | Env.Alias arity home argNames aliasedType ->
107 | checkArity arity region name args $
108 | Can.TAlias home name (zip argNames cargs) (Can.Holey aliasedType)
109 |
110 | Env.Union arity home ->
111 | checkArity arity region name args $
112 | Can.TType home name cargs
113 |
114 |
115 | checkArity :: Int -> R.Region -> N.Name -> [A.Located arg] -> answer -> Result i w answer
116 | checkArity expected region name args answer =
117 | let actual = length args in
118 | if expected == actual then
119 | Result.ok answer
120 | else
121 | Result.throw (Error.BadArity region Error.TypeArity name expected actual)
122 |
123 |
124 |
125 | -- ADD FREE VARS
126 |
127 |
128 | addFreeVars :: Map.Map N.Name () -> Can.Type -> Map.Map N.Name ()
129 | addFreeVars freeVars tipe =
130 | case tipe of
131 | Can.TLambda arg result ->
132 | addFreeVars (addFreeVars freeVars result) arg
133 |
134 | Can.TVar var ->
135 | Map.insert var () freeVars
136 |
137 | Can.TType _ _ args ->
138 | List.foldl' addFreeVars freeVars args
139 |
140 | Can.TRecord fields Nothing ->
141 | Map.foldl addFieldFreeVars freeVars fields
142 |
143 | Can.TRecord fields (Just ext) ->
144 | Map.foldl addFieldFreeVars (Map.insert ext () freeVars) fields
145 |
146 | Can.TUnit ->
147 | freeVars
148 |
149 | Can.TTuple a b maybeC ->
150 | case maybeC of
151 | Nothing ->
152 | addFreeVars (addFreeVars freeVars a) b
153 |
154 | Just c ->
155 | addFreeVars (addFreeVars (addFreeVars freeVars a) b) c
156 |
157 | Can.TAlias _ _ args _ ->
158 | List.foldl' (\fvs (_,arg) -> addFreeVars fvs arg) freeVars args
159 |
160 |
161 | addFieldFreeVars :: Map.Map N.Name () -> Can.FieldType -> Map.Map N.Name ()
162 | addFieldFreeVars freeVars (Can.FieldType _ tipe) =
163 | addFreeVars freeVars tipe
164 |
--------------------------------------------------------------------------------
/compiler/src/Optimize/Names.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | {-# LANGUAGE Rank2Types #-}
4 | module Optimize.Names
5 | ( Tracker
6 | , run
7 | , generate
8 | , registerKernel
9 | , registerGlobal
10 | , registerDebug
11 | , registerCtor
12 | , registerField
13 | , registerFieldDict
14 | , registerFieldList
15 | )
16 | where
17 |
18 |
19 | import qualified Data.Map as Map
20 | import qualified Data.Set as Set
21 |
22 | import qualified AST.Canonical as Can
23 | import qualified AST.Optimized as Opt
24 | import qualified AST.Module.Name as ModuleName
25 | import qualified Data.Index as Index
26 | import qualified Elm.Name as N
27 | import qualified Elm.Package as Pkg
28 | import qualified Reporting.Region as R
29 |
30 |
31 |
32 | -- GENERATOR
33 |
34 |
35 | newtype Tracker a =
36 | Tracker (
37 | forall r.
38 | Int
39 | -> Set.Set Opt.Global
40 | -> Map.Map N.Name Int
41 | -> (Int -> Set.Set Opt.Global -> Map.Map N.Name Int -> a -> r)
42 | -> r
43 | )
44 |
45 |
46 | run :: Tracker a -> (Set.Set Opt.Global, Map.Map N.Name Int, a)
47 | run (Tracker k) =
48 | k 0 Set.empty Map.empty
49 | (\_uid deps fields value -> (deps, fields, value))
50 |
51 |
52 | generate :: Tracker N.Name
53 | generate =
54 | Tracker $ \uid deps fields ok ->
55 | ok (uid + 1) deps fields (N.addIndex "_n" uid)
56 |
57 |
58 | registerKernel :: N.Name -> a -> Tracker a
59 | registerKernel home value =
60 | Tracker $ \uid deps fields ok ->
61 | ok uid (Set.insert (toKernelGlobal home) deps) fields value
62 |
63 |
64 | toKernelGlobal :: N.Name -> Opt.Global
65 | toKernelGlobal home =
66 | Opt.Global (ModuleName.Canonical Pkg.kernel home) N.dollar
67 |
68 |
69 | registerGlobal :: ModuleName.Canonical -> N.Name -> Tracker Opt.Expr
70 | registerGlobal home name =
71 | Tracker $ \uid deps fields ok ->
72 | let global = Opt.Global home name in
73 | ok uid (Set.insert global deps) fields (Opt.VarGlobal global)
74 |
75 |
76 | registerDebug :: N.Name -> ModuleName.Canonical -> R.Region -> Tracker Opt.Expr
77 | registerDebug name home region =
78 | Tracker $ \uid deps fields ok ->
79 | let global = Opt.Global ModuleName.debug name in
80 | ok uid (Set.insert global deps) fields (Opt.VarDebug name home region Nothing)
81 |
82 |
83 | registerCtor :: ModuleName.Canonical -> N.Name -> Index.ZeroBased -> Can.CtorOpts -> Tracker Opt.Expr
84 | registerCtor home name index opts =
85 | Tracker $ \uid deps fields ok ->
86 | let
87 | global = Opt.Global home name
88 | newDeps = Set.insert global deps
89 | in
90 | case opts of
91 | Can.Normal ->
92 | ok uid newDeps fields (Opt.VarGlobal global)
93 |
94 | Can.Enum ->
95 | ok uid newDeps fields $
96 | case name of
97 | "True" | home == ModuleName.basics -> Opt.Bool True
98 | "False" | home == ModuleName.basics -> Opt.Bool False
99 | _ -> Opt.VarEnum global index
100 |
101 | Can.Unbox ->
102 | ok uid (Set.insert identity newDeps) fields (Opt.VarBox global)
103 |
104 |
105 | identity :: Opt.Global
106 | identity =
107 | Opt.Global ModuleName.basics N.identity
108 |
109 |
110 | registerField :: N.Name -> a -> Tracker a
111 | registerField name value =
112 | Tracker $ \uid d fields ok ->
113 | ok uid d (Map.insertWith (+) name 1 fields) value
114 |
115 |
116 | registerFieldDict :: Map.Map N.Name v -> a -> Tracker a
117 | registerFieldDict newFields value =
118 | Tracker $ \uid d fields ok ->
119 | ok uid d (Map.unionWith (+) fields (Map.map toOne newFields)) value
120 |
121 |
122 | toOne :: a -> Int
123 | toOne _ = 1
124 |
125 |
126 | registerFieldList :: [N.Name] -> a -> Tracker a
127 | registerFieldList names value =
128 | Tracker $ \uid deps fields ok ->
129 | ok uid deps (foldr addOne fields names) value
130 |
131 |
132 | addOne :: N.Name -> Map.Map N.Name Int -> Map.Map N.Name Int
133 | addOne name fields =
134 | Map.insertWith (+) name 1 fields
135 |
136 |
137 |
138 | -- INSTANCES
139 |
140 |
141 | instance Functor Tracker where
142 | fmap func (Tracker kv) =
143 | Tracker $ \n d f ok ->
144 | let
145 | ok1 n1 d1 f1 value =
146 | ok n1 d1 f1 (func value)
147 | in
148 | kv n d f ok1
149 |
150 |
151 | instance Applicative Tracker where
152 | {-# INLINE pure #-}
153 | pure value =
154 | Tracker $ \n d f ok -> ok n d f value
155 |
156 | (<*>) (Tracker kf) (Tracker kv) =
157 | Tracker $ \n d f ok ->
158 | let
159 | ok1 n1 d1 f1 func =
160 | let
161 | ok2 n2 d2 f2 value =
162 | ok n2 d2 f2 (func value)
163 | in
164 | kv n1 d1 f1 ok2
165 | in
166 | kf n d f ok1
167 |
168 |
169 | instance Monad Tracker where
170 | return = pure
171 |
172 | (>>=) (Tracker k) callback =
173 | Tracker $ \n d f ok ->
174 | let
175 | ok1 n1 d1 f1 a =
176 | case callback a of
177 | Tracker kb -> kb n1 d1 f1 ok
178 | in
179 | k n d f ok1
180 |
--------------------------------------------------------------------------------
/compiler/src/Elm/Compiler/Type.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Elm.Compiler.Type
4 | ( Type(..)
5 | , RT.Context(..)
6 | , toDoc
7 | , DebugMetadata(..)
8 | , Alias(..)
9 | , Union(..)
10 | , encode
11 | , decoder
12 | , encodeMetadata
13 | )
14 | where
15 |
16 |
17 | import qualified Data.Text as Text
18 | import qualified Data.Text.Encoding as Text
19 |
20 | import qualified AST.Source as Src
21 | import qualified Elm.Name as N
22 | import qualified Parse.Primitives as Parse
23 | import qualified Parse.Type as Type
24 | import qualified Reporting.Annotation as A
25 | import qualified Reporting.Doc as D
26 | import qualified Reporting.Render.Type as RT
27 | import qualified Reporting.Render.Type.Localizer as L
28 |
29 | import qualified Json.Decode as Decode
30 | import qualified Json.Encode as Encode
31 | import Json.Encode ((==>))
32 |
33 |
34 |
35 | -- TYPES
36 |
37 |
38 | data Type
39 | = Lambda Type Type
40 | | Var N.Name
41 | | Type N.Name [Type]
42 | | Record [(N.Name, Type)] (Maybe N.Name)
43 | | Unit
44 | | Tuple Type Type [Type]
45 |
46 |
47 | data DebugMetadata =
48 | DebugMetadata
49 | { _message :: Type
50 | , _aliases :: [Alias]
51 | , _unions :: [Union]
52 | }
53 |
54 |
55 | data Alias = Alias N.Name [N.Name] Type
56 | data Union = Union N.Name [N.Name] [(N.Name, [Type])]
57 |
58 |
59 |
60 | -- TO DOC
61 |
62 |
63 | toDoc :: L.Localizer -> RT.Context -> Type -> D.Doc
64 | toDoc localizer context tipe =
65 | case tipe of
66 | Lambda _ _ ->
67 | let
68 | a:b:cs =
69 | map (toDoc localizer RT.Func) (collectLambdas tipe)
70 | in
71 | RT.lambda context a b cs
72 |
73 | Var name ->
74 | D.fromName name
75 |
76 | Unit ->
77 | "()"
78 |
79 | Tuple a b cs ->
80 | RT.tuple
81 | (toDoc localizer RT.None a)
82 | (toDoc localizer RT.None b)
83 | (map (toDoc localizer RT.None) cs)
84 |
85 | Type name args ->
86 | RT.apply
87 | context
88 | (D.fromName name)
89 | (map (toDoc localizer RT.App) args)
90 |
91 | Record fields ext ->
92 | RT.record
93 | (map (entryToDoc localizer) fields)
94 | (fmap D.fromName ext)
95 |
96 |
97 | entryToDoc :: L.Localizer -> (N.Name, Type) -> (D.Doc, D.Doc)
98 | entryToDoc localizer (field, fieldType) =
99 | ( D.fromName field, toDoc localizer RT.None fieldType )
100 |
101 |
102 | collectLambdas :: Type -> [Type]
103 | collectLambdas tipe =
104 | case tipe of
105 | Lambda arg body ->
106 | arg : collectLambdas body
107 |
108 | _ ->
109 | [tipe]
110 |
111 |
112 |
113 | -- JSON for TYPE
114 |
115 |
116 | encode :: Type -> Encode.Value
117 | encode tipe =
118 | Encode.text $ Text.pack $ D.toLine (toDoc L.empty RT.None tipe)
119 |
120 |
121 | decoder :: Decode.Decoder () Type
122 | decoder =
123 | do txt <- Decode.text
124 | case Parse.run Type.expression (Text.encodeUtf8 (Text.replace "'" "_" txt)) of
125 | Left _ ->
126 | Decode.fail ()
127 |
128 | Right (tipe, _, _) ->
129 | Decode.succeed (fromRawType tipe)
130 |
131 |
132 | fromRawType :: Src.Type -> Type
133 | fromRawType (A.At _ astType) =
134 | case astType of
135 | Src.TLambda t1 t2 ->
136 | Lambda (fromRawType t1) (fromRawType t2)
137 |
138 | Src.TVar x ->
139 | Var x
140 |
141 | Src.TUnit ->
142 | Unit
143 |
144 | Src.TTuple a b cs ->
145 | Tuple
146 | (fromRawType a)
147 | (fromRawType b)
148 | (map fromRawType cs)
149 |
150 | Src.TType _ name args ->
151 | Type name (map fromRawType args)
152 |
153 | Src.TTypeQual _ _ name args ->
154 | Type name (map fromRawType args)
155 |
156 | Src.TRecord fields ext ->
157 | let fromField (A.At _ field, tipe) = (field, fromRawType tipe) in
158 | Record
159 | (map fromField fields)
160 | (fmap A.toValue ext)
161 |
162 |
163 |
164 | -- JSON for PROGRAM
165 |
166 |
167 | encodeMetadata :: DebugMetadata -> Encode.Value
168 | encodeMetadata (DebugMetadata msg aliases unions) =
169 | Encode.object
170 | [ "message" ==> encode msg
171 | , "aliases" ==> Encode.object (map toAliasField aliases)
172 | , "unions" ==> Encode.object (map toUnionField unions)
173 | ]
174 |
175 |
176 | toAliasField :: Alias -> ( Text.Text, Encode.Value )
177 | toAliasField (Alias name args tipe) =
178 | N.toText name ==>
179 | Encode.object
180 | [ "args" ==> Encode.list Encode.name args
181 | , "type" ==> encode tipe
182 | ]
183 |
184 |
185 | toUnionField :: Union -> ( Text.Text, Encode.Value )
186 | toUnionField (Union name args constructors) =
187 | N.toText name ==>
188 | Encode.object
189 | [ "args" ==> Encode.list Encode.name args
190 | , "tags" ==> Encode.object (map toCtorObject constructors)
191 | ]
192 |
193 |
194 | toCtorObject :: (N.Name, [Type]) -> ( Text.Text, Encode.Value )
195 | toCtorObject (name, args) =
196 | N.toText name ==> Encode.list encode args
197 |
--------------------------------------------------------------------------------
/compiler/src/Reporting/Error/Docs.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Reporting.Error.Docs
4 | ( Error(..)
5 | , toReport
6 | )
7 | where
8 |
9 |
10 | import qualified Elm.Name as N
11 | import Reporting.Doc ((<>))
12 | import qualified Reporting.Doc as D
13 | import qualified Reporting.Region as R
14 | import qualified Reporting.Render.Code as Code
15 | import qualified Reporting.Report as Report
16 |
17 |
18 |
19 | data Error
20 | = NoDocs R.Region
21 | | ImplicitExposing R.Region
22 | | Duplicate N.Name R.Region R.Region
23 | | OnlyInDocs N.Name R.Region
24 | | OnlyInExports N.Name R.Region
25 | | NoComment N.Name R.Region
26 | | NoAnnotation N.Name R.Region
27 |
28 |
29 |
30 | -- TO REPORT
31 |
32 |
33 | toReport :: Code.Source -> Error -> Report.Report
34 | toReport source err =
35 | case err of
36 | NoDocs region ->
37 | Report.Report "NO DOCS" region [] $
38 | D.stack
39 | [
40 | D.reflow $
41 | "You must have a documentation comment between the module\
42 | \ declaration and the imports."
43 | ,
44 | D.reflow
45 | "Learn more at "
46 | ]
47 |
48 | ImplicitExposing region ->
49 | Report.Report "IMPLICIT EXPOSING" region [] $
50 | D.stack
51 | [
52 | D.reflow $
53 | "I need you to be explicit about what this module exposes:"
54 | ,
55 | D.reflow $
56 | "A great API usually hides some implementation details, so it is rare that\
57 | \ everything in the file should be exposed. And requiring package authors\
58 | \ to be explicit about this is a way of adding another quality check before\
59 | \ code gets published. So as you write out the public API, ask yourself if\
60 | \ it will be easy to understand as people read the documentation!"
61 | ]
62 |
63 | Duplicate name r1 r2 ->
64 | Report.Report "DUPLICATE DOCS" r2 [] $
65 | Report.toCodePair source r1 r2
66 | (
67 | D.reflow $
68 | "There can only be one `" <> N.toString name
69 | <> "` in your module documentation, but it is listed twice:"
70 | ,
71 | "Remove one of them!"
72 | )
73 | (
74 | D.reflow $
75 | "There can only be one `" <> N.toString name
76 | <> "` in your module documentation, but I see two. One here:"
77 | ,
78 | "And another one over here:"
79 | ,
80 | "Remove one of them!"
81 | )
82 |
83 | OnlyInDocs name region ->
84 | Report.Report "DOCS MISTAKE" region [] $
85 | Report.toCodeSnippet source region Nothing
86 | (
87 | D.reflow $
88 | "I do not see `" <> N.toString name
89 | <> "` in the `exposing` list, but it is in your module documentation:"
90 | ,
91 | D.reflow $
92 | "Does it need to be added to the `exposing` list as well? Or maybe you removed `"
93 | <> N.toString name <> "` and forgot to delete it here?"
94 | )
95 |
96 | OnlyInExports name region ->
97 | Report.Report "DOCS MISTAKE" region [] $
98 | Report.toCodeSnippet source region Nothing
99 | (
100 | D.reflow $
101 | "I do not see `" <> N.toString name
102 | <> "` in your module documentation, but it is in your `exposing` list:"
103 | ,
104 | D.stack
105 | [ D.reflow $
106 | "Add a line like `@docs " <> N.toString name
107 | <> "` to your module documentation!"
108 | , D.link "Note" "See" "docs" "for more guidance on writing high quality docs."
109 | ]
110 | )
111 |
112 | NoComment name region ->
113 | Report.Report "NO DOCS" region [] $
114 | Report.toCodeSnippet source region Nothing
115 | (
116 | D.reflow $
117 | "The `" <> N.toString name <> "` definition does not have a documentation comment."
118 | ,
119 | D.stack
120 | [ D.reflow $
121 | "Add documentation with nice examples of how to use it!"
122 | , D.link "Note" "Read" "docs" "for more advice on writing great docs. There are a couple important tricks!"
123 | ]
124 | )
125 |
126 | NoAnnotation name region ->
127 | Report.Report "NO TYPE ANNOTATION" region [] $
128 | Report.toCodeSnippet source region Nothing
129 | (
130 | D.reflow $
131 | "The `" <> N.toString name <> "` definition does not have a type annotation."
132 | ,
133 | D.stack
134 | [ D.reflow $
135 | "I use the type variable names from your annotations when generating docs. So if\
136 | \ you say `Html msg` in your type annotation, I can use `msg` in the docs and make\
137 | \ them a bit clearer. So add an annotation and try to use nice type variables!"
138 | , D.link "Note" "Read" "docs" "for more advice on writing great docs. There are a couple important tricks!"
139 | ]
140 | )
141 |
--------------------------------------------------------------------------------
/compiler/src/Canonicalize/Pattern.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module Canonicalize.Pattern
3 | ( verify
4 | , Bindings
5 | , DupsDict
6 | , canonicalize
7 | )
8 | where
9 |
10 |
11 | import qualified Data.Map.Strict as Map
12 |
13 | import qualified AST.Canonical as Can
14 | import qualified AST.Module.Name as ModuleName
15 | import qualified AST.Source as Src
16 | import qualified Canonicalize.Environment as Env
17 | import qualified Canonicalize.Environment.Dups as Dups
18 | import qualified Data.Index as Index
19 | import qualified Elm.Name as N
20 | import qualified Reporting.Annotation as A
21 | import qualified Reporting.Error.Canonicalize as Error
22 | import qualified Reporting.Region as R
23 | import qualified Reporting.Result as Result
24 |
25 |
26 |
27 | -- RESULTS
28 |
29 |
30 | type Result i w a =
31 | Result.Result i w Error.Error a
32 |
33 |
34 | type Bindings =
35 | Map.Map N.Name R.Region
36 |
37 |
38 |
39 | -- VERIFY
40 |
41 |
42 | verify :: Error.DuplicatePatternContext -> Result DupsDict w a -> Result i w (a, Bindings)
43 | verify context (Result.Result k) =
44 | Result.Result $ \info warnings bad good ->
45 | k Dups.none warnings
46 | (\_ warnings1 errors ->
47 | bad info warnings1 errors
48 | )
49 | (\bindings warnings1 value ->
50 | case Dups.detect (Error.DuplicatePattern context) bindings of
51 | Result.Result k1 ->
52 | k1 () ()
53 | (\() () errs -> bad info warnings1 errs)
54 | (\() () dict -> good info warnings1 (value, dict))
55 | )
56 |
57 |
58 |
59 | -- CANONICALIZE
60 |
61 |
62 | type DupsDict =
63 | Dups.Dict R.Region
64 |
65 |
66 | canonicalize :: Env.Env -> Src.Pattern -> Result DupsDict w Can.Pattern
67 | canonicalize env (A.At region pattern) =
68 | A.At region <$>
69 | case pattern of
70 | Src.PAnything ->
71 | Result.ok Can.PAnything
72 |
73 | Src.PVar name ->
74 | logVar name region (Can.PVar name)
75 |
76 | Src.PRecord fields ->
77 | logFields fields (Can.PRecord (map A.toValue fields))
78 |
79 | Src.PUnit ->
80 | Result.ok Can.PUnit
81 |
82 | Src.PTuple a b cs ->
83 | Can.PTuple
84 | <$> canonicalize env a
85 | <*> canonicalize env b
86 | <*> canonicalizeTuple region env cs
87 |
88 | Src.PCtor nameRegion name patterns ->
89 | canonicalizeCtor env region name patterns =<< Env.findCtor nameRegion env name
90 |
91 | Src.PCtorQual nameRegion home name patterns ->
92 | canonicalizeCtor env region name patterns =<< Env.findCtorQual nameRegion env home name
93 |
94 | Src.PList patterns ->
95 | Can.PList <$> canonicalizeList env patterns
96 |
97 | Src.PCons first rest ->
98 | Can.PCons
99 | <$> canonicalize env first
100 | <*> canonicalize env rest
101 |
102 | Src.PAlias ptrn (A.At reg name) ->
103 | do cpattern <- canonicalize env ptrn
104 | logVar name reg (Can.PAlias cpattern name)
105 |
106 | Src.PChr chr ->
107 | Result.ok (Can.PChr chr)
108 |
109 | Src.PStr str ->
110 | Result.ok (Can.PStr str)
111 |
112 | Src.PInt int ->
113 | Result.ok (Can.PInt int)
114 |
115 |
116 | canonicalizeCtor :: Env.Env -> R.Region -> N.Name -> [Src.Pattern] -> Env.Ctor -> Result DupsDict w Can.Pattern_
117 | canonicalizeCtor env region name patterns ctor =
118 | case ctor of
119 | Env.Ctor home tipe union index args ->
120 | let
121 | toCanonicalArg argIndex argPattern argTipe =
122 | Can.PatternCtorArg argIndex argTipe <$> canonicalize env argPattern
123 | in
124 | do verifiedList <- Index.indexedZipWithA toCanonicalArg patterns args
125 | case verifiedList of
126 | Index.LengthMatch cargs ->
127 | if tipe == N.bool && home == ModuleName.basics then
128 | Result.ok (Can.PBool union (name == N.true))
129 | else
130 | Result.ok (Can.PCtor home tipe union name index cargs)
131 |
132 | Index.LengthMismatch actualLength expectedLength ->
133 | Result.throw (Error.BadArity region Error.PatternArity name expectedLength actualLength)
134 |
135 | Env.RecordCtor _ _ _ ->
136 | Result.throw (Error.PatternHasRecordCtor region name)
137 |
138 |
139 | canonicalizeTuple :: R.Region -> Env.Env -> [Src.Pattern] -> Result DupsDict w (Maybe Can.Pattern)
140 | canonicalizeTuple tupleRegion env extras =
141 | case extras of
142 | [] ->
143 | Result.ok Nothing
144 |
145 | [three] ->
146 | Just <$> canonicalize env three
147 |
148 | _ ->
149 | Result.throw $ Error.TupleLargerThanThree tupleRegion
150 |
151 |
152 | canonicalizeList :: Env.Env -> [Src.Pattern] -> Result DupsDict w [Can.Pattern]
153 | canonicalizeList env list =
154 | case list of
155 | [] ->
156 | Result.ok []
157 |
158 | pattern : otherPatterns ->
159 | (:)
160 | <$> canonicalize env pattern
161 | <*> canonicalizeList env otherPatterns
162 |
163 |
164 |
165 | -- LOG BINDINGS
166 |
167 |
168 | logVar :: N.Name -> R.Region -> a -> Result DupsDict w a
169 | logVar name region value =
170 | Result.Result $ \bindings warnings _ ok ->
171 | ok (Dups.insert name region region bindings) warnings value
172 |
173 |
174 | logFields :: [A.Located N.Name] -> a -> Result DupsDict w a
175 | logFields fields value =
176 | let
177 | addField (A.At region name) dict =
178 | Dups.insert name region region dict
179 | in
180 | Result.Result $ \bindings warnings _ ok ->
181 | ok (foldr addField bindings fields) warnings value
182 |
--------------------------------------------------------------------------------
/reactor/src/Errors.elm:
--------------------------------------------------------------------------------
1 | module Errors exposing (main)
2 |
3 |
4 | import Browser
5 | import Char
6 | import Html exposing (..)
7 | import Html.Attributes exposing (..)
8 | import String
9 | import Json.Decode as D
10 | import Elm.Error as Error
11 |
12 |
13 |
14 | -- MAIN
15 |
16 |
17 | main =
18 | Browser.document
19 | { init = \flags -> (D.decodeValue Error.decoder flags, Cmd.none)
20 | , update = \_ exit -> (exit, Cmd.none)
21 | , view = view
22 | , subscriptions = \_ -> Sub.none
23 | }
24 |
25 |
26 |
27 | -- VIEW
28 |
29 |
30 | view : Result D.Error Error.Error -> Browser.Document msg
31 | view result =
32 | { title = "Problem!"
33 | , body =
34 | case result of
35 | Err err ->
36 | [ text (D.errorToString err) ]
37 |
38 | Ok error ->
39 | [ viewError error ]
40 | }
41 |
42 |
43 | viewError : Error.Error -> Html msg
44 | viewError error =
45 | div
46 | [ style "width" "100%"
47 | , style "min-height" "100%"
48 | , style "display" "flex"
49 | , style "flex-direction" "column"
50 | , style "align-items" "center"
51 | , style "background-color" "rgb(39, 40, 34)"
52 | , style "color" "rgb(233, 235, 235)"
53 | , style "font-family" "monospace"
54 | ]
55 | [ div
56 | [ style "display" "block"
57 | , style "white-space" "pre-wrap"
58 | , style "background-color" "black"
59 | , style "padding" "2em"
60 | ]
61 | (viewErrorHelp error)
62 | ]
63 |
64 |
65 | viewErrorHelp : Error.Error -> List (Html msg)
66 | viewErrorHelp error =
67 | case error of
68 | Error.GeneralProblem { path, title, message } ->
69 | viewHeader title path :: viewMessage message
70 |
71 | Error.ModuleProblems badModules ->
72 | viewBadModules badModules
73 |
74 |
75 |
76 | -- VIEW HEADER
77 |
78 |
79 | viewHeader : String -> Maybe String -> Html msg
80 | viewHeader title maybeFilePath =
81 | let
82 | left = "-- " ++ title ++ " "
83 | right =
84 | case maybeFilePath of
85 | Nothing ->
86 | ""
87 | Just filePath ->
88 | " " ++ filePath
89 | in
90 | span [ style "color" "rgb(51,187,200)" ] [ text (fill left right ++ "\n\n") ]
91 |
92 |
93 | fill : String -> String -> String
94 | fill left right =
95 | left ++ String.repeat (80 - String.length left - String.length right) "-" ++ right
96 |
97 |
98 |
99 | -- VIEW BAD MODULES
100 |
101 |
102 | viewBadModules : List Error.BadModule -> List (Html msg)
103 | viewBadModules badModules =
104 | case badModules of
105 | [] ->
106 | []
107 |
108 | [badModule] ->
109 | [viewBadModule badModule]
110 |
111 | a :: b :: cs ->
112 | viewBadModule a :: viewSeparator a.name b.name :: viewBadModules (b :: cs)
113 |
114 |
115 | viewBadModule : Error.BadModule -> Html msg
116 | viewBadModule { path, problems } =
117 | span [] (List.map (viewProblem path) problems)
118 |
119 |
120 | viewProblem : String -> Error.Problem -> Html msg
121 | viewProblem filePath problem =
122 | span [] (viewHeader problem.title (Just filePath) :: viewMessage problem.message)
123 |
124 |
125 | viewSeparator : String -> String -> Html msg
126 | viewSeparator before after =
127 | span [ style "color" "rgb(211,56,211)" ]
128 | [ text <|
129 | String.padLeft 80 ' ' (before ++ " ↑ ") ++ "\n" ++
130 | "====o======================================================================o====\n" ++
131 | " ↓ " ++ after ++ "\n\n\n"
132 | ]
133 |
134 |
135 |
136 | -- VIEW MESSAGE
137 |
138 |
139 | viewMessage : List Error.Chunk -> List (Html msg)
140 | viewMessage chunks =
141 | case chunks of
142 | [] ->
143 | [ text "\n\n\n" ]
144 |
145 | chunk :: others ->
146 | let
147 | htmlChunk =
148 | case chunk of
149 | Error.Unstyled string ->
150 | text string
151 |
152 | Error.Styled style string ->
153 | span (styleToAttrs style) [ text string ]
154 | in
155 | htmlChunk :: viewMessage others
156 |
157 |
158 | styleToAttrs : Error.Style -> List (Attribute msg)
159 | styleToAttrs { bold, underline, color } =
160 | addBold bold <| addUnderline underline <| addColor color []
161 |
162 |
163 | addBold : Bool -> List (Attribute msg) -> List (Attribute msg)
164 | addBold bool attrs =
165 | if bool then
166 | style "font-weight" "bold" :: attrs
167 | else
168 | attrs
169 |
170 |
171 | addUnderline : Bool -> List (Attribute msg) -> List (Attribute msg)
172 | addUnderline bool attrs =
173 | if bool then
174 | style "text-decoration" "underline" :: attrs
175 | else
176 | attrs
177 |
178 |
179 | addColor : Maybe Error.Color -> List (Attribute msg) -> List (Attribute msg)
180 | addColor maybeColor attrs =
181 | case maybeColor of
182 | Nothing ->
183 | attrs
184 |
185 | Just color ->
186 | style "color" (colorToCss color) :: attrs
187 |
188 |
189 | colorToCss : Error.Color -> String
190 | colorToCss color =
191 | case color of
192 | Error.Red -> "rgb(194,54,33)"
193 | Error.RED -> "rgb(252,57,31)"
194 | Error.Magenta -> "rgb(211,56,211)"
195 | Error.MAGENTA -> "rgb(249,53,248)"
196 | Error.Yellow -> "rgb(173,173,39)"
197 | Error.YELLOW -> "rgb(234,236,35)"
198 | Error.Green -> "rgb(37,188,36)"
199 | Error.GREEN -> "rgb(49,231,34)"
200 | Error.Cyan -> "rgb(51,187,200)"
201 | Error.CYAN -> "rgb(20,240,240)"
202 | Error.Blue -> "rgb(73,46,225)"
203 | Error.BLUE -> "rgb(88,51,255)"
204 | Error.White -> "rgb(203,204,205)"
205 | Error.WHITE -> "rgb(233,235,235)"
206 | Error.Black -> "rgb(0,0,0)"
207 | Error.BLACK -> "rgb(129,131,131)"
208 |
--------------------------------------------------------------------------------
/compiler/src/Elm/Interface.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module Elm.Interface
3 | ( Interfaces
4 | , Interface(..)
5 | , Union
6 | , Alias
7 | , Binop(..)
8 | , fromModule
9 | , toPublicUnion
10 | , toPublicAlias
11 | , toUnionInternals
12 | , toAliasInternals
13 | )
14 | where
15 |
16 |
17 | import Control.Monad (liftM4)
18 | import Data.Binary
19 | import Data.Map ((!))
20 | import qualified Data.Map as Map
21 | import qualified Data.Map.Merge.Strict as Map
22 |
23 | import qualified AST.Canonical as Can
24 | import qualified AST.Module.Name as ModuleName
25 | import qualified AST.Utils.Binop as Binop
26 | import qualified Elm.Name as N
27 | import qualified Reporting.Annotation as A
28 |
29 |
30 |
31 | -- INTERFACES
32 |
33 |
34 | type Interfaces =
35 | Map.Map ModuleName.Canonical Interface
36 |
37 |
38 |
39 | -- INTERFACE
40 |
41 |
42 | data Interface =
43 | Interface
44 | { _types :: Map.Map N.Name Can.Annotation
45 | , _unions :: Map.Map N.Name Union
46 | , _aliases :: Map.Map N.Name Alias
47 | , _binops :: Map.Map N.Name Binop
48 | }
49 |
50 |
51 | data Union
52 | = OpenUnion Can.Union
53 | | ClosedUnion Can.Union
54 | | PrivateUnion Can.Union
55 |
56 |
57 | data Alias
58 | = PublicAlias Can.Alias
59 | | PrivateAlias Can.Alias
60 |
61 |
62 | data Binop =
63 | Binop
64 | { _op_name :: N.Name
65 | , _op_annotation :: Can.Annotation
66 | , _op_associativity :: Binop.Associativity
67 | , _op_precedence :: Binop.Precedence
68 | }
69 |
70 |
71 |
72 | -- FROM MODULE
73 |
74 |
75 | fromModule :: Map.Map N.Name Can.Annotation -> Can.Module -> Interface
76 | fromModule types (Can.Module _ _ exports _ unions aliases binops _) =
77 | Interface
78 | { _types = privatize exports types
79 | , _unions = privatizeUnions exports unions
80 | , _aliases = privatizeAliases exports aliases
81 | , _binops = privatize exports (Map.map (toOp types) binops)
82 | }
83 |
84 |
85 | privatize :: Can.Exports -> Map.Map N.Name a -> Map.Map N.Name a
86 | privatize exports dict =
87 | case exports of
88 | Can.ExportEverything _ ->
89 | dict
90 |
91 | Can.Export explicitExports ->
92 | Map.intersection dict explicitExports
93 |
94 |
95 | toOp :: Map.Map N.Name Can.Annotation -> Can.Binop -> Binop
96 | toOp types (Can.Binop_ associativity precedence name) =
97 | Binop name (types ! name) associativity precedence
98 |
99 |
100 | privatizeUnions :: Can.Exports -> Map.Map N.Name Can.Union -> Map.Map N.Name Union
101 | privatizeUnions exports unions =
102 | case exports of
103 | Can.ExportEverything _ ->
104 | Map.map OpenUnion unions
105 |
106 | Can.Export explicitExports ->
107 | Map.merge onLeft onRight onBoth explicitExports unions
108 | where
109 | onLeft = Map.dropMissing
110 | onRight = Map.mapMissing (\_ u -> PrivateUnion u)
111 | onBoth = Map.zipWithMatched $ \_ e u ->
112 | case A.toValue e of
113 | Can.ExportUnionOpen -> OpenUnion u
114 | Can.ExportUnionClosed -> ClosedUnion u
115 | _ -> error "impossible exports discovered in privatizeUnions"
116 |
117 |
118 | privatizeAliases :: Can.Exports -> Map.Map N.Name Can.Alias -> Map.Map N.Name Alias
119 | privatizeAliases exports aliases =
120 | case exports of
121 | Can.ExportEverything _ ->
122 | Map.map PublicAlias aliases
123 |
124 | Can.Export explicitExports ->
125 | Map.merge onLeft onRight onBoth explicitExports aliases
126 | where
127 | onLeft = Map.dropMissing
128 | onRight = Map.mapMissing (\_ a -> PrivateAlias a)
129 | onBoth = Map.zipWithMatched (\_ _ a -> PublicAlias a)
130 |
131 |
132 |
133 | -- PUBLICIZE
134 |
135 |
136 | toPublicUnion :: Union -> Maybe Can.Union
137 | toPublicUnion iUnion =
138 | case iUnion of
139 | OpenUnion union ->
140 | Just union
141 |
142 | ClosedUnion (Can.Union vars _ _ opts) ->
143 | Just (Can.Union vars [] 0 opts)
144 |
145 | PrivateUnion _ ->
146 | Nothing
147 |
148 |
149 | toPublicAlias :: Alias -> Maybe Can.Alias
150 | toPublicAlias iAlias =
151 | case iAlias of
152 | PublicAlias alias ->
153 | Just alias
154 |
155 | PrivateAlias _ ->
156 | Nothing
157 |
158 |
159 |
160 | -- INTERNALS
161 |
162 |
163 | toUnionInternals :: Union -> Can.Union
164 | toUnionInternals iUnion =
165 | case iUnion of
166 | OpenUnion union -> union
167 | ClosedUnion union -> union
168 | PrivateUnion union -> union
169 |
170 |
171 | toAliasInternals :: Alias -> Can.Alias
172 | toAliasInternals iAlias =
173 | case iAlias of
174 | PublicAlias alias -> alias
175 | PrivateAlias alias -> alias
176 |
177 |
178 |
179 | -- BINARY
180 |
181 |
182 | instance Binary Interface where
183 | get =
184 | liftM4 Interface get get get get
185 |
186 | put (Interface a b c d) =
187 | put a >> put b >> put c >> put d
188 |
189 |
190 | instance Binary Union where
191 | put union =
192 | case union of
193 | OpenUnion u -> putWord8 0 >> put u
194 | ClosedUnion u -> putWord8 1 >> put u
195 | PrivateUnion u -> putWord8 2 >> put u
196 |
197 | get =
198 | do n <- getWord8
199 | case n of
200 | 0 -> OpenUnion <$> get
201 | 1 -> ClosedUnion <$> get
202 | 2 -> PrivateUnion <$> get
203 | _ -> error "binary encoding of Union was corrupted"
204 |
205 |
206 | instance Binary Alias where
207 | put union =
208 | case union of
209 | PublicAlias a -> putWord8 0 >> put a
210 | PrivateAlias a -> putWord8 1 >> put a
211 |
212 | get =
213 | do n <- getWord8
214 | case n of
215 | 0 -> PublicAlias <$> get
216 | 1 -> PrivateAlias <$> get
217 | _ -> error "binary encoding of Alias was corrupted"
218 |
219 |
220 | instance Binary Binop where
221 | get =
222 | liftM4 Binop get get get get
223 |
224 | put (Binop a b c d) =
225 | put a >> put b >> put c >> put d
226 |
227 |
--------------------------------------------------------------------------------
/compiler/src/Parse/Declaration.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall -fno-warn-unused-do-bind #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Parse.Declaration
4 | ( declaration
5 | , infix_
6 | )
7 | where
8 |
9 |
10 | import qualified Data.Text.Encoding as Text
11 |
12 | import qualified AST.Source as Src
13 | import qualified AST.Utils.Binop as Binop
14 | import qualified Elm.Name as N
15 | import qualified Parse.Expression as Expr
16 | import qualified Parse.Pattern as Pattern
17 | import Parse.Primitives
18 | import qualified Parse.Primitives.Keyword as Keyword
19 | import qualified Parse.Primitives.Number as Number
20 | import qualified Parse.Primitives.Symbol as Symbol
21 | import qualified Parse.Primitives.Variable as Var
22 | import qualified Parse.Primitives.Whitespace as W
23 | import qualified Parse.Type as Type
24 | import qualified Reporting.Annotation as A
25 | import qualified Reporting.Error.Syntax as E
26 | import qualified Reporting.Region as R
27 |
28 |
29 |
30 | -- DECLARATION
31 |
32 |
33 | declaration :: SParser Src.Decl
34 | declaration =
35 | hint E.Decl $
36 | do start <- getPosition
37 | oneOf
38 | [ doc_ start
39 | , type_ start
40 | , port_ start
41 | , def_ start
42 | ]
43 |
44 |
45 |
46 | -- DOC COMMENTS
47 |
48 |
49 | {-# INLINE doc_ #-}
50 | doc_ :: R.Position -> SParser Src.Decl
51 | doc_ start =
52 | do doc <- W.docComment
53 | end <- getPosition
54 | pos <- W.whitespace
55 | return ( A.at start end (Src.Docs (Text.decodeUtf8 doc)), end, pos )
56 |
57 |
58 |
59 | -- DEFINITION and ANNOTATION
60 |
61 |
62 | {-# INLINE def_ #-}
63 | def_ :: R.Position -> SParser Src.Decl
64 | def_ start =
65 | do name <- Var.lower
66 | nameEnd <- getPosition
67 | let locatedName = A.at start nameEnd name
68 | spaces
69 | oneOf
70 | [ do Symbol.hasType
71 | inContext start (E.Annotation name) $
72 | do spaces
73 | (tipe, end, space) <- Type.expression
74 | return ( A.at start end (Src.Annotation locatedName tipe), end, space )
75 | , inContext start (E.Definition name) $
76 | definitionHelp start locatedName []
77 | ]
78 |
79 |
80 | definitionHelp :: R.Position -> A.Located N.Name -> [Src.Pattern] -> SParser Src.Decl
81 | definitionHelp start name revArgs =
82 | oneOf
83 | [ do arg <- hint E.Arg Pattern.term
84 | spaces
85 | definitionHelp start name (arg : revArgs)
86 | , do Symbol.equals
87 | spaces
88 | (body, end, space) <- Expr.expression
89 | let def = A.at start end (Src.Definition name (reverse revArgs) body)
90 | return ( def, end, space )
91 | ]
92 |
93 |
94 |
95 | -- TYPE ALIAS and UNION TYPES
96 |
97 |
98 | {-# INLINE type_ #-}
99 | type_ :: R.Position -> SParser Src.Decl
100 | type_ start =
101 | do Keyword.type_
102 | spaces
103 | oneOf
104 | [ do Keyword.alias_
105 | inContext start E.TypeAlias $
106 | do spaces
107 | (name, args) <- nameArgsEquals
108 | (tipe, end, pos) <- Type.expression
109 | return ( A.at start end (Src.Alias name args tipe), end, pos )
110 | , inContext start E.TypeUnion $
111 | do (name, args) <- nameArgsEquals
112 | (firstCtor, firstEnd, firstSpace) <- Type.unionConstructor
113 | (ctors, end, pos) <- chompConstructors [firstCtor] firstEnd firstSpace
114 | return ( A.at start end (Src.Union name args ctors), end, pos )
115 | ]
116 |
117 |
118 | nameArgsEquals :: Parser (A.Located N.Name, [A.Located N.Name])
119 | nameArgsEquals =
120 | do name <- addLocation Var.upper
121 | spaces
122 | nameArgsEqualsHelp name []
123 |
124 |
125 | nameArgsEqualsHelp :: A.Located N.Name -> [A.Located N.Name] -> Parser (A.Located N.Name, [A.Located N.Name])
126 | nameArgsEqualsHelp name args =
127 | oneOf
128 | [ do arg <- addLocation Var.lower
129 | spaces
130 | nameArgsEqualsHelp name (arg:args)
131 | , do Symbol.equals
132 | spaces
133 | return ( name, reverse args )
134 | ]
135 |
136 |
137 | chompConstructors :: [(A.Located N.Name, [Src.Type])] -> R.Position -> SPos -> SParser [(A.Located N.Name, [Src.Type])]
138 | chompConstructors ctors end pos =
139 | oneOf
140 | [ do checkSpace pos
141 | Symbol.pipe
142 | spaces
143 | (ctor, newEnd, newSpace) <- Type.unionConstructor
144 | chompConstructors (ctor:ctors) newEnd newSpace
145 | , return ( reverse ctors, end, pos )
146 | ]
147 |
148 |
149 |
150 | -- PORT
151 |
152 |
153 | {-# INLINE port_ #-}
154 | port_ :: R.Position -> SParser Src.Decl
155 | port_ start =
156 | do Keyword.port_
157 | inContext start E.Port $
158 | do spaces
159 | name <- addLocation Var.lower
160 | spaces
161 | Symbol.hasType
162 | spaces
163 | (tipe, end, pos) <- Type.expression
164 | return ( A.at start end (Src.Port name tipe), end, pos )
165 |
166 |
167 |
168 | -- INFIX
169 |
170 |
171 | -- INVARIANT: always chomps to a freshline
172 | --
173 | infix_ :: Parser Src.Decl
174 | infix_ =
175 | do start <- getPosition
176 | Keyword.infix_
177 | inContext start E.Infix $
178 | do spaces
179 | associativity <-
180 | oneOf
181 | [ Keyword.left_ >> return Binop.Left
182 | , Keyword.right_ >> return Binop.Right
183 | , Keyword.non_ >> return Binop.Non
184 | ]
185 | spaces
186 | precedence <- Number.precedence
187 | spaces
188 | Symbol.leftParen
189 | op <- Symbol.binop
190 | Symbol.rightParen
191 | spaces
192 | Symbol.equals
193 | spaces
194 | name <- Var.lower
195 | end <- getPosition
196 | checkFreshLine =<< W.whitespace
197 | return (A.at start end (Src.Binop op associativity precedence name))
198 |
--------------------------------------------------------------------------------
/compiler/src/Parse/Primitives.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall -fno-warn-unused-do-bind #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Parse.Primitives
4 | ( I.Parser
5 | , I.oneOf
6 | , run, runAt
7 | , try, deadend, hint, endOfFile
8 | , noFloatsAllowedInPatterns
9 | , getPosition, getCol
10 | , pushContext, popContext
11 | , getIndent, setIndent
12 | , W.SPos
13 | , SParser
14 | , addLocation, inContext
15 | , spaces, noSpace, checkSpace, checkAligned, checkFreshLine
16 | )
17 | where
18 |
19 |
20 | import Prelude hiding (length)
21 | import qualified Data.ByteString.Internal as B
22 |
23 | import Parse.Primitives.Internals (Parser(..), State(..), expect, noError)
24 | import qualified Parse.Primitives.Internals as I
25 | import qualified Parse.Primitives.Whitespace as W
26 | import qualified Reporting.Annotation as A
27 | import qualified Reporting.Error.Syntax as E
28 | import qualified Reporting.Region as R
29 |
30 |
31 |
32 | -- RUN
33 |
34 |
35 | run :: Parser a -> B.ByteString -> Either E.Error a
36 | run parser bytes =
37 | runAt 1 1 parser bytes
38 |
39 |
40 | runAt :: Int -> Int -> Parser a -> B.ByteString -> Either E.Error a
41 | runAt startRow startColumn (Parser parser) (B.PS fp offset length) =
42 | case parser (State fp offset (offset + length) 0 startRow startColumn []) Ok Err Ok Err of
43 | Ok value _ _ ->
44 | Right value
45 |
46 | Err (E.ParseError row col problem) ->
47 | let
48 | pos = R.Position row col
49 | mkError overallRegion subRegion =
50 | Left (E.Parse overallRegion subRegion problem)
51 | in
52 | case problem of
53 | E.BadChar endCol ->
54 | mkError (R.Region pos (R.Position row endCol)) Nothing
55 |
56 | E.BadEscape width _ ->
57 | mkError (R.Region pos (R.Position row (col + width))) Nothing
58 |
59 | E.BadUnderscore badCol ->
60 | mkError (R.Region pos (R.Position row badCol)) Nothing
61 |
62 | E.BadOp _ ((_, start) : _) ->
63 | mkError (R.Region start pos) (Just (R.Region pos pos))
64 |
65 | E.Theories ((_, start) : _) _ ->
66 | mkError (R.Region start pos) (Just (R.Region pos pos))
67 |
68 | _ ->
69 | mkError (R.Region pos pos) Nothing
70 |
71 |
72 |
73 | -- RESULT
74 |
75 |
76 | data Result a
77 | = Ok a State E.ParseError
78 | | Err E.ParseError
79 |
80 |
81 |
82 | -- COMBINATORS
83 |
84 |
85 | try :: Parser a -> Parser a
86 | try (Parser parser) =
87 | Parser $ \state cok _ eok eerr ->
88 | parser state cok eerr eok eerr
89 |
90 |
91 | deadend :: [E.Theory] -> Parser a
92 | deadend thrys =
93 | Parser $ \(State _ _ _ _ row col ctx) _ _ _ eerr ->
94 | eerr (E.ParseError row col (E.Theories ctx thrys))
95 |
96 |
97 | hint :: E.Next -> Parser a -> Parser a
98 | hint next (Parser parser) =
99 | Parser $ \state@(State _ _ _ _ row col ctx) cok cerr eok eerr ->
100 | let
101 | eok' x s _ =
102 | eok x s (expect row col ctx (E.Expecting next))
103 |
104 | eerr' _ =
105 | eerr (expect row col ctx (E.Expecting next))
106 | in
107 | parser state cok cerr eok' eerr'
108 |
109 |
110 | endOfFile :: Parser ()
111 | endOfFile =
112 | Parser $ \state@(State _ offset terminal _ _ _ _) _ _ eok eerr ->
113 | if offset < terminal then
114 | eerr noError
115 | else
116 | eok () state noError
117 |
118 |
119 | noFloatsAllowedInPatterns :: Parser a
120 | noFloatsAllowedInPatterns =
121 | Parser $ \(State _ _ _ _ row col _) _ cerr _ _ ->
122 | cerr (E.ParseError row col E.FloatInPattern)
123 |
124 |
125 |
126 | -- STATE
127 |
128 |
129 | {-# INLINE getPosition #-}
130 | getPosition :: Parser R.Position
131 | getPosition =
132 | Parser $ \state@(State _ _ _ _ row col _) _ _ eok _ ->
133 | eok (R.Position row col) state noError
134 |
135 |
136 | getIndent :: Parser Int
137 | getIndent =
138 | Parser $ \state@(State _ _ _ indent _ _ _) _ _ eok _ ->
139 | eok indent state noError
140 |
141 |
142 | getCol :: Parser Int
143 | getCol =
144 | Parser $ \state@(State _ _ _ _ _ col _) _ _ eok _ ->
145 | eok col state noError
146 |
147 |
148 | pushContext :: R.Position -> E.Context -> Parser ()
149 | pushContext pos ctx =
150 | Parser $ \state@(State _ _ _ _ _ _ context) _ _ eok _ ->
151 | eok () (state { _context = (ctx, pos) : context }) noError
152 |
153 |
154 | popContext :: a -> Parser a
155 | popContext value =
156 | Parser $ \state@(State _ _ _ _ _ _ context) _ _ eok _ ->
157 | eok value (state { _context = tail context }) noError
158 |
159 |
160 | setIndent :: Int -> Parser ()
161 | setIndent indent =
162 | Parser $ \state _ _ eok _ ->
163 | eok () (state { _indent = indent }) noError
164 |
165 |
166 |
167 | -- SPACE PARSER
168 |
169 |
170 | type SParser a =
171 | Parser (a, R.Position, W.SPos)
172 |
173 |
174 |
175 | -- LOCATION
176 |
177 |
178 | addLocation :: Parser a -> Parser (A.Located a)
179 | addLocation parser =
180 | do start <- getPosition
181 | value <- parser
182 | end <- getPosition
183 | return (A.at start end value)
184 |
185 |
186 | inContext :: R.Position -> E.Context -> Parser a -> Parser a
187 | inContext pos ctx parser =
188 | do pushContext pos ctx
189 | a <- parser
190 | popContext a
191 |
192 |
193 | -- WHITESPACE VARIATIONS
194 |
195 |
196 | spaces :: Parser ()
197 | spaces =
198 | checkSpace =<< W.whitespace
199 |
200 |
201 | noSpace :: R.Position -> W.SPos -> Parser ()
202 | noSpace pos (W.SPos spos) =
203 | if pos == spos
204 | then return ()
205 | else deadend []
206 |
207 |
208 | checkSpace :: W.SPos -> Parser ()
209 | checkSpace (W.SPos (R.Position _ col)) =
210 | do indent <- getIndent
211 | if col > indent && col > 1
212 | then return ()
213 | else deadend [E.BadSpace]
214 |
215 |
216 | checkAligned :: W.SPos -> Parser ()
217 | checkAligned (W.SPos (R.Position _ col)) =
218 | do indent <- getIndent
219 | if col == indent
220 | then return ()
221 | else deadend [E.BadSpace]
222 |
223 |
224 | checkFreshLine :: W.SPos -> Parser ()
225 | checkFreshLine (W.SPos (R.Position _ col)) =
226 | if col == 1
227 | then return ()
228 | else deadend [E.BadSpace]
229 |
--------------------------------------------------------------------------------