Project P089

Fixed-width report

Payroll lines in fixed columns, shown under a ruler: every line's length checked before its columns are trusted, every error reported at once (and what stopping at the first would have said), and pay added up by department in whole cents.

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

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

Payroll lines in fixed columns, shown under a column ruler. Each line's length is checked before its columns are trusted, every error is reported at once - with what stopping at the first error would have said - and pay is added up by department in whole cents. It starts with nine lines, five of them bad.

  • main.eml - the menu, the ruler, the checks, the report, adding and removing lines
  • records.eml - the columns, the field checks, the check of all lines, the first error, and pay

How each part works:

  • A line is exactly 42 characters: id in columns 0-6 (six digits), name 6-26 (letters, spaces, - ' and .), department 26-30 (four capital letters), hours 30-35 (like 037.5, at most 168.0) and rate 35-42 (like 0025.50, more than 0).
  • As the corpus case fixed-width-record-parser warns, a short line still slices without complaint - into wrong fields. So the length is checked first, and a line of the wrong length is reported on its own; its columns are not read at all.
  • As in the corpus case schema-validator-all-errors, every error of every line is listed, not just the first: one round of fixes instead of one per error. The program also shows the single error that stopping at the first would have reported. A repeated id is an error on the later line.
  • Pay is hours (in tenths) x rate (in cents) / 10, rounded half up to the cent: 42.5 h x 31.75 = 1,349.375, paid as 1,349.38. Only good lines are paid, by department and in all.

What is checked: menu choices 1 to 6; a new line of any text (it is added, with its errors listed); a line number that exists; at most 20 lines. An empty answer cancels.

Sessions: sessions/basic.in gives: - the nine lines under the ruler, and their seven errors on five lines; - the pay report: SALE 1,845.00, TECH 2,466.58, 4,311.58 in all; - a good line added for Jo Kim and paid; the short line removed, leaving six errors on four lines.

sessions/bad-input.in gives: - menu choices 0 and x; - "abc" added (3 characters), and a line with five errors in one go; - line numbers 0, 99 and x to remove, then cancels; - the check of all eleven lines: 13 errors on 7 of them.

Built on the verified corpus cases fixed-width-record-parser (check the line before trusting its slices) and schema-validator-all-errors (every failure at once, against stopping at the first).

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
== Fixed-width report ==
Payroll lines in fixed columns: checked before they are trusted, every error at once.

1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit
choice> 0
Pick a number from 1 to 6.

1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit
choice> x
Pick a number from 1 to 6.

1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit
choice> 4
new line (42 characters, as on the ruler)> abc
Added, with 1 error:
  is 3 characters, not 42 - its columns cannot be trusted

1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit
choice> 4
new line (42 characters, as on the ruler)> 1000aaBad Name 9          Sale999.9abcd.ef
Added, with 5 errors:
  id '1000aa' is not six digits
  name 'Bad Name 9' must start in column 6 and be letters, spaces, - ' or .
  dept 'Sale' is not four capital letters
  hours 999.9 is more than the 168 in a week
  rate 'abcd.ef' is not like 0025.50

1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit
choice> 5
line number to remove> 0
There is no line 0.

1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit
choice> 5
line number to remove> 99
There is no line 99.

1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit
choice> 5
line number to remove> x
There is no line x.

1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit
choice> 5
line number to remove> 
Cancelled.

1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit
choice> 4
new line (42 characters, as on the ruler)> 
Cancelled.

1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit
choice> 2
  line 5: id '10000X' is not six digits
  line 5: dept 'tech' is not four capital letters
  line 6: hours 170.0 is more than the 168 in a week
  line 7: dept 'OPS ' is not four capital letters
  line 7: rate is 0
  line 8: id 100002 is already on line 2
  line 9: is 38 characters, not 42 - its columns cannot be trusted
  line 10: is 3 characters, not 42 - its columns cannot be trusted
  line 11: id '1000aa' is not six digits
  line 11: name 'Bad Name 9' must start in column 6 and be letters, spaces, - ' or .
  line 11: dept 'Sale' is not four capital letters
  line 11: hours 999.9 is more than the 168 in a week
  line 11: rate 'abcd.ef' is not like 0025.50
Checking everything: 13 errors on 7 of the 11 lines.
Stopping at the first would have said only: line 5: id '10000X' is not six digits - and the next run would find the next one.

1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit
choice> 6
Bye.
What was typed (18 lines)
0
x
4
abc
4
1000aaBad Name 9          Sale999.9abcd.ef
5
0
5
99
5
x
5

4

2
6

basic

interpreter: byte-equal
== Fixed-width report ==
Payroll lines in fixed columns: checked before they are trusted, every error at once.

1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit
choice> 1
              1         2         3         4
    012345678901234567890123456789012345678901
 1  100001Ana Lopez           SALE040.00025.50
 2  100002Ben O'Hara          SALE037.50022.00
 3  100003Chloe Dubois        TECH042.50031.75
 4  100004Dev Patel           TECH038.00029.40
 5  10000XEli Moss            tech040.00024.00
 6  100006Fay Lin             SALE170.00025.00
 7  100007Gus Young           OPS 035.00000.00
 8  100002Hana Ito            TECH036.00027.50
 9  100009Ivo Novak           SALE040.0002
    id    name                dept hours rate

1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit
choice> 2
  line 5: id '10000X' is not six digits
  line 5: dept 'tech' is not four capital letters
  line 6: hours 170.0 is more than the 168 in a week
  line 7: dept 'OPS ' is not four capital letters
  line 7: rate is 0
  line 8: id 100002 is already on line 2
  line 9: is 38 characters, not 42 - its columns cannot be trusted
Checking everything: 7 errors on 5 of the 9 lines.
Stopping at the first would have said only: line 5: id '10000X' is not six digits - and the next run would find the next one.

1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit
choice> 3
 1  Ana Lopez, SALE: 040.0 h x 0025.50 = 1,020.00
 2  Ben O'Hara, SALE: 037.5 h x 0022.00 = 825.00
 3  Chloe Dubois, TECH: 042.5 h x 0031.75 = 1,349.38
 4  Dev Patel, TECH: 038.0 h x 0029.40 = 1,117.20
  SALE      1,845.00
  TECH      2,466.58
  all       4,311.58  from 4 good lines; 5 left out until they are fixed

1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit
choice> 4
new line (42 characters, as on the ruler)> 100010Jo Kim              TECH040.00030.00
Added; it is good.

1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit
choice> 3
 1  Ana Lopez, SALE: 040.0 h x 0025.50 = 1,020.00
 2  Ben O'Hara, SALE: 037.5 h x 0022.00 = 825.00
 3  Chloe Dubois, TECH: 042.5 h x 0031.75 = 1,349.38
 4  Dev Patel, TECH: 038.0 h x 0029.40 = 1,117.20
10  Jo Kim, TECH: 040.0 h x 0030.00 = 1,200.00
  SALE      1,845.00
  TECH      3,666.58
  all       5,511.58  from 5 good lines; 5 left out until they are fixed

1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit
choice> 5
line number to remove> 9
Removed line 9.

1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit
choice> 2
  line 5: id '10000X' is not six digits
  line 5: dept 'tech' is not four capital letters
  line 6: hours 170.0 is more than the 168 in a week
  line 7: dept 'OPS ' is not four capital letters
  line 7: rate is 0
  line 8: id 100002 is already on line 2
Checking everything: 6 errors on 4 of the 9 lines.
Stopping at the first would have said only: line 5: id '10000X' is not six digits - and the next run would find the next one.

1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit
choice> 6
Bye.
What was typed (10 lines)
1
2
3
4
100010Jo Kim              TECH040.00030.00
3
5
9
2
6

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
# P089 fixed-width report: payroll lines in fixed columns - shown under a
# column ruler, checked with every error listed (and what stopping at the
# first would have said), and paid out by department in whole cents.
import records

def money(c):
    0 => w
    c => r
    while r >= 100:
        r - 100 => r
        w + 1 => w
    "" => z
    if r < 10:
        "0" => z
    # thousands
    str(w) => s
    "" => g
    0 => k
    len(s) - 1 => i
    while i >= 0:
        if k > 0 and k % 3 == 0:
            "," + g => g
        s[i] + g => g
        k + 1 => k
        i - 1 => i
    return g + "." + z + str(r)

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

def show(lines):
    "    " + "          1         2         3         4" => top
    top ^0
    "    012345678901234567890123456789012345678901" ^0
    for i in [0:len(lines) - 1]:
        (lpad(str(i + 1), 2) + "  " + lines[i]) ^0
    "    id    name                dept hours rate" ^0

def check_all(lines):
    records.check(lines) => all
    0 => bad
    0 => count
    for i in [0:len(all) - 1]:
        if len(all[i]) > 0:
            bad + 1 => bad
            for e in all[i]:
                ("  line " + str(i + 1) + ": " + e) ^0
                count + 1 => count
    if bad == 0:
        ("All " + str(len(lines)) + " lines are good.") ^0
        return
    ("Checking everything: " + str(count) + " errors on " + str(bad) + " of the " + str(len(lines)) + " lines.") ^0
    records.first_error(lines) => f
    ("Stopping at the first would have said only: line " + str(f[0] + 1) + ": " + f[1] + " - and the next run would find the next one.") ^0

def report(lines):
    records.check(lines) => all
    [] => depts
    [] => sums
    0 => total
    0 => used
    for i in [0:len(lines) - 1]:
        if len(all[i]) == 0:
            lines[i] => l
            records.pay(l) => p
            (lpad(str(i + 1), 2) + "  " + records.trim_right(l[6:26]) + ", " + l[26:30] + ": " + l[30:35] + " h x " + l[35:42] + " = " + money(p)) ^0
            -1 => k
            for j in [0:len(depts) - 1]:
                if depts[j] == l[26:30]:
                    j => k
            if k == -1:
                depts + [l[26:30]] => depts
                sums + [p] => sums
            else:
                sums[k] + p => sums[k]
            total + p => total
            used + 1 => used
    if used == 0:
        "No good lines to pay." ^0
        return
    for j in [0:len(depts) - 1]:
        ("  " + depts[j] + "  " + lpad(money(sums[j]), 12)) ^0
    ("  all   " + lpad(money(total), 12) + "  from " + str(used) + " good lines; " + str(len(lines) - used) + " left out until they are fixed") ^0

def add(lines):
    if len(lines) == 20:
        "There are 20 lines already." ^0
        return lines
    input("new line (42 characters, as on the ruler)> ") => s
    if s == "":
        "Cancelled." ^0
        return lines
    records.check(lines + [s]) => all
    all[len(all) - 1] => errs
    if len(errs) == 0:
        "Added; it is good." ^0
    else:
        "errors" => w
        if len(errs) == 1:
            "error" => w
        ("Added, with " + str(len(errs)) + " " + w + ":") ^0
        for e in errs:
            ("  " + e) ^0
    return lines + [s]

def remove(lines):
    input("line number to remove> ") => s
    if s == "":
        "Cancelled." ^0
        return lines
    0 => n
    True => ok
    if len(s) > 2:
        False => ok
    for c in s:
        if c >= "0" and c <= "9":
            n * 10 + int(c) => n
        else:
            False => ok
    if not ok or n < 1 or n > len(lines):
        ("There is no line " + s + ".") ^0
        return lines
    [] => out
    for i in [0:len(lines) - 1]:
        if i != n - 1:
            out + [lines[i]] => out
    ("Removed line " + str(n) + ".") ^0
    return out

[
    "100001Ana Lopez           SALE040.00025.50",
    "100002Ben O'Hara          SALE037.50022.00",
    "100003Chloe Dubois        TECH042.50031.75",
    "100004Dev Patel           TECH038.00029.40",
    "10000XEli Moss            tech040.00024.00",
    "100006Fay Lin             SALE170.00025.00",
    "100007Gus Young           OPS 035.00000.00",
    "100002Hana Ito            TECH036.00027.50",
    "100009Ivo Novak           SALE040.0002",
] => lines

"== Fixed-width report ==" ^0
"Payroll lines in fixed columns: checked before they are trusted, every error at once." ^0
True => running
while running:
    "" ^0
    "1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit" ^0
    input("choice> ") => choice
    if choice == "1":
        show(lines)
    elif choice == "2":
        check_all(lines)
    elif choice == "3":
        report(lines)
    elif choice == "4":
        add(lines) => lines
    elif choice == "5":
        remove(lines) => lines
    elif choice == "6":
        False => running
    else:
        "Pick a number from 1 to 6." ^0
"Bye." ^0
Python projection (main.py)
import records

def money(c):
    w = 0
    r = c
    while r >= 100:
        r = r - 100
        w = w + 1
    z = ""
    if r < 10:
        z = "0"
    s = str(w)
    g = ""
    k = 0
    i = len(s) - 1
    while i >= 0:
        if k > 0 and k % 3 == 0:
            g = "," + g
        g = s[i] + g
        k = k + 1
        i = i - 1
    return g + "." + z + str(r)

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

def show(lines):
    top = "    " + "          1         2         3         4"
    print(top)
    print("    012345678901234567890123456789012345678901")
    for i in range(0, len(lines)):
        print(lpad(str(i + 1), 2) + "  " + lines[i])
    print("    id    name                dept hours rate")

def check_all(lines):
    all = records.check(lines)
    bad = 0
    count = 0
    for i in range(0, len(all)):
        if len(all[i]) > 0:
            bad = bad + 1
            for e in all[i]:
                print("  line " + str(i + 1) + ": " + e)
                count = count + 1
    if bad == 0:
        print("All " + str(len(lines)) + " lines are good.")
        return
    print("Checking everything: " + str(count) + " errors on " + str(bad) + " of the " + str(len(lines)) + " lines.")
    f = records.first_error(lines)
    print("Stopping at the first would have said only: line " + str(f[0] + 1) + ": " + f[1] + " - and the next run would find the next one.")

def report(lines):
    all = records.check(lines)
    depts = []
    sums = []
    total = 0
    used = 0
    for i in range(0, len(lines)):
        if len(all[i]) == 0:
            l = lines[i]
            p = records.pay(l)
            print(lpad(str(i + 1), 2) + "  " + records.trim_right(l[6:26]) + ", " + l[26:30] + ": " + l[30:35] + " h x " + l[35:42] + " = " + money(p))
            k = -1
            for j in range(0, len(depts)):
                if depts[j] == l[26:30]:
                    k = j
            if k == -1:
                depts = depts + [l[26:30]]
                sums = sums + [p]
            else:
                sums[k] = sums[k] + p
            total = total + p
            used = used + 1
    if used == 0:
        print("No good lines to pay.")
        return
    for j in range(0, len(depts)):
        print("  " + depts[j] + "  " + lpad(money(sums[j]), 12))
    print("  all   " + lpad(money(total), 12) + "  from " + str(used) + " good lines; " + str(len(lines) - used) + " left out until they are fixed")

def add(lines):
    if len(lines) == 20:
        print("There are 20 lines already.")
        return lines
    s = input("new line (42 characters, as on the ruler)> ")
    if s == "":
        print("Cancelled.")
        return lines
    all = records.check(lines + [s])
    errs = all[len(all) - 1]
    if len(errs) == 0:
        print("Added; it is good.")
    else:
        w = "errors"
        if len(errs) == 1:
            w = "error"
        print("Added, with " + str(len(errs)) + " " + w + ":")
        for e in errs:
            print("  " + e)
    return lines + [s]

def remove(lines):
    s = input("line number to remove> ")
    if s == "":
        print("Cancelled.")
        return lines
    n = 0
    ok = True
    if len(s) > 2:
        ok = False
    for c in s:
        if c >= "0" and c <= "9":
            n = n * 10 + int(c)
        else:
            ok = False
    if not ok or n < 1 or n > len(lines):
        print("There is no line " + s + ".")
        return lines
    out = []
    for i in range(0, len(lines)):
        if i != n - 1:
            out = out + [lines[i]]
    print("Removed line " + str(n) + ".")
    return out

lines = ["100001Ana Lopez           SALE040.00025.50", "100002Ben O'Hara          SALE037.50022.00", "100003Chloe Dubois        TECH042.50031.75", "100004Dev Patel           TECH038.00029.40", "10000XEli Moss            tech040.00024.00", "100006Fay Lin             SALE170.00025.00", "100007Gus Young           OPS 035.00000.00", "100002Hana Ito            TECH036.00027.50", "100009Ivo Novak           SALE040.0002"]
print("== Fixed-width report ==")
print("Payroll lines in fixed columns: checked before they are trusted, every error at once.")
running = True
while running:
    print("")
    print("1) lines  2) check  3) pay report  4) add a line  5) remove a line  6) quit")
    choice = input("choice> ")
    if choice == "1":
        show(lines)
    elif choice == "2":
        check_all(lines)
    elif choice == "3":
        report(lines)
    elif choice == "4":
        lines = add(lines)
    elif choice == "5":
        lines = remove(lines)
    elif choice == "6":
        running = False
    else:
        print("Pick a number from 1 to 6.")
print("Bye.")

records.eml

eml
# P089 fixed-width report - payroll lines in fixed columns, checked before
# they are trusted, every error reported at once, and pay added up in whole
# cents. The columns (start, end, end not included):
#     id     0-6    six digits
#     name   6-26   letters, spaces, - ' and .
#     dept   26-30  four capital letters
#     hours  30-35  like 037.5 (tenths of an hour, at most 168.0)
#     rate   35-42  like 0025.50 (per hour, more than 0)
# A line is exactly 42 characters.

42 => WIDTH

def is_digits(s):
    if len(s) == 0:
        return False
    for c in s:
        if not (c >= "0" and c <= "9"):
            return False
    return True

def value(s):
    0 => n
    for c in s:
        n * 10 + int(c) => n
    return n

def trim_right(s):
    len(s) => j
    while j > 0 and s[j - 1] == " ":
        j - 1 => j
    return s[0:j]

def field_errors(line):
    # Every problem with a line of the right length, as text.
    [] => errs
    line[0:6] => ident
    line[6:26] => name
    line[26:30] => dept
    line[30:35] => hours
    line[35:42] => rate
    if not is_digits(ident):
        errs + ["id '" + ident + "' is not six digits"] => errs
    trim_right(name) => nm
    True => ok_name
    if nm == "" or name[0] == " ":
        False => ok_name
    for c in nm:
        if not ((c >= "a" and c <= "z") or (c >= "A" and c <= "Z") or c == " " or c == "-" or c == "'" or c == "."):
            False => ok_name
    if not ok_name:
        errs + ["name '" + nm + "' must start in column 6 and be letters, spaces, - ' or ."] => errs
    True => ok_dept
    for c in dept:
        if not (c >= "A" and c <= "Z"):
            False => ok_dept
    if not ok_dept:
        errs + ["dept '" + dept + "' is not four capital letters"] => errs
    if not (is_digits(hours[0:3]) and hours[3] == "." and is_digits(hours[4:5])):
        errs + ["hours '" + hours + "' is not like 037.5"] => errs
    elif value(hours[0:3]) * 10 + value(hours[4:5]) > 1680:
        errs + ["hours " + hours + " is more than the 168 in a week"] => errs
    if not (is_digits(rate[0:4]) and rate[4] == "." and is_digits(rate[5:7])):
        errs + ["rate '" + rate + "' is not like 0025.50"] => errs
    elif value(rate[0:4]) * 100 + value(rate[5:7]) == 0:
        errs + ["rate is 0"] => errs
    return errs

def check(lines):
    # Fail-complete, as in the corpus case schema-validator-all-errors:
    # every error of every line. A line of the wrong length is reported
    # alone - its slices would not mean anything (the corpus case
    # fixed-width-record-parser). A repeated id is an error on the later
    # line. Returns, per line, its list of errors.
    [] => out
    [] => ids
    for i in [0:len(lines) - 1]:
        lines[i] => line
        if len(line) != WIDTH:
            out + [["is " + str(len(line)) + " characters, not " + str(WIDTH) + " - its columns cannot be trusted"]] => out
        else:
            field_errors(line) => errs
            for k in [0:len(ids) - 1]:
                if ids[k][0] == line[0:6] and len(errs) == 0:
                    errs + ["id " + line[0:6] + " is already on line " + str(ids[k][1] + 1)] => errs
            if len(errs) == 0:
                ids + [[line[0:6], i]] => ids
            out + [errs] => out
    return out

def first_error(lines):
    # Fail-fast: the first error found, as [line, error], or [] if none.
    check(lines) => all
    for i in [0:len(all) - 1]:
        if len(all[i]) > 0:
            return [i, all[i][0]]
    return []

def pay(line):
    # hours (tenths) x rate (cents) / 10, rounded half up to the cent.
    value(line[30:33]) * 10 + value(line[34:35]) => tenths
    value(line[35:39]) * 100 + value(line[40:42]) => cents
    tenths * cents => t
    0 => q
    t * 2 + 10 => n
    while n >= 20:
        20 => m
        1 => k
        while m + m <= n:
            m + m => m
            k + k => k
        n - m => n
        q + k => q
    return q
Python projection (records.py)
WIDTH = 42

def is_digits(s):
    if len(s) == 0:
        return False
    for c in s:
        if not (c >= "0" and c <= "9"):
            return False
    return True

def value(s):
    n = 0
    for c in s:
        n = n * 10 + int(c)
    return n

def trim_right(s):
    j = len(s)
    while j > 0 and s[j - 1] == " ":
        j = j - 1
    return s[0:j]

def field_errors(line):
    errs = []
    ident = line[0:6]
    name = line[6:26]
    dept = line[26:30]
    hours = line[30:35]
    rate = line[35:42]
    if not is_digits(ident):
        errs = errs + ["id '" + ident + "' is not six digits"]
    nm = trim_right(name)
    ok_name = True
    if nm == "" or name[0] == " ":
        ok_name = False
    for c in nm:
        if not (c >= "a" and c <= "z" or c >= "A" and c <= "Z" or c == " " or c == "-" or c == "'" or c == "."):
            ok_name = False
    if not ok_name:
        errs = errs + ["name '" + nm + "' must start in column 6 and be letters, spaces, - ' or ."]
    ok_dept = True
    for c in dept:
        if not (c >= "A" and c <= "Z"):
            ok_dept = False
    if not ok_dept:
        errs = errs + ["dept '" + dept + "' is not four capital letters"]
    if not (is_digits(hours[0:3]) and hours[3] == "." and is_digits(hours[4:5])):
        errs = errs + ["hours '" + hours + "' is not like 037.5"]
    elif value(hours[0:3]) * 10 + value(hours[4:5]) > 1680:
        errs = errs + ["hours " + hours + " is more than the 168 in a week"]
    if not (is_digits(rate[0:4]) and rate[4] == "." and is_digits(rate[5:7])):
        errs = errs + ["rate '" + rate + "' is not like 0025.50"]
    elif value(rate[0:4]) * 100 + value(rate[5:7]) == 0:
        errs = errs + ["rate is 0"]
    return errs

def check(lines):
    out = []
    ids = []
    for i in range(0, len(lines)):
        line = lines[i]
        if len(line) != WIDTH:
            out = out + [["is " + str(len(line)) + " characters, not " + str(WIDTH) + " - its columns cannot be trusted"]]
        else:
            errs = field_errors(line)
            for k in range(0, len(ids)):
                if ids[k][0] == line[0:6] and len(errs) == 0:
                    errs = errs + ["id " + line[0:6] + " is already on line " + str(ids[k][1] + 1)]
            if len(errs) == 0:
                ids = ids + [[line[0:6], i]]
            out = out + [errs]
    return out

def first_error(lines):
    all = check(lines)
    for i in range(0, len(all)):
        if len(all[i]) > 0:
            return [i, all[i][0]]
    return []

def pay(line):
    tenths = value(line[30:33]) * 10 + value(line[34:35])
    cents = value(line[35:39]) * 100 + value(line[40:42])
    t = tenths * cents
    q = 0
    n = t * 2 + 10
    while n >= 20:
        m = 20
        k = 1
        while m + m <= n:
            m = m + m
            k = k + k
        n = n - m
        q = q + k
    return q

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.