Learn Lua in Neovim: Comments & User Input — io.read() & tonumber() | Episode 6
Video: Learn Lua in Neovim: Comments & User Input — io.read() & tonumber() | Episode 6 by Taught by Celeste AI - AI Coding Coach
Lua Comments and User Input: io.read() and tonumber()
--for single-line comments,--[[ ... ]]for multi-line.io.read()reads a line from stdin (always as a string).tonumber()to convert the string to a number.
Today's episode adds two skills: documenting your code with comments, and making your code interactive by reading input from the user.
Single-line comments
-- This is a single-line comment
print("Hello!") -- comments can also be at the end of a line
Two dashes start a comment. Everything from -- to the end of the line is ignored by Lua.
Multi-line comments
--[[
This is a multi-line comment.
It can span several lines.
Useful for longer explanations.
]]
--[[ ... ]] is the multi-line variant. Note the trick: --[[ is just -- (start a single-line comment) followed by [[ (start a long string). The whole long string becomes the comment's content. The closing ]] ends the string and the -- of the line above keeps it commented out.
A handy idiom for "comment out a block during debugging":
--[[
print("this code is disabled")
print("for now")
--]]
To turn it back on, just add a - to the opener:
---[[
print("now this runs")
print("yes really")
--]]
The first line is now ---[[ — three dashes — which is just a comment (no long string), so the rest of the lines are real Lua again. Switch between commented and uncommented with one keystroke.
Reading input with io.read()
print("What is your name?")
local name = io.read()
print("Hello, " .. name .. "!")
io.read() blocks until the user types a line and presses Enter. The line (without the trailing newline) is returned as a string.
Run this and you see:
What is your name?
Alice
Hello, Alice!
The user types "Alice", presses Enter, and the program continues with name = "Alice".
Reading numbers
print("What year were you born?")
local year = tonumber(io.read())
local age = 2026 - year
print("You are about " .. age .. " years old")
io.read() always returns a string. To do math with it, convert with tonumber():
local raw = io.read() -- "1990" (a string)
local year = tonumber(raw) -- 1990 (a number)
If the input isn't a valid number, tonumber() returns nil. Always validate:
local year = tonumber(io.read())
if year == nil then
print("That wasn't a number!")
else
print("Year: " .. year)
end
io.read modes
io.read() -- one line (default)
io.read("*l") -- one line (explicit, same as default)
io.read("*n") -- one number (auto-converts)
io.read("*a") -- entire input until EOF
io.read(5) -- 5 characters
For reading numbers, io.read("*n") skips the explicit tonumber step:
print("What year were you born?")
local year = io.read("*n")
local age = 2026 - year
But io.read("*n") returns nil on invalid input, same as tonumber, so you still need to validate.
In Lua 5.4, the modes drop the * (so io.read("l") instead of io.read("*l")). Both still work for compatibility.
Reading multiple values per line
print("Enter two numbers:")
local a, b = io.read("*n", "*n")
print("Sum: " .. (a + b))
io.read accepts multiple format args and returns multiple values. The user types 5 10 (separated by whitespace), and a becomes 5, b becomes 10.
Closing the loop: a small program
print("=== Birthday Calculator ===")
print("What is your name?")
local name = io.read()
print("What year were you born?")
local year = tonumber(io.read())
if year == nil then
print("Sorry, " .. name .. ", that wasn't a number.")
else
local age = 2026 - year
print(name .. ", you are about " .. age .. " years old.")
end
Two io.read calls, one tonumber conversion, one validation, one branching message. Small but real.
Common stumbles
Forgetting tonumber(). 2026 - io.read() errors at runtime — can't subtract a string from a number.
Not validating tonumber result. Bad input silently becomes nil; the next line crashes. Always check.
Trying to read in non-interactive context. When you run lua script.lua < input.txt, io.read() reads from the file. When you run from inside Neovim with :!lua %, stdin is connected to your terminal — input works.
Comments inside long strings. local s = [[ -- not a comment ]] — the dashes are literal characters in the string, not a comment.
Using --[[ without thinking about closing. A stray ]] later in the file ends the comment block early. Use the dash-toggle trick so blocks are easy to flip.
What's next
Episode 7: if statements. if, elseif, else. Build a grade calculator that maps a score to a letter grade.
Recap
Comments: -- for single-line, --[[ ... ]] for multi-line. The ---[[ trick lets you toggle blocks in/out with one keystroke. io.read() reads a line as a string. tonumber() converts the string to a number; returns nil on bad input — always validate. io.read("*n") reads and converts in one step. Multiple values: local a, b = io.read("*n", "*n").
Next episode: if, elseif, else.