Project P122

Turnstile

A coin unlocks the gate and a push lets one person through: three machines that differ only in a coin while the gate is open, step by step, side by side, and over every short sequence of events.

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

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

A coin unlocks the gate and a push lets one person through; a push on a locked gate is refused. Three machines differ only in what a coin does when the gate is already open.

  • main.eml - the menu, the machines, one machine step by step, all three side by side, and every short sequence of events
  • gate.eml - one step of each machine, a run of events with its totals, and the count over every sequence

How each part works:

  • As in the corpus case turnstile-simulator, a state machine driven by events, with running counts beside it. The state is the number of passages paid for and not yet used.
  • A coin while the gate is open: - classic keeps the coin, and the gate stays open for one person: the coin buys nothing; - credit counts it, so one more person may pass; - refund gives it back.
  • On every machine and every sequence, coins paid = people passed + coins lost + passages still owed.
  • Every sequence of 1 to 8 events: classic loses a coin in 369 of the 510, and up to 7 in one; the other two never do. Classic loses a coin exactly when a coin comes while the gate is open.

What is checked: menu choices 1 to 5; one of the three machines; events of c and p, in either case, with or without spaces, up to 20. An empty answer cancels.

Sessions: sessions/basic.in gives: - the machines; - c p p c c p p on classic (a coin lost, a push refused) and on credit (all three pass); - c c c p p p p and p c p c p side by side; - every sequence.

sessions/bad-input.in gives: - menu choices 0 and x; - a cancel and turbo for the machine; - events: a cancel, c x p, and 21 coins, then CP CP on refund; - a cancel side by side.

Built on the verified corpus case turnstile-simulator (a two-state machine and its counters, driven by a list of events).

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
== Turnstile ==
A coin unlocks the gate and a push lets one person through. Three machines differ only in what a coin does when the gate is already open.

1) machines  2) one machine  3) side by side  4) every sequence  5) quit
choice> 0
Pick a number from 1 to 5.

1) machines  2) one machine  3) side by side  4) every sequence  5) quit
choice> x
Pick a number from 1 to 5.

1) machines  2) one machine  3) side by side  4) every sequence  5) quit
choice> 2
machine (classic, credit or refund)> 
Cancelled.

1) machines  2) one machine  3) side by side  4) every sequence  5) quit
choice> 2
machine (classic, credit or refund)> turbo
Type classic, credit or refund.

1) machines  2) one machine  3) side by side  4) every sequence  5) quit
choice> 2
machine (classic, credit or refund)> classic
Events: c for a coin, p for a push, up to 20, such as c p p c c p.
events> 
Cancelled.

1) machines  2) one machine  3) side by side  4) every sequence  5) quit
choice> 2
machine (classic, credit or refund)> classic
Events: c for a coin, p for a push, up to 20, such as c p p c c p.
events> c x p
Type only c, p and spaces.

1) machines  2) one machine  3) side by side  4) every sequence  5) quit
choice> 2
machine (classic, credit or refund)> classic
Events: c for a coin, p for a push, up to 20, such as c p p c c p.
events> c c c c c c c c c c c c c c c c c c c c c
Up to 20 events.

1) machines  2) one machine  3) side by side  4) every sequence  5) quit
choice> 2
machine (classic, credit or refund)> refund
Events: c for a coin, p for a push, up to 20, such as c p p c c p.
events> CP CP
refund:
   1  coin locked -> open        coin taken
   2  push open -> locked        one person passes
   3  coin locked -> open        coin taken
   4  push open -> locked        one person passes
  paid 2, passed 2, refused 0, given back 0, lost 0, still owed 0.

1) machines  2) one machine  3) side by side  4) every sequence  5) quit
choice> 3
Events: c for a coin, p for a push, up to 20, such as c p p c c p.
events> 
Cancelled.

1) machines  2) one machine  3) side by side  4) every sequence  5) quit
choice> 5
Bye.
What was typed (21 lines)
0
x
2

2
turbo
2
classic

2
classic
c x p
2
classic
c c c c c c c c c c c c c c c c c c c c c
2
refund
CP CP
3

5

basic

interpreter: byte-equal
== Turnstile ==
A coin unlocks the gate and a push lets one person through. Three machines differ only in what a coin does when the gate is already open.

1) machines  2) one machine  3) side by side  4) every sequence  5) quit
choice> 1
Every machine: a coin opens a locked gate; a push through an open gate lets one person pass and locks it once nothing more is paid; a push on a locked gate is refused.
A coin while the gate is already open:
  classic  keeps the coin, and the gate stays open for one person
  credit   counts it: one more person may pass
  refund   gives it back

1) machines  2) one machine  3) side by side  4) every sequence  5) quit
choice> 2
machine (classic, credit or refund)> classic
Events: c for a coin, p for a push, up to 20, such as c p p c c p.
events> c p p c c p p
classic:
   1  coin locked -> open        coin taken
   2  push open -> locked        one person passes
   3  push locked                refused: pay first
   4  coin locked -> open        coin taken
   5  coin open                  coin kept, but the gate was already open: it buys nothing
   6  push open -> locked        one person passes
   7  push locked                refused: pay first
  paid 3, passed 2, refused 2, given back 0, lost 1, still owed 0.

1) machines  2) one machine  3) side by side  4) every sequence  5) quit
choice> 2
machine (classic, credit or refund)> credit
Events: c for a coin, p for a push, up to 20, such as c p p c c p.
events> c p p c c p p
credit:
   1  coin locked -> open        coin taken
   2  push open -> locked        one person passes
   3  push locked                refused: pay first
   4  coin locked -> open        coin taken
   5  coin open -> open, 2 paid  coin taken
   6  push open, 2 paid -> open  one person passes
   7  push open -> locked        one person passes
  paid 3, passed 3, refused 1, given back 0, lost 0, still owed 0.

1) machines  2) one machine  3) side by side  4) every sequence  5) quit
choice> 3
Events: c for a coin, p for a push, up to 20, such as c p p c c p.
events> c c c p p p p
  classic paid 3, passed 1, refused 3, given back 0, lost 2, still owed 0
  credit  paid 3, passed 3, refused 1, given back 0, lost 0, still owed 0
  refund  paid 1, passed 1, refused 3, given back 2, lost 0, still owed 0
Coins paid = people passed + coins lost + passages still owed, on every machine.

1) machines  2) one machine  3) side by side  4) every sequence  5) quit
choice> 3
Events: c for a coin, p for a push, up to 20, such as c p p c c p.
events> p c p c p
  classic paid 2, passed 2, refused 1, given back 0, lost 0, still owed 0
  credit  paid 2, passed 2, refused 1, given back 0, lost 0, still owed 0
  refund  paid 2, passed 2, refused 1, given back 0, lost 0, still owed 0
Coins paid = people passed + coins lost + passages still owed, on every machine.

1) machines  2) one machine  3) side by side  4) every sequence  5) quit
choice> 4
Every sequence of 1 to 8 events, 510 in all:
  classic loses a coin in 369 of the 510, and up to 7 coins in one.
  credit  never loses a coin.
  refund  never loses a coin.
Only classic loses coins, and exactly when a coin comes while the gate is open.

1) machines  2) one machine  3) side by side  4) every sequence  5) quit
choice> 5
Bye.
What was typed (13 lines)
1
2
classic
c p p c c p p
2
credit
c p p c c p p
3
c c c p p p p
3
p c p c p
4
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
# P122 turnstile: three machines, a list of coins and pushes run through
# one of them step by step or through all three side by side, and every
# short sequence of events counted.
import gate

def plain(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 pad(s, n):
    s => out
    while len(out) < n:
        out + " " => out
    return out

def lpad(s, n):
    s => out
    while len(out) < n:
        " " + out => out
    return out

def state_text(owed):
    if owed == 0:
        return "locked"
    if owed == 1:
        return "open"
    return "open, " + str(owed) + " paid"

def show():
    "Every machine: a coin opens a locked gate; a push through an open gate lets one person pass and locks it once nothing more is paid; a push on a locked gate is refused." ^0
    "A coin while the gate is already open:" ^0
    "  classic  keeps the coin, and the gate stays open for one person" ^0
    "  credit   counts it: one more person may pass" ^0
    "  refund   gives it back" ^0

def ask_events():
    ("Events: c for a coin, p for a push, up to 20, such as c p p c c p.") ^0
    plain(input("events> ")) => t
    if t == "":
        "Cancelled." ^0
        return []
    [] => out
    for c in t:
        if c == "c" or c == "C":
            out + ["coin"] => out
        elif c == "p" or c == "P":
            out + ["push"] => out
        elif c != " ":
            "Type only c, p and spaces." ^0
            return []
    if len(out) > 20:
        "Up to 20 events." ^0
        return []
    return out

def what_text(w):
    if w == "taken":
        return "coin taken"
    if w == "refunded":
        return "coin given back: the gate is already open"
    if w == "passes":
        return "one person passes"
    if w == "refused":
        return "refused: pay first"
    return "coin kept, but the gate was already open: it buys nothing"

def totals_text(t):
    return "paid " + str(t["paid"]) + ", passed " + str(t["passes"]) + ", refused " + str(t["refused"]) + ", given back " + str(t["refunded"]) + ", lost " + str(t["lost"]) + ", still owed " + str(t["owed"])

def ask_machine():
    plain(input("machine (classic, credit or refund)> ")) => m
    if m == "":
        "Cancelled." ^0
        return ""
    for x in gate.machines():
        if x == m:
            return m
    "Type classic, credit or refund." ^0
    return ""

def one(machine):
    ask_events() => evs
    if len(evs) == 0:
        return
    gate.run(machine, evs) => r
    (machine + ":") ^0
    for k in [0:len(evs) - 1]:
        r[0][k] => e
        state_text(e[1]) => a
        state_text(e[2]) => b
        if a == b:
            ("  " + lpad(str(k + 1), 2) + "  " + pad(e[0], 5) + pad(a, 22) + what_text(e[3])) ^0
        else:
            ("  " + lpad(str(k + 1), 2) + "  " + pad(e[0], 5) + pad(a + " -> " + b, 22) + what_text(e[3])) ^0
    ("  " + totals_text(r[1]) + ".") ^0

def side():
    ask_events() => evs
    if len(evs) == 0:
        return
    for m in gate.machines():
        ("  " + pad(m, 8) + totals_text(gate.run(m, evs)[1])) ^0
    "Coins paid = people passed + coins lost + passages still owed, on every machine." ^0

def every():
    "Every sequence of 1 to 8 events, 510 in all:" ^0
    for m in gate.machines():
        gate.census(m, 8) => c
        if c[1] == 0:
            ("  " + pad(m, 8) + "never loses a coin.") ^0
        else:
            ("  " + pad(m, 8) + "loses a coin in " + str(c[1]) + " of the " + str(c[0]) + ", and up to " + str(c[2]) + " coins in one.") ^0
    "Only classic loses coins, and exactly when a coin comes while the gate is open." ^0

"== Turnstile ==" ^0
"A coin unlocks the gate and a push lets one person through. Three machines differ only in what a coin does when the gate is already open." ^0
True => running
while running:
    "" ^0
    "1) machines  2) one machine  3) side by side  4) every sequence  5) quit" ^0
    plain(input("choice> ")) => choice
    if choice == "1":
        show()
    elif choice == "2":
        ask_machine() => m
        if m != "":
            one(m)
    elif choice == "3":
        side()
    elif choice == "4":
        every()
    elif choice == "5":
        False => running
    else:
        "Pick a number from 1 to 5." ^0
"Bye." ^0
Python projection (main.py)
import gate

def plain(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 pad(s, n):
    out = s
    while len(out) < n:
        out = out + " "
    return out

def lpad(s, n):
    out = s
    while len(out) < n:
        out = " " + out
    return out

def state_text(owed):
    if owed == 0:
        return "locked"
    if owed == 1:
        return "open"
    return "open, " + str(owed) + " paid"

def show():
    print("Every machine: a coin opens a locked gate; a push through an open gate lets one person pass and locks it once nothing more is paid; a push on a locked gate is refused.")
    print("A coin while the gate is already open:")
    print("  classic  keeps the coin, and the gate stays open for one person")
    print("  credit   counts it: one more person may pass")
    print("  refund   gives it back")

def ask_events():
    print("Events: c for a coin, p for a push, up to 20, such as c p p c c p.")
    t = plain(input("events> "))
    if t == "":
        print("Cancelled.")
        return []
    out = []
    for c in t:
        if c == "c" or c == "C":
            out = out + ["coin"]
        elif c == "p" or c == "P":
            out = out + ["push"]
        elif c != " ":
            print("Type only c, p and spaces.")
            return []
    if len(out) > 20:
        print("Up to 20 events.")
        return []
    return out

def what_text(w):
    if w == "taken":
        return "coin taken"
    if w == "refunded":
        return "coin given back: the gate is already open"
    if w == "passes":
        return "one person passes"
    if w == "refused":
        return "refused: pay first"
    return "coin kept, but the gate was already open: it buys nothing"

def totals_text(t):
    return "paid " + str(t["paid"]) + ", passed " + str(t["passes"]) + ", refused " + str(t["refused"]) + ", given back " + str(t["refunded"]) + ", lost " + str(t["lost"]) + ", still owed " + str(t["owed"])

def ask_machine():
    m = plain(input("machine (classic, credit or refund)> "))
    if m == "":
        print("Cancelled.")
        return ""
    for x in gate.machines():
        if x == m:
            return m
    print("Type classic, credit or refund.")
    return ""

def one(machine):
    evs = ask_events()
    if len(evs) == 0:
        return
    r = gate.run(machine, evs)
    print(machine + ":")
    for k in range(0, len(evs)):
        e = r[0][k]
        a = state_text(e[1])
        b = state_text(e[2])
        if a == b:
            print("  " + lpad(str(k + 1), 2) + "  " + pad(e[0], 5) + pad(a, 22) + what_text(e[3]))
        else:
            print("  " + lpad(str(k + 1), 2) + "  " + pad(e[0], 5) + pad(a + " -> " + b, 22) + what_text(e[3]))
    print("  " + totals_text(r[1]) + ".")

def side():
    evs = ask_events()
    if len(evs) == 0:
        return
    for m in gate.machines():
        print("  " + pad(m, 8) + totals_text(gate.run(m, evs)[1]))
    print("Coins paid = people passed + coins lost + passages still owed, on every machine.")

def every():
    print("Every sequence of 1 to 8 events, 510 in all:")
    for m in gate.machines():
        c = gate.census(m, 8)
        if c[1] == 0:
            print("  " + pad(m, 8) + "never loses a coin.")
        else:
            print("  " + pad(m, 8) + "loses a coin in " + str(c[1]) + " of the " + str(c[0]) + ", and up to " + str(c[2]) + " coins in one.")
    print("Only classic loses coins, and exactly when a coin comes while the gate is open.")

print("== Turnstile ==")
print("A coin unlocks the gate and a push lets one person through. Three machines differ only in what a coin does when the gate is already open.")
running = True
while running:
    print("")
    print("1) machines  2) one machine  3) side by side  4) every sequence  5) quit")
    choice = plain(input("choice> "))
    if choice == "1":
        show()
    elif choice == "2":
        m = ask_machine()
        if m != "":
            one(m)
    elif choice == "3":
        side()
    elif choice == "4":
        every()
    elif choice == "5":
        running = False
    else:
        print("Pick a number from 1 to 5.")
print("Bye.")

gate.eml

eml
# P122 turnstile - a coin unlocks it, a push lets one person through and
# locks it again; a push while locked is refused. As in the corpus case
# turnstile-simulator, a two-state machine driven by events. Three machines
# differ only in a coin that arrives while the gate is already open:
#   classic  keeps the coin and stays open: one passage for two coins;
#   credit   counts it: each coin is one passage, however they arrive;
#   refund   gives it back.
# The state is the number of passages paid for and not yet used (classic
# and refund never hold more than one).

def machines():
    return ["classic", "credit", "refund"]

def step(machine, owed, ev):
    # [passages owed after, what happened]: what is "taken", "refunded",
    # "passes", "refused" or "lost" (a coin taken that buys nothing)
    if ev == "coin":
        if owed == 0 or machine == "credit":
            return [owed + 1, "taken"]
        if machine == "refund":
            return [owed, "refunded"]
        return [owed, "lost"]
    if owed > 0:
        return [owed - 1, "passes"]
    return [owed, "refused"]

def run(machine, events):
    # [trace of [event, owed before, owed after, what], totals {name: n}]
    0 => owed
    [] => trace
    {"taken": 0, "refunded": 0, "passes": 0, "refused": 0, "lost": 0} => tot
    for ev in events:
        step(machine, owed, ev) => r
        trace + [[ev, owed, r[0], r[1]]] => trace
        r[0] => owed
        tot[r[1]] + 1 => tot[r[1]]
    tot["taken"] + tot["lost"] => tot["paid"]
    owed => tot["owed"]
    return [trace, tot]

def sequences(n):
    # every sequence of n events
    [[]] => out
    for k in [1:n]:
        [] => more
        for s in out:
            more + [s + ["coin"]] => more
            more + [s + ["push"]] => more
        more => out
    return out

def census(machine, n):
    # over every sequence of 1 to n events: [sequences, sequences losing a
    # coin, most coins lost in one, most passages refused in one]
    0 => count
    0 => losing
    0 => most
    0 => refused
    for k in [1:n]:
        for s in sequences(k):
            run(machine, s)[1] => t
            count + 1 => count
            if t["lost"] > 0:
                losing + 1 => losing
            if t["lost"] > most:
                t["lost"] => most
            if t["refused"] > refused:
                t["refused"] => refused
    return [count, losing, most, refused]
Python projection (gate.py)
def machines():
    return ["classic", "credit", "refund"]

def step(machine, owed, ev):
    if ev == "coin":
        if owed == 0 or machine == "credit":
            return [owed + 1, "taken"]
        if machine == "refund":
            return [owed, "refunded"]
        return [owed, "lost"]
    if owed > 0:
        return [owed - 1, "passes"]
    return [owed, "refused"]

def run(machine, events):
    owed = 0
    trace = []
    tot = {"taken": 0, "refunded": 0, "passes": 0, "refused": 0, "lost": 0}
    for ev in events:
        r = step(machine, owed, ev)
        trace = trace + [[ev, owed, r[0], r[1]]]
        owed = r[0]
        tot[r[1]] = tot[r[1]] + 1
    tot["paid"] = tot["taken"] + tot["lost"]
    tot["owed"] = owed
    return [trace, tot]

def sequences(n):
    out = [[]]
    for k in range(1, n+1):
        more = []
        for s in out:
            more = more + [s + ["coin"]]
            more = more + [s + ["push"]]
        out = more
    return out

def census(machine, n):
    count = 0
    losing = 0
    most = 0
    refused = 0
    for k in range(1, n+1):
        for s in sequences(k):
            t = run(machine, s)[1]
            count = count + 1
            if t["lost"] > 0:
                losing = losing + 1
            if t["lost"] > most:
                most = t["lost"]
            if t["refused"] > refused:
                refused = t["refused"]
    return [count, losing, most, refused]

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.