Project P030

Room booking

Books three meeting rooms of 4, 8 and 12 seats over a working week. A booking that overlaps another in the same room is refused, naming the booking it clashes with and the rooms that are free instead - back-to-back bookings do not clash; each day has a schedule, free rooms can be looked up, and bookings can be cancelled.

3 modules · 2 recorded sessionstext-menu UI in the terminalupdated 2026-10-06

Every screen below was recorded under CPython. When this page was built, the EML interpreter replayed each session from the same input and printed the same bytes.

About

Books three meeting rooms - Oak (4 seats), Pine (8) and Cedar (12) - from Monday to Friday, 08:00 to 20:00. A booking that overlaps another in the same room is refused: the program names the booking it clashes with and the rooms that are free at that time for that many people. Each day has a schedule, free rooms can be looked up, and bookings can be cancelled by number.

  • main.eml - the menu, the questions and their checks, and what the screen shows
  • bookings.eml - the rooms, the overlap test, clashes, free rooms, a room's day in order, and cancelling
  • hours.eml - weekdays, reading and writing times

How each part works:

  • A booking runs from its start up to, not including, its end. Two bookings overlap when each starts before the other ends - the predicate of the corpus case interval-overlap-detector. So 09:00-09:45 and 09:45-10:30 do not clash (back-to-back is the most common case in a real calendar), while 11:59-12:01 clashes with 08:00-12:00.
  • Before a booking is made, the clashes are looked for in that room on that day; if there are any, the booking is refused with each clashing booking and the list of rooms with enough seats and no clash.
  • A day's schedule lists each room's bookings, earliest first, with numbers; a room with none is free all day.
  • Booking numbers count up from 1 and are never reused, so a cancelled number stays gone.

What is checked: a room by name in any case; a weekday by its name or its first three letters; a start from 08:00 and an end after it, no later than 20:00, at most 4 hours later; 1 to 12 people, and no more than the room's seats; a title of 1 to 30 characters; a booking number that exists (with or without #). An empty answer cancels.

Sessions: sessions/basic.in books a stand-up and a back-to-back design review in Oak, is refused 10:00-11:00 in Oak (clash with the review; Pine and Cedar are free), books Cedar and Pine in overlapping hours, shows Monday, finds no room for 6 people on Monday 13:00-14:00 and two on Tuesday, cancels the review and then books 10:00-11:00 in Oak; sessions/bad-input.in types menu choices that are not 1 to 5, Saturday, a cancel with no bookings, a room that does not exist, funday, starts before opening and at 25:00, ends before the start and after closing, too many people for Oak, 0 people, a booking longer than 4 hours, 13 people, a title of 31 characters, a two-minute booking inside another, a booking number that is not a number and one that does not exist.

Built on the verified corpus case interval-overlap-detector (half-open intervals: touching is not overlapping).

Recorded sessions

What the screen shows while someone uses the program. Each typed line appears after its prompt, the way a terminal shows it.

bad-input

interpreter: byte-equal
== Room booking ==
Rooms: Oak (4 seats), Pine (8) and Cedar (12); Mon to Fri, 08:00 to 20:00.

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 0
Pick a number from 1 to 5.

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> x
Pick a number from 1 to 5.

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 2
day (Mon to Fri)> Sat
Type a weekday, Mon to Fri.
day (Mon to Fri)> 
Cancelled.

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 3
There are no bookings.

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 1
room (Oak, Pine or Cedar)> Maple
There are three rooms: Oak, Pine and Cedar.
room (Oak, Pine or Cedar)> 
Cancelled.

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 1
room (Oak, Pine or Cedar)> Oak
day (Mon to Fri)> funday
Type a weekday, Mon to Fri.
day (Mon to Fri)> Tue
from (HH:MM)> 7:30
The rooms are open from 08:00 to 20:00; type a start such as 09:30.
from (HH:MM)> 25:00
The rooms are open from 08:00 to 20:00; type a start such as 09:30.
from (HH:MM)> 19:45
to (HH:MM)> 19:30
Type an end after 19:45 and no later than 20:00.
to (HH:MM)> 20:15
Type an end after 19:45 and no later than 20:00.
to (HH:MM)> 20:00
people> 5
Oak has 4 seats.
people> 0
Type a number of people from 1 to 12.
people> 4
title> 
Cancelled.

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 1
room (Oak, Pine or Cedar)> Pine
day (Mon to Fri)> Wed
from (HH:MM)> 08:00
to (HH:MM)> 12:30
A booking can last at most 4 hours.
to (HH:MM)> 12:00
people> 13
Type a number of people from 1 to 12.
people> 8
title> A title that is thirty-one long
A title can have at most 30 characters.
title> Workshop
Booked #1: Pine, Wed 08:00-12:00, Workshop (8 people).

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 1
room (Oak, Pine or Cedar)> pine
day (Mon to Fri)> wed
from (HH:MM)> 11:59
to (HH:MM)> 12:01
people> 2
Pine is booked then:
  #1 08:00-12:00 Workshop
Free then for 2 people: Oak (4 seats) and Cedar (12 seats).

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 3
booking number> abc
Type a booking number, such as 3.
booking number> #7
There is no booking #7.
booking number> 
Cancelled.

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 3
booking number> #1
Cancelled #1: Pine, Wed 08:00-12:00, Workshop.

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 4
day (Mon to Fri)> Wed
from (HH:MM)> 08:00
to (HH:MM)> 12:00
people> 12
Free on Wed 08:00-12:00 for 12 people: Cedar (12 seats).

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 4
day (Mon to Fri)> 
Cancelled.

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 5
Bye.
What was typed (53 lines)
0
x
2
Sat

3
1
Maple

1
Oak
funday
Tue
7:30
25:00
19:45
19:30
20:15
20:00
5
0
4

1
Pine
Wed
08:00
12:30
12:00
13
8
A title that is thirty-one long
Workshop
1
pine
wed
11:59
12:01
2
3
abc
#7

3
#1
4
Wed
08:00
12:00
12
4

5

basic

interpreter: byte-equal
== Room booking ==
Rooms: Oak (4 seats), Pine (8) and Cedar (12); Mon to Fri, 08:00 to 20:00.

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 1
room (Oak, Pine or Cedar)> Oak
day (Mon to Fri)> mon
from (HH:MM)> 09:00
to (HH:MM)> 09:45
people> 3
title> Standup
Booked #1: Oak, Mon 09:00-09:45, Standup (3 people).

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 1
room (Oak, Pine or Cedar)> Oak
day (Mon to Fri)> Monday
from (HH:MM)> 09:45
to (HH:MM)> 10:30
people> 4
title> Design review
Booked #2: Oak, Mon 09:45-10:30, Design review (4 people).

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 1
room (Oak, Pine or Cedar)> oak
day (Mon to Fri)> Mon
from (HH:MM)> 10:00
to (HH:MM)> 11:00
people> 3
Oak is booked then:
  #2 09:45-10:30 Design review
Free then for 3 people: Pine (8 seats) and Cedar (12 seats).

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 1
room (Oak, Pine or Cedar)> Cedar
day (Mon to Fri)> Mon
from (HH:MM)> 13:00
to (HH:MM)> 14:00
people> 12
title> All hands
Booked #3: Cedar, Mon 13:00-14:00, All hands (12 people).

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 1
room (Oak, Pine or Cedar)> Pine
day (Mon to Fri)> Mon
from (HH:MM)> 13:30
to (HH:MM)> 14:30
people> 6
title> Interview
Booked #4: Pine, Mon 13:30-14:30, Interview (6 people).

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 2
day (Mon to Fri)> Mon
Mon:
  Oak (4 seats)     09:00-09:45  #1 Standup (3 people)
                    09:45-10:30  #2 Design review (4 people)
  Pine (8 seats)    13:30-14:30  #4 Interview (6 people)
  Cedar (12 seats)  13:00-14:00  #3 All hands (12 people)

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 4
day (Mon to Fri)> Mon
from (HH:MM)> 13:00
to (HH:MM)> 14:00
people> 6
No room is free on Mon 13:00-14:00 for 6 people.

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 4
day (Mon to Fri)> Tue
from (HH:MM)> 14:00
to (HH:MM)> 15:30
people> 6
Free on Tue 14:00-15:30 for 6 people: Pine (8 seats) and Cedar (12 seats).

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 3
booking number> 2
Cancelled #2: Oak, Mon 09:45-10:30, Design review.

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 1
room (Oak, Pine or Cedar)> Oak
day (Mon to Fri)> Mon
from (HH:MM)> 10:00
to (HH:MM)> 11:00
people> 3
title> Planning
Booked #5: Oak, Mon 10:00-11:00, Planning (3 people).

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 2
day (Mon to Fri)> mon
Mon:
  Oak (4 seats)     09:00-09:45  #1 Standup (3 people)
                    10:00-11:00  #5 Planning (3 people)
  Pine (8 seats)    13:30-14:30  #4 Interview (6 people)
  Cedar (12 seats)  13:00-14:00  #3 All hands (12 people)

1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit
choice> 5
Bye.
What was typed (58 lines)
1
Oak
mon
09:00
09:45
3
Standup
1
Oak
Monday
09:45
10:30
4
Design review
1
oak
Mon
10:00
11:00
3
1
Cedar
Mon
13:00
14:00
12
All hands
1
Pine
Mon
13:30
14:30
6
Interview
2
Mon
4
Mon
13:00
14:00
6
4
Tue
14:00
15:30
6
3
2
1
Oak
Mon
10:00
11:00
3
Planning
2
mon
5

Modules

The program as written, entry module first. Each module transpiles to its own Python file, which is what eml project run executes.

main.eml(entry)

eml
# P030 room booking: three meeting rooms over a working week. A booking that
# overlaps another in the same room is refused, with the booking it clashes
# with and the rooms that are free instead; each day has a schedule, and
# bookings can be cancelled.
import bookings
import hours

8 * 60 => opening
20 * 60 => closing
4 * 60 => longest

def listed(items):
    "" => out
    0 => i
    while i < len(items):
        if i > 0 and i == len(items) - 1:
            out + " and " => out
        elif i > 0:
            out + ", " => out
        out + items[i] => out
        i + 1 => i
    return out

def head_count(n):
    if n == 1:
        return "1 person"
    return str(n) + " people"

def whole_number(s):
    # 1 to 3 digits, with an optional # in front; -1 for anything else.
    hours.trim(s) => s
    if s != "" and s[0] == "#":
        s[1:len(s)] => s
    if s == "" or len(s) > 3:
        return 0 - 1
    0 => n
    for c in s:
        if not (c in "0123456789"):
            return 0 - 1
        n * 10 + int(c) => n
    return n

def ask_room():
    while True:
        hours.trim(input("room (Oak, Pine or Cedar)> ")) => answer
        if answer == "":
            return 0 - 1
        bookings.room_of(answer) => r
        if r >= 0:
            return r
        "There are three rooms: Oak, Pine and Cedar." ^0

def ask_day():
    while True:
        input("day (Mon to Fri)> ") => answer
        if hours.trim(answer) == "":
            return 0 - 1
        hours.day_of(answer) => d
        if d >= 0:
            return d
        "Type a weekday, Mon to Fri." ^0

def ask_times():
    # [start, end] inside opening hours, at most 4 hours long; [] cancels.
    0 - 1 => start
    while start == 0 - 1:
        input("from (HH:MM)> ") => answer
        if hours.trim(answer) == "":
            return []
        hours.minute_of(answer) => start
        if start == 0 - 1 or start < opening or start >= closing:
            "The rooms are open from 08:00 to 20:00; type a start such as 09:30." ^0
            0 - 1 => start
    0 - 1 => end
    while end == 0 - 1:
        input("to (HH:MM)> ") => answer
        if hours.trim(answer) == "":
            return []
        hours.minute_of(answer) => end
        if end == 0 - 1 or end <= start or end > closing:
            ("Type an end after " + hours.clock(start) + " and no later than 20:00.") ^0
            0 - 1 => end
        elif end - start > longest:
            "A booking can last at most 4 hours." ^0
            0 - 1 => end
    return [start, end]

def ask_people(most):
    while True:
        input("people> ") => answer
        if hours.trim(answer) == "":
            return 0 - 1
        whole_number(answer) => n
        if n >= 1 and n <= most:
            return n
        ("Type a number of people from 1 to " + str(most) + ".") ^0

def room_name(r):
    return bookings.rooms[r] + " (" + str(bookings.seats[r]) + " seats)"

def describe(b):
    return "#" + str(b[0]) + ": " + bookings.rooms[b[1]] + ", " + hours.days[b[2]] + " " + hours.span(b[3], b[4]) + ", " + b[6]

def say_free(found, people):
    [] => names
    for r in found:
        names + [room_name(r)] => names
    if len(names) == 0:
        ("No room is free then for " + head_count(people) + ".") ^0
    else:
        ("Free then for " + head_count(people) + ": " + listed(names) + ".") ^0

def book(state):
    # state is [bookings, next number]; returns the new state.
    ask_room() => r
    if r == 0 - 1:
        "Cancelled." ^0
        return state
    ask_day() => d
    if d == 0 - 1:
        "Cancelled." ^0
        return state
    ask_times() => t
    if len(t) == 0:
        "Cancelled." ^0
        return state
    ask_people(12) => people
    while people > bookings.seats[r]:
        (bookings.rooms[r] + " has " + str(bookings.seats[r]) + " seats.") ^0
        ask_people(12) => people
    if people == 0 - 1:
        "Cancelled." ^0
        return state
    bookings.clashes(state[0], r, d, t[0], t[1]) => taken
    if len(taken) > 0:
        (bookings.rooms[r] + " is booked then:") ^0
        for b in taken:
            ("  #" + str(b[0]) + " " + hours.span(b[3], b[4]) + " " + b[6]) ^0
        say_free(bookings.free_rooms(state[0], d, t[0], t[1], people), people)
        return state
    "" => title
    while title == "":
        hours.trim(input("title> ")) => title
        if title == "":
            "Cancelled." ^0
            return state
        if len(title) > 30:
            "A title can have at most 30 characters." ^0
            "" => title
    [state[1], r, d, t[0], t[1], people, title] => b
    ("Booked " + describe(b) + " (" + head_count(people) + ").") ^0
    return [state[0] + [b], state[1] + 1]

def schedule(state):
    ask_day() => d
    if d == 0 - 1:
        "Cancelled." ^0
        return
    (hours.days[d] + ":") ^0
    0 => width
    for r in [0:len(bookings.rooms) - 1]:
        if len(room_name(r)) > width:
            len(room_name(r)) => width
    for r in [0:len(bookings.rooms) - 1]:
        room_name(r) => label
        while len(label) < width:
            label + " " => label
        bookings.in_order(state[0], r, d) => day_bookings
        if len(day_bookings) == 0:
            ("  " + label + "  free all day") ^0
        else:
            for b in day_bookings:
                ("  " + label + "  " + hours.span(b[3], b[4]) + "  #" + str(b[0]) + " " + b[6] + " (" + head_count(b[5]) + ")") ^0
                " " * width => label

def cancel(state):
    if len(state[0]) == 0:
        "There are no bookings." ^0
        return state
    while True:
        input("booking number> ") => answer
        if hours.trim(answer) == "":
            "Cancelled." ^0
            return state
        whole_number(answer) => n
        None => found
        for b in state[0]:
            if b[0] == n:
                b => found
        if n == 0 - 1:
            "Type a booking number, such as 3." ^0
        elif found == None:
            ("There is no booking #" + str(n) + ".") ^0
        else:
            ("Cancelled " + describe(found) + ".") ^0
            return [bookings.without(state[0], n), state[1]]

def free(state):
    ask_day() => d
    if d == 0 - 1:
        "Cancelled." ^0
        return
    ask_times() => t
    if len(t) == 0:
        "Cancelled." ^0
        return
    ask_people(12) => people
    if people == 0 - 1:
        "Cancelled." ^0
        return
    bookings.free_rooms(state[0], d, t[0], t[1], people) => found
    [] => names
    for r in found:
        names + [room_name(r)] => names
    "on " + hours.days[d] + " " + hours.span(t[0], t[1]) + " for " + head_count(people) => when
    if len(names) == 0:
        ("No room is free " + when + ".") ^0
    else:
        ("Free " + when + ": " + listed(names) + ".") ^0

"== Room booking ==" ^0
"Rooms: Oak (4 seats), Pine (8) and Cedar (12); Mon to Fri, 08:00 to 20:00." ^0
[[], 1] => state
True => running
while running:
    "" ^0
    "1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit" ^0
    hours.trim(input("choice> ")) => choice
    if choice == "1":
        book(state) => state
    elif choice == "2":
        schedule(state)
    elif choice == "3":
        cancel(state) => state
    elif choice == "4":
        free(state)
    elif choice == "5":
        False => running
    else:
        "Pick a number from 1 to 5." ^0
"Bye." ^0
Python projection (main.py)
import bookings
import hours
opening = 8 * 60
closing = 20 * 60
longest = 4 * 60

def listed(items):
    out = ""
    i = 0
    while i < len(items):
        if i > 0 and i == len(items) - 1:
            out = out + " and "
        elif i > 0:
            out = out + ", "
        out = out + items[i]
        i = i + 1
    return out

def head_count(n):
    if n == 1:
        return "1 person"
    return str(n) + " people"

def whole_number(s):
    s = hours.trim(s)
    if s != "" and s[0] == "#":
        s = s[1:len(s)]
    if s == "" or len(s) > 3:
        return 0 - 1
    n = 0
    for c in s:
        if not c in "0123456789":
            return 0 - 1
        n = n * 10 + int(c)
    return n

def ask_room():
    while True:
        answer = hours.trim(input("room (Oak, Pine or Cedar)> "))
        if answer == "":
            return 0 - 1
        r = bookings.room_of(answer)
        if r >= 0:
            return r
        print("There are three rooms: Oak, Pine and Cedar.")

def ask_day():
    while True:
        answer = input("day (Mon to Fri)> ")
        if hours.trim(answer) == "":
            return 0 - 1
        d = hours.day_of(answer)
        if d >= 0:
            return d
        print("Type a weekday, Mon to Fri.")

def ask_times():
    start = 0 - 1
    while start == 0 - 1:
        answer = input("from (HH:MM)> ")
        if hours.trim(answer) == "":
            return []
        start = hours.minute_of(answer)
        if start == 0 - 1 or start < opening or start >= closing:
            print("The rooms are open from 08:00 to 20:00; type a start such as 09:30.")
            start = 0 - 1
    end = 0 - 1
    while end == 0 - 1:
        answer = input("to (HH:MM)> ")
        if hours.trim(answer) == "":
            return []
        end = hours.minute_of(answer)
        if end == 0 - 1 or end <= start or end > closing:
            print("Type an end after " + hours.clock(start) + " and no later than 20:00.")
            end = 0 - 1
        elif end - start > longest:
            print("A booking can last at most 4 hours.")
            end = 0 - 1
    return [start, end]

def ask_people(most):
    while True:
        answer = input("people> ")
        if hours.trim(answer) == "":
            return 0 - 1
        n = whole_number(answer)
        if n >= 1 and n <= most:
            return n
        print("Type a number of people from 1 to " + str(most) + ".")

def room_name(r):
    return bookings.rooms[r] + " (" + str(bookings.seats[r]) + " seats)"

def describe(b):
    return "#" + str(b[0]) + ": " + bookings.rooms[b[1]] + ", " + hours.days[b[2]] + " " + hours.span(b[3], b[4]) + ", " + b[6]

def say_free(found, people):
    names = []
    for r in found:
        names = names + [room_name(r)]
    if len(names) == 0:
        print("No room is free then for " + head_count(people) + ".")
    else:
        print("Free then for " + head_count(people) + ": " + listed(names) + ".")

def book(state):
    r = ask_room()
    if r == 0 - 1:
        print("Cancelled.")
        return state
    d = ask_day()
    if d == 0 - 1:
        print("Cancelled.")
        return state
    t = ask_times()
    if len(t) == 0:
        print("Cancelled.")
        return state
    people = ask_people(12)
    while people > bookings.seats[r]:
        print(bookings.rooms[r] + " has " + str(bookings.seats[r]) + " seats.")
        people = ask_people(12)
    if people == 0 - 1:
        print("Cancelled.")
        return state
    taken = bookings.clashes(state[0], r, d, t[0], t[1])
    if len(taken) > 0:
        print(bookings.rooms[r] + " is booked then:")
        for b in taken:
            print("  #" + str(b[0]) + " " + hours.span(b[3], b[4]) + " " + b[6])
        say_free(bookings.free_rooms(state[0], d, t[0], t[1], people), people)
        return state
    title = ""
    while title == "":
        title = hours.trim(input("title> "))
        if title == "":
            print("Cancelled.")
            return state
        if len(title) > 30:
            print("A title can have at most 30 characters.")
            title = ""
    b = [state[1], r, d, t[0], t[1], people, title]
    print("Booked " + describe(b) + " (" + head_count(people) + ").")
    return [state[0] + [b], state[1] + 1]

def schedule(state):
    d = ask_day()
    if d == 0 - 1:
        print("Cancelled.")
        return
    print(hours.days[d] + ":")
    width = 0
    for r in range(0, len(bookings.rooms)):
        if len(room_name(r)) > width:
            width = len(room_name(r))
    for r in range(0, len(bookings.rooms)):
        label = room_name(r)
        while len(label) < width:
            label = label + " "
        day_bookings = bookings.in_order(state[0], r, d)
        if len(day_bookings) == 0:
            print("  " + label + "  free all day")
        else:
            for b in day_bookings:
                print("  " + label + "  " + hours.span(b[3], b[4]) + "  #" + str(b[0]) + " " + b[6] + " (" + head_count(b[5]) + ")")
                label = " " * width

def cancel(state):
    if len(state[0]) == 0:
        print("There are no bookings.")
        return state
    while True:
        answer = input("booking number> ")
        if hours.trim(answer) == "":
            print("Cancelled.")
            return state
        n = whole_number(answer)
        found = None
        for b in state[0]:
            if b[0] == n:
                found = b
        if n == 0 - 1:
            print("Type a booking number, such as 3.")
        elif found == None:
            print("There is no booking #" + str(n) + ".")
        else:
            print("Cancelled " + describe(found) + ".")
            return [bookings.without(state[0], n), state[1]]

def free(state):
    d = ask_day()
    if d == 0 - 1:
        print("Cancelled.")
        return
    t = ask_times()
    if len(t) == 0:
        print("Cancelled.")
        return
    people = ask_people(12)
    if people == 0 - 1:
        print("Cancelled.")
        return
    found = bookings.free_rooms(state[0], d, t[0], t[1], people)
    names = []
    for r in found:
        names = names + [room_name(r)]
    when = "on " + hours.days[d] + " " + hours.span(t[0], t[1]) + " for " + head_count(people)
    if len(names) == 0:
        print("No room is free " + when + ".")
    else:
        print("Free " + when + ": " + listed(names) + ".")

print("== Room booking ==")
print("Rooms: Oak (4 seats), Pine (8) and Cedar (12); Mon to Fri, 08:00 to 20:00.")
state = [[], 1]
running = True
while running:
    print("")
    print("1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit")
    choice = hours.trim(input("choice> "))
    if choice == "1":
        state = book(state)
    elif choice == "2":
        schedule(state)
    elif choice == "3":
        state = cancel(state)
    elif choice == "4":
        free(state)
    elif choice == "5":
        running = False
    else:
        print("Pick a number from 1 to 5.")
print("Bye.")

bookings.eml

eml
# P030 room booking - rooms, bookings and conflicts. A booking is
# [number, room, day, start, end, people, title]; room and day are indexes,
# start and end are minutes after midnight.

["Oak", "Pine", "Cedar"] => rooms
[4, 8, 12] => seats

def overlaps(a_start, a_end, b_start, b_end):
    # Half-open times [start, end): a booking that ends at 10:00 and one that
    # starts at 10:00 do not overlap - the predicate of the corpus case
    # interval-overlap-detector.
    return a_start < b_end and b_start < a_end

def clashes(bookings, room, day, start, end):
    # The bookings of that room on that day that overlap [start, end), in
    # the order they were made.
    [] => out
    for b in bookings:
        if b[1] == room and b[2] == day and overlaps(start, end, b[3], b[4]):
            out + [b] => out
    return out

def free_rooms(bookings, day, start, end, people):
    # The indexes of the rooms with enough seats and nothing booked in
    # [start, end) that day.
    [] => out
    for r in [0:len(rooms) - 1]:
        if seats[r] >= people and len(clashes(bookings, r, day, start, end)) == 0:
            out + [r] => out
    return out

def in_order(bookings, room, day):
    # That room's bookings on that day, earliest first (insertion sort).
    [] => out
    for b in bookings:
        if b[1] == room and b[2] == day:
            len(out) => j
            while j > 0 and out[j - 1][3] > b[3]:
                j - 1 => j
            out[0:j] + [b] + out[j:len(out)] => out
    return out

def without(bookings, number):
    # The bookings with that number left out.
    [] => out
    for b in bookings:
        if b[0] != number:
            out + [b] => out
    return out

def room_of(name):
    # The index of a room by name, in any case; -1 if there is none.
    for r in [0:len(rooms) - 1]:
        if lower(rooms[r]) == lower(name):
            return r
    return 0 - 1

def lower(s):
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ" => upper
    "abcdefghijklmnopqrstuvwxyz" => small
    "" => out
    for c in s:
        0 => k
        while k < 26 and upper[k] != c:
            k + 1 => k
        if k < 26:
            out + small[k] => out
        else:
            out + c => out
    return out
Python projection (bookings.py)
rooms = ["Oak", "Pine", "Cedar"]
seats = [4, 8, 12]

def overlaps(a_start, a_end, b_start, b_end):
    return a_start < b_end and b_start < a_end

def clashes(bookings, room, day, start, end):
    out = []
    for b in bookings:
        if b[1] == room and b[2] == day and overlaps(start, end, b[3], b[4]):
            out = out + [b]
    return out

def free_rooms(bookings, day, start, end, people):
    out = []
    for r in range(0, len(rooms)):
        if seats[r] >= people and len(clashes(bookings, r, day, start, end)) == 0:
            out = out + [r]
    return out

def in_order(bookings, room, day):
    out = []
    for b in bookings:
        if b[1] == room and b[2] == day:
            j = len(out)
            while j > 0 and out[j - 1][3] > b[3]:
                j = j - 1
            out = out[0:j] + [b] + out[j:len(out)]
    return out

def without(bookings, number):
    out = []
    for b in bookings:
        if b[0] != number:
            out = out + [b]
    return out

def room_of(name):
    for r in range(0, len(rooms)):
        if lower(rooms[r]) == lower(name):
            return r
    return 0 - 1

def lower(s):
    upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    small = "abcdefghijklmnopqrstuvwxyz"
    out = ""
    for c in s:
        k = 0
        while k < 26 and upper[k] != c:
            k = k + 1
        if k < 26:
            out = out + small[k]
        else:
            out = out + c
    return out

hours.eml

eml
# P030 room booking - days and times. A time is kept as minutes after
# midnight; the interpreter that checks every session does not run string
# methods yet, so the text handling is written out here.

["Mon", "Tue", "Wed", "Thu", "Fri"] => days
"0123456789" => digits

def trim(s):
    0 => i
    len(s) => j
    while i < j and s[i] == " ":
        i + 1 => i
    while j > i and s[j - 1] == " ":
        j - 1 => j
    return s[i:j]

def lower(s):
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ" => upper
    "abcdefghijklmnopqrstuvwxyz" => small
    "" => out
    for c in s:
        0 => k
        while k < 26 and upper[k] != c:
            k + 1 => k
        if k < 26:
            out + small[k] => out
        else:
            out + c => out
    return out

def day_of(s):
    # The index of a weekday from its name or its first three letters, in
    # any case ("mon", "Monday"); -1 otherwise.
    lower(trim(s)) => s
    ["monday", "tuesday", "wednesday", "thursday", "friday"] => names
    for i in [0:4]:
        if s == names[i] or (len(s) == 3 and s == names[i][0:3]):
            return i
    return 0 - 1

def minute_of(s):
    # Minutes after midnight for "9:30" or "09:30" (00:00 to 23:59), else -1.
    trim(s) => s
    0 => colon
    while colon < len(s) and s[colon] != ":":
        colon + 1 => colon
    if colon == 0 or colon > 2 or colon + 3 != len(s):
        return 0 - 1
    0 => h
    for c in s[0:colon]:
        if not (c in digits):
            return 0 - 1
        h * 10 + int(c) => h
    if not (s[colon + 1] in digits and s[colon + 2] in digits):
        return 0 - 1
    int(s[colon + 1]) * 10 + int(s[colon + 2]) => m
    if h > 23 or m > 59:
        return 0 - 1
    return h * 60 + m

def clock(minute):
    # 570 as "09:30".
    int((minute - minute % 60) / 60) => h
    minute % 60 => m
    "" => out
    if h < 10:
        "0" => out
    out + str(h) + ":" => out
    if m < 10:
        out + "0" => out
    return out + str(m)

def span(start, end):
    return clock(start) + "-" + clock(end)
Python projection (hours.py)
days = ["Mon", "Tue", "Wed", "Thu", "Fri"]
digits = "0123456789"

def trim(s):
    i = 0
    j = len(s)
    while i < j and s[i] == " ":
        i = i + 1
    while j > i and s[j - 1] == " ":
        j = j - 1
    return s[i:j]

def lower(s):
    upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    small = "abcdefghijklmnopqrstuvwxyz"
    out = ""
    for c in s:
        k = 0
        while k < 26 and upper[k] != c:
            k = k + 1
        if k < 26:
            out = out + small[k]
        else:
            out = out + c
    return out

def day_of(s):
    s = lower(trim(s))
    names = ["monday", "tuesday", "wednesday", "thursday", "friday"]
    for i in range(0, 5):
        if s == names[i] or len(s) == 3 and s == names[i][0:3]:
            return i
    return 0 - 1

def minute_of(s):
    s = trim(s)
    colon = 0
    while colon < len(s) and s[colon] != ":":
        colon = colon + 1
    if colon == 0 or colon > 2 or colon + 3 != len(s):
        return 0 - 1
    h = 0
    for c in s[0:colon]:
        if not c in digits:
            return 0 - 1
        h = h * 10 + int(c)
    if not (s[colon + 1] in digits and s[colon + 2] in digits):
        return 0 - 1
    m = int(s[colon + 1]) * 10 + int(s[colon + 2])
    if h > 23 or m > 59:
        return 0 - 1
    return h * 60 + m

def clock(minute):
    h = int((minute - minute % 60) / 60)
    m = minute % 60
    out = ""
    if h < 10:
        out = "0"
    out = out + str(h) + ":"
    if m < 10:
        out = out + "0"
    return out + str(m)

def span(start, end):
    return clock(start) + "-" + clock(end)

Built on these corpus cases

ΣEML 2026

Efficient New Language — a high-density semantic overlay for humans and AI agents.

This site’s demos run on the real EML toolchain, in your browser.

© 2026 EveMissLab (一言諾科技有限公司) / Neo.K. All rights reserved.