diff --git a/lib/lua/lexer.ex b/lib/lua/lexer.ex index 81edb60..b899dea 100644 --- a/lib/lua/lexer.ex +++ b/lib/lua/lexer.ex @@ -37,10 +37,23 @@ defmodule Lua.Lexer do """ @spec tokenize(String.t()) :: {:ok, [token()]} | {:error, term()} def tokenize(code) when is_binary(code) do + # Handle shebang on first line (Unix convention: #! means interpreter directive) + code = strip_shebang(code) pos = %{line: 1, column: 1, byte_offset: 0} do_tokenize(code, [], pos) end + # Strip shebang (#!) if it's the first line + defp strip_shebang(<<"#!", rest::binary>>) do + # Skip entire first line (everything up to and including the newline) + case String.split(rest, ~r/\r\n|\r|\n/, parts: 2) do + [_shebang_line, remaining] -> remaining + [_only_shebang] -> "" + end + end + + defp strip_shebang(code), do: code + # End of input defp do_tokenize(<<>>, acc, pos) do {:ok, Enum.reverse([{:eof, pos} | acc])} diff --git a/test/lua/lexer_test.exs b/test/lua/lexer_test.exs index 8074a65..205d3be 100644 --- a/test/lua/lexer_test.exs +++ b/test/lua/lexer_test.exs @@ -340,6 +340,28 @@ defmodule Lua.LexerTest do assert {:ok, [{:comment, :multi, " comment ", _}, {:identifier, "x", _}, {:eof, _}]} = Lexer.tokenize("--[[ comment ]]x") end + + test "ignores shebang on first line" do + # Lua ignores the first line if it starts with # + code = """ + #!/usr/bin/env lua + local x = 1 + return x + """ + + assert {:ok, tokens} = Lexer.tokenize(code) + # Shebang should be treated as a comment and stripped + assert [{:keyword, :local, _} | _] = tokens + + # Shebang only on first line + code2 = """ + local x = 1 + #!/usr/bin/env lua + """ + + # Second line with # should cause error (not a shebang) + assert {:error, _} = Lexer.tokenize(code2) + end end describe "whitespace" do