Skip to content

Commit 805ae5e

Browse files
authored
Merge pull request #176 from fwcd/haskell-commands
Re-add Haskell commands
2 parents 08e1eef + 0157d02 commit 805ae5e

File tree

7 files changed

+126
-2
lines changed

7 files changed

+126
-2
lines changed

Dockerfile

+8-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,14 @@ RUN curl -sL https://deb.nodesource.com/setup_18.x | bash -
2929

3030
# Install native dependencies
3131
COPY Scripts/install-runtime-dependencies-apt Scripts/
32-
RUN Scripts/install-runtime-dependencies-apt && rm -rf /var/lib/apt/lists/*
32+
RUN Scripts/install-runtime-dependencies-apt && apt-get install -y cabal-install && rm -rf /var/lib/apt/lists/*
33+
34+
# Install Haskell dependencies
35+
COPY Scripts/install-haskell-dependencies Scripts/
36+
RUN cabal update && Scripts/install-haskell-dependencies cabal
37+
38+
# Add Cabal to PATH
39+
ENV PATH /.cabal/bin:/root/.cabal/bin:$PATH
3340

3441
# Install Node dependencies
3542
COPY Scripts/install-node-dependencies Scripts/

README.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ To build and run D2 locally, make sure to have the following installed:
2323

2424
- Linux or macOS
2525
- Swift 6+
26+
- Haskell + cabal-install or Stack (for Hoogle, Pointfree, ...)
2627
- Node.js and npm (for LaTeX rendering)
2728

2829
On Ubuntu, run
@@ -41,7 +42,17 @@ Scripts/install-dependencies-brew
4142

4243
Create a folder named `local` under the repository and add configuration files as described in [the configuration section](#configuration).
4344

44-
To install the dependencies for node packages used by D2, run
45+
To install the Haskell packages used by D2, please install Haskell Stack or cabal-install externally (e.g. via `ghcup`), then run
46+
47+
```sh
48+
Scripts/install-haskell-dependencies stack
49+
```
50+
51+
> If this fails due to version conflicts, check whether your global Stack resolver is too old in `~/.stack/global-project/stack.yaml` and/or append the `--allow-newer` flag to the command, which will be forwarded to Stack by the script.
52+
53+
> This is the command for Haskell Stack, for cabal-install substitute `cabal` for `stack` above.
54+
55+
To install the Node packages used by D2, run
4556

4657
```sh
4758
Scripts/install-node-dependencies

Scripts/install-haskell-dependencies

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/bin/bash
2+
3+
# Installs all Haskell packages that are needed to run D2's Haskell-related commands
4+
5+
set -e
6+
7+
if [ $# -lt 1 ]; then
8+
echo "Usage: $0 <stack or cabal> [additional flags...]"
9+
exit 1
10+
fi
11+
12+
tool="$1"
13+
shift
14+
15+
"$tool" install "$@" happy show mueval pointfree
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import Logging
2+
import D2MessageIO
3+
import Utils
4+
5+
fileprivate let log = Logger(label: "D2Commands.HaskellEvalCommand")
6+
7+
public class HaskellEvalCommand: StringCommand {
8+
public let info = CommandInfo(
9+
category: .programming,
10+
shortDescription: "Evaluates a Haskell expression",
11+
longDescription: "Computes the result of a (pure) Haskell expression using Mueval",
12+
presented: true,
13+
requiredPermissionLevel: .basic
14+
)
15+
public let outputValueType: RichValueType = .code
16+
private let timeout: Int = 4
17+
18+
public init() {}
19+
20+
public func invoke(with input: String, output: CommandOutput, context: CommandContext) async {
21+
do {
22+
let value = try await Shell().utf8(for: "mueval", args: ["-e", input, "-t", String(timeout)]).get() ?? "No output"
23+
await output.append(.code(value, language: "haskell"))
24+
} catch {
25+
await output.append(error, errorText: "Could not evaluate expression.")
26+
}
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import Logging
2+
import D2MessageIO
3+
import Utils
4+
5+
fileprivate let log = Logger(label: "D2Commands.HaskellTypeCommand")
6+
7+
public class HaskellTypeCommand: StringCommand {
8+
public let info = CommandInfo(
9+
category: .programming,
10+
shortDescription: "Fetches the type of a Haskell expression",
11+
longDescription: "Computes the inferred type of a (pure) Haskell expression using Mueval",
12+
presented: true,
13+
requiredPermissionLevel: .basic
14+
)
15+
public let outputValueType: RichValueType = .code
16+
private let timeout: Int = 4
17+
18+
public init() {}
19+
20+
public func invoke(with input: String, output: CommandOutput, context: CommandContext) async {
21+
do {
22+
let lines = try await Array((Shell().utf8(for: "mueval", args: ["-iTe", input, "-t", String(timeout)]).get() ?? "").split(separator: "\n"))
23+
guard lines.count >= 2 else {
24+
log.error("Invalid mueval output: \(lines)")
25+
await output.append(errorText: "Invalid mueval output")
26+
return
27+
}
28+
await output.append(.code("\(lines[0]) :: \(lines[1])", language: "haskell"))
29+
} catch {
30+
await output.append(error, errorText: "Could not fetch inferred expression type.")
31+
}
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import Logging
2+
import D2MessageIO
3+
import Utils
4+
5+
fileprivate let log = Logger(label: "D2Commands.PointfreeCommand")
6+
7+
public class PointfreeCommand: StringCommand {
8+
public let info = CommandInfo(
9+
category: .programming,
10+
shortDescription: "Pointfree notation converter",
11+
longDescription: "Converts a Haskell expression into pointfree notation",
12+
presented: true,
13+
requiredPermissionLevel: .basic
14+
)
15+
public let outputValueType: RichValueType = .code
16+
17+
public init() {}
18+
19+
public func invoke(with input: String, output: CommandOutput, context: CommandContext) async {
20+
do {
21+
let pointfree = try await Shell().utf8(for: "pointfree", args: [input]).get()
22+
await output.append(.code(pointfree ?? "No results", language: "haskell"))
23+
} catch {
24+
await output.append(error, errorText: "An error occurred while converting to pointfree notation")
25+
}
26+
}
27+
}

Sources/D2Handlers/D2Receiver.swift

+3
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,9 @@ public class D2Receiver: Receiver {
303303
registry["buzzwordphrase", aka: ["buzzword", "buzzwords", "buzzphrase", "buzz"]] = BuzzwordPhraseCommand()
304304
registry["buzzwordbingo", aka: ["buzzbingo"]] = BuzzwordBingoCommand()
305305
registry["hoogle"] = HoogleCommand()
306+
registry["haskelleval", aka: ["haskell"]] = HaskellEvalCommand()
307+
registry["haskelltype"] = HaskellTypeCommand()
308+
registry["pointfree"] = PointfreeCommand()
306309
registry["prolog"] = PrologCommand()
307310
registry["reindent", aka: ["indent", "redent"]] = ReindentCommand()
308311
registry["morseencode", aka: ["morse", "morsify"]] = MorseEncoderCommand()

0 commit comments

Comments
 (0)