Skip to content
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
7 changes: 7 additions & 0 deletions lib/maker/make-pathway-tocs.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ for _,course in ipairs(all_courses) do
local this_course_lessons_file = course_dir .. '.cached/.workbook-lessons.rkt.kp'
if not file_exists_p(this_course_lessons_file) then goto continue end
local lesson_units = sread_file(this_course_lessons_file)
-- An empty or malformed .kp makes sread_file return a non-table (false on
-- empty/exhausted input). Skip the course rather than indexing into it.
if type(lesson_units) ~= 'table' then
io.stderr:write('WARNING: skipping pathway-toc for ' .. course ..
' -- empty/unparseable ' .. this_course_lessons_file .. '\n')
goto continue
end
o:write(' \"' .. course .. '\": [\n')
for _,lunit in ipairs(lesson_units) do
local unit_name = lunit[1]
Expand Down
63 changes: 37 additions & 26 deletions lib/maker/make-workbook-lessons-list.lua
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
#! /usr/bin/env lua

-- last modified 2026-03-10
-- last modified 2026-06-15

local inf, outf, pl = ...

local i = io.open(inf, 'r')
local o = io.open(outf, 'w')

local current_unit = 'NO_UNIT'
local current_unit_lessons = {}

local function write_unit()
local function write_unit(o)
if #current_unit_lessons > 0 then
o:write('( "' .. current_unit .. '"\n')
for _,y in ipairs(current_unit_lessons) do
Expand All @@ -26,28 +23,42 @@ if pl ~= 'pyret' and pl ~= 'none' then
proglang = '-' .. pl
end

o:write('(\n')

for x0 in i:lines() do
local x = x0
x = x:gsub('^%s+', '')
x = x:gsub(';.*', '')
x = x:gsub('%s+$', '')
if x == '' then goto continue end
if x:find('^%[') then
x = x:gsub('%[%s*(.-)%s*%]', '%1')
write_unit()
current_unit = x
current_unit_lessons = {}
else
x = x .. proglang
table.insert(current_unit_lessons, x)
local function get_all_lines(inf)
local the_lines = {}
for L in io.lines(inf) do
table.insert(the_lines, L)
end
::continue::
return the_lines
end
write_unit()

o:write(')\n')
local function write_all_units(inf, outf)
local the_lines = get_all_lines(inf)
local o = io.open(outf, 'w')

o:write('(\n')

for _,x0 in ipairs(the_lines) do
local x = x0
x = x:gsub('^%s+', '')
x = x:gsub(';.*', '')
x = x:gsub('%s+$', '')
if x == '' then goto continue end
if x:find('^%[') then
x = x:gsub('%[%s*(.-)%s*%]', '%1')
write_unit(o)
current_unit = x
current_unit_lessons = {}
else
x = x .. proglang
table.insert(current_unit_lessons, x)
end
::continue::
end
write_unit(o)

o:write(')\n')

o:close()
end

i:close()
o:close()
write_all_units(inf, outf)
14 changes: 12 additions & 2 deletions lib/maker/massage-course.sh
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,22 @@ for pl in $proglangs; do
fi
done

# Publish .workbook-lessons.rkt.kp atomically. make-workbook-lessons-list.lua
# opens its output with 'w' (truncates to 0 immediately), so a crash or
# interrupt would leave a 0-byte file that a later build's reader chokes on.
# Write a temp and mv it into place only on success; never leave it empty.
kp=.cached/.workbook-lessons.rkt.kp
if test ! -f lesson-order.txt; then
echo WARNING: No lesson-order.txt in pathway $targetpathway
touch lesson-order.txt
touch .cached/.workbook-lessons.rkt.kp
printf '()\n' > "$kp" # valid empty s-expression, not a 0-byte file
else
$TOPDIR/${MAKE_DIR}make-workbook-lessons-list.lua lesson-order.txt .cached/.workbook-lessons.rkt.kp $pl
if $TOPDIR/${MAKE_DIR}make-workbook-lessons-list.lua lesson-order.txt "$kp.tmp" $pl; then
mv -f "$kp.tmp" "$kp"
else
echo "ERROR: make-workbook-lessons-list failed for $targetpathway; keeping previous $kp" >&2
rm -f "$kp.tmp"
fi
fi

cd ..
Expand Down
18 changes: 12 additions & 6 deletions lib/maker/sread.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
local function sread_line(i)
while true do
local c = i:read(1)
if c == '\r' or c == '\n' then
if c == nil or c == '\r' or c == '\n' then -- nil == EOF
break
end
end
Expand All @@ -18,7 +18,9 @@ end
local function sread_block_comment(i)
while true do
local c = i:read(1)
if c == '|' then
if c == nil then -- EOF inside an unterminated block comment
break
elseif c == '|' then
c = i:read(1)
if c == '#' then
break
Expand Down Expand Up @@ -66,8 +68,8 @@ local function sread_atom(i)
in_escape_p = true
i:read(1)
table.insert(result, c)
elseif c == ' ' or c == '\t' or c == '\n' or c == '\r' or c == '(' or c == '[' or c == ')' or c == ']' or c == ';' then
break
elseif c == nil or c == ' ' or c == '\t' or c == '\n' or c == '\r' or c == '(' or c == '[' or c == ')' or c == ']' or c == ';' then
break -- nil == EOF terminates the atom
else
i:read(1)
table.insert(result, c)
Expand Down Expand Up @@ -97,7 +99,9 @@ local function sread_string(i)
local in_escape_p = false
while true do
local c = i:read(1)
if in_escape_p then
if c == nil then -- EOF inside an unterminated string
break
elseif in_escape_p then
in_escape_p = false
table.insert(result, c)
elseif c == '\\' then
Expand All @@ -116,7 +120,9 @@ function sread(i)
sread_ignorespaces(i)
local c = buf_peek_char(i)
local result = false
if c == '(' or c == '[' then
if c == nil then -- EOF: no s-expression to read (empty/exhausted input)
result = false
elseif c == '(' or c == '[' then
i:read(1)
result = sread_list(i)
elseif c == ')' or c == ']' then
Expand Down