Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add stdlib test importing every stdlib module #795

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .github/workflows/acme.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Check every stdlib module is compiled (acme)

on:
pull_request:
paths:
- 'libraries/common/**'

jobs:
check-stdlib-sync:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Generate acme and test for differences
id: generate-acme
shell: bash
run: |
# Function to convert file path to module import path
path_to_module() {
local filepath="$1"
# Remove libraries/common/ prefix and .effekt suffix
local module_path="${filepath#libraries/common/}"
module_path="${module_path%.effekt}"
echo "$module_path"
}

# Find all .effekt files in libraries/common, excluding acme.effekt
MODULES=$(find libraries/common -type f -name "*.effekt" | sort | while read -r file; do
path_to_module "$file"
done)

# Create the new acme.effekt content
{
echo "/// This module is auto-generated and checked in CI."
echo "/// It imports **every** stdlib module: ACME = All Common Modules in Effekt"
echo "module acme"
echo ""
for module in $MODULES; do
echo "import $module"
done
echo ""
echo "def main() = ()"
} > generated-acme.effekt

# Compare files, ignoring whitespace, blank lines, and line ending differences
if ! diff -Bbq examples/stdlib/acme.effekt generated-acme.effekt; then
echo "::error::The stdlib import file (examples/stdlib/acme.effekt) is out of sync with the modules in libraries/common."
echo "Differences found:"
diff -Bu examples/stdlib/acme.effekt generated-acme.effekt
exit 1
fi
Empty file added examples/stdlib/acme.check
Empty file.
37 changes: 37 additions & 0 deletions examples/stdlib/acme.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/// This module is auto-generated and checked in CI.
/// It imports **every** stdlib module: ACME = All Common Modules in Effekt
module acme

import args
import array
import bench
import buffer
import bytearray
import char
import dequeue
import effekt
import exception
import io
import io/console
import io/error
import io/filesystem
import io/network
import io/time
import json
import list
import map
import option
import process
import queue
import ref
import regex
import result
import scanner
import seq
import set
import stream
import string
import test
import tty

def main() = ()
8 changes: 8 additions & 0 deletions examples/stdlib/buffer.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
1
false
Some(17)
Some(1)
Some(2)
Some(3)
Some(4)
Some(5)
3 changes: 3 additions & 0 deletions examples/stdlib/buffer.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import buffer

def main() = buffer::examples::main()
65 changes: 65 additions & 0 deletions examples/stdlib/json.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
a

a

a
b
f

{
"x"
:
"Hallo"
,
"y"
:
null
,
"z"
:
12.3783
}

{
"a"
:
null
,
"b"
:
[
true
,
false
,
false
,
true
]
,
"f"
:
12.532
}

{
"a"
:
null
,
"b"
:
[
true
,
false
,
false
,
true
]
,
"f"
:
12.532
}
3 changes: 3 additions & 0 deletions examples/stdlib/json.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import json

def main() = json::test::main()
6 changes: 3 additions & 3 deletions libraries/common/buffer.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def emptyBuffer[T](capacity: Int): Buffer[T] at {global} = {
}
def arrayBuffer[T](initialCapacity: Int): Buffer[T] at {global} = {
// TODO allocate buffer (and array) into a region r.
val contents = emptyArray[T](initialCapacity)
val contents = array::allocate[T](initialCapacity)
var head in global = 0
var tail in global = 0

Expand All @@ -51,15 +51,15 @@ def arrayBuffer[T](initialCapacity: Int): Buffer[T] at {global} = {
def read() = {
if (buffer.empty?) None()
else {
val result: T = contents.remove(head).getOrElse { <> };
val result: T = contents.unsafeGet(head);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is semantically correct, we use it as a circular buffer, so we don't really need to invalidate the previous value -- we'd have overwritten it.

head = mod(head + 1, initialCapacity)
Some(result)
}
}
def write(el: T) = {
if (buffer.full?) <> // raise(BufferOverflow())

contents.put(tail, el)
contents.unsafeSet(tail, el)
tail = mod(tail + 1, initialCapacity)
}
}
Expand Down
13 changes: 8 additions & 5 deletions libraries/common/io/network.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,17 @@ def server(host: String, port: Int, handler: () => Unit / Socket at {io, async,

namespace examples {
def helloWorldApp(): Unit / Socket = {
val request = do receive();
val request = do receive().toString;

println("Received a request: " ++ request.toUTF8)
println("Received a request: " ++ request)

if (request.toUTF8.startsWith("GET /")) {
do send(fromUTF8("HTTP/1.1 200 OK\r\n\r\nHello from Effekt!"))
def respond(s: String): Unit / Socket =
do send(s.fromString)

if (request.startsWith("GET /")) {
respond("HTTP/1.1 200 OK\r\n\r\nHello from Effekt!")
} else {
do send("HTTP/1.1 400 Bad Request\r\n\r\n".fromUTF8)
respond("HTTP/1.1 400 Bad Request\r\n\r\n")
}
do end()
}
Expand Down
52 changes: 30 additions & 22 deletions libraries/common/json.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def encodeJsonObject[R]{ body: => R / JsonObjectBuilder }: R / emit[String] = {
do emit("{")
def c(k: String) = {
if (not(first)) { do emit(",") }
do emit(escape(k)); do emit(":")
do emit(escape(k)); do emit(":")
first = false
}
val r = encodeJson {
Expand All @@ -76,7 +76,7 @@ def encodeJsonObject[R]{ body: => R / JsonObjectBuilder }: R / emit[String] = {
/// Main entry point for encoding json.
/// Emits individual tokens of the resulting json.
def encodeJson[R]{ body: => R / JsonBuilder }: R / emit[String] = {
try body() with JsonBuilder {
try body() with JsonBuilder {
def null() = { resume(do emit("null")) }
def bool(v) = { resume(do emit( if(v){ "true" } else { "false" } )) }
def number(n) = { resume(do emit(show(n))) }
Expand Down Expand Up @@ -112,7 +112,7 @@ def readDouble(): Double / Scan[Char] = {
if (optionally { readIf('.') }) {
var b = 0.1
var r = pre.toDouble
while (optionally[Int] { readDigit() } is Some(d)) {
while (returning::optionally[Int] { readDigit() } is Some(d)) {
r = r + b * d.toDouble
b = b * 0.1
}
Expand All @@ -126,14 +126,14 @@ def readDouble(): Double / Scan[Char] = {

def expectString(string: String): Unit / { Scan[Char], Exception[WrongFormat] } =
for[Char] { string.each } { char =>
expect[Unit]("Expected " ++ string) { readIf(char) }
expect("Expected " ++ string) { readIf(char) }
}

/// Read and unescape a string in ""
def readQuotedString(): Unit / { Scan[Char], emit[Char], Exception[WrongFormat] } = {
try {
skipWhitespace()
expect[Unit]("Expected \"") { readIf('"') }
expect("Expected \"") { readIf('"') }
while(read[Char]() is c and c != '"') {
c match {
case '\\' => read[Char]() match {
Expand Down Expand Up @@ -191,7 +191,7 @@ def decodeJsonObject(): Unit / {Scan[Char], JsonObjectBuilder, Exception[WrongFo
}
do skip[Char]()
}
def decodeJsonList(): Unit / {Scan[Char], JsonBuilder, Exception[WrongFormat]} = {
def decodeJsonList(): Unit / {Scan[Char], JsonBuilder, Exception[WrongFormat]} = {
var first = true
expectString("[")
with boundary
Expand Down Expand Up @@ -278,7 +278,7 @@ def build[R](){ body: => R / JsonBuilder }: (R, JsonValue) = {
}
(x, r)
}
def buildList[R](){ body: => R / JsonBuilder }: (R, List[JsonValue]) = collectList[JsonValue, R] {
def buildList[R](){ body: => R / JsonBuilder }: (R, List[JsonValue]) = returning::collectList[JsonValue, R] {
try body() with JsonBuilder {
def number(n) = { do emit(Number(n)); resume(()) }
def bool(b) = { do emit(Bool(b)); resume(()) }
Expand All @@ -296,7 +296,7 @@ def buildList[R](){ body: => R / JsonBuilder }: (R, List[JsonValue]) = collectLi
}
}
}
def buildDict[R](){ body: => R / JsonObjectBuilder }: (R, List[(String, JsonValue)]) = collectList[(String, JsonValue), R] {
def buildDict[R](){ body: => R / JsonObjectBuilder }: (R, List[(String, JsonValue)]) = returning::collectList[(String, JsonValue), R] {
try body() with JsonObjectBuilder {
def field(k) = resume { {v} =>
val x = build{v}
Expand All @@ -315,10 +315,12 @@ namespace test {

// Read quoted string
feed("\"\ta\n\ra\"") {
with scanner[Char, Unit]
with scanner[Char]
println(collectString { readQuotedString() })
}

println("")

// Parse example
feed("""{ "a": null, "b": [true,false,false,true], "f": 12.532 }"""){
with scanner[Char]
Expand All @@ -336,8 +338,10 @@ namespace test {
}
handleJsonBuilder{d}{ decodeJson() }
}
try{


println("")

try {
// Encode example
encodeJson {
do dict{
Expand All @@ -352,32 +356,36 @@ namespace test {
}
}
}


println("")

// format with intermediate value
val j = feed("""{
"a": null,
"b": [true, false, false, true], "f": 12.532
val j = feed[JsonValue]("""{
"a": null,
"b": [true, false, false, true], "f": 12.532
} """){
with scanner[Char, JsonValue]
with returning::scanner[Char, JsonValue]
build {
decodeJson()
}.second
}
encodeJson{
unbuild(j)
}


println("")

// format (minify) example
encodeJson{
feed("""{
"a": null,
"b": [true, false, false, true], "f": 12.532
feed("""{
"a": null,
"b": [true, false, false, true], "f": 12.532
} """){
with scanner[Char, Unit]
with scanner[Char]
decodeJson()
}
}

} with emit[String] { e =>
resume(println(e))
}
Expand Down
Loading