Как использовать с выпиской в ​​эликсире? - PullRequest
0 голосов
/ 21 марта 2020

У меня есть основная функция, которая использует все остальные функции, использующие этот код:

File.read!(file_path)
|> Sanitizer.sanitize_source()
|> IO.inspect(label: "\nSanitizer ouput")
|> Lexer.scan_words()
|> IO.inspect(label: "\nLexer ouput")
|> Parser.parse_program()
|> IO.inspect(label: "\nParser ouput")
|> CodeGenerator.generate_code()
|> Linker.generate_binary(assembly_path

Но каждый раз, когда функция возвращает ошибку, остальное заставляет программу обработать sh. Мой учитель сказал мне использовать with вместо этого, чтобы решить эту проблему, и после прочтения документации я пришел к следующему:

with {:ok, contentF} <- File.read!(file_path)
         sanitizedList when is_list(sanitizedList) <- Sanitizer.sanitize_source(contentF)
         {:ok, _sMessage} <- IO.inspect(sanitizedList, label: "\nSanitizer output")
         lexedList when not is_tuple(errorLex) <- Lexer.scan_words(sanitizedList)
         {:ok, _sMessage} <- IO.inspect(lexedList, label: "\nLexer output")
         parsedAST when not is_tuple(errorPar) <- Parser.parse_program(lexedList)
         {:ok, _sMessage} <- IO.inspect(parsedAST, label: "\nParser output")
         {:ok, codeAssembly} <- CodeGenerator.generate_code(parsedAST)
         :ok <- Linker.generate_binary(codeAssembly, assembly_path)
    do
      {:ok, "compilation complete"}
    else
      error -> {:error, "error: couldn't compile the file" <> file_path}
    end

Из того, что я прочитал, это должно, по крайней мере, скомпилироваться, но это дает мне ошибка

(CompileError) lib/nqcc.ex:32: missing :do option in "with"
    lib/nqcc.ex:28: (module)

Я не совсем уверен, почему, у меня есть оператор do.

Если это помогает, это то, что должна возвращать каждая функция:

Sanitizer.sanitize_source() должен возвращать список кортежей

Lexer.scan_words() должен возвращать список кортежей или {:error, "message"}

Parser.parse_program() должен возвращать AST или {:error, "message"}

Ответы [ 2 ]

0 голосов
/ 21 марта 2020

TL; DR: ошибка вызвана отсутствием запятых между предложениями with/1. Кроме того, File.read!/1 возвращает содержимое или повышает его; чтобы сопоставить шаблон с {: ok, _} кортежем, вы должны использовать File.read/1 (без взрыва). С этими двумя исправлениями код должен работать.


foo должен вернуться result или {:ok, error} кортеж.

Это конструктивный недостаток, не позволяющий использовать with/1 в идиоматическом c, читабельном путь. Независимо от того, имеет ли функция монадическое поведение c, следует прибегнуть к возврату {:ok, result} и {:error, reason} кортежей. Проверьте File.read/1 на наличие вдохновения.

Итак, если у вас Sanitizer, Lexer и Parser, обновите соответствующие функции, чтобы они возвращали кортежи в обоих случаях. Если нет, предоставьте помощника, как показано ниже

defp idiomatize({:error, reason}), do: {:error, reason}
defp idiomatize(result), do: {:ok, result}

Хотя вы, очевидно, можете добиться этого с помощью охранников, код быстро становится нечитаемым и не поддерживается. С помощником выше вы можете сделать

with {:ok, contentF} <- File.read(file_path),
     {:ok, sanitizedList} <- idiomatize(Sanitizer.sanitize_source(contentF)),
     IO.inspect(sanitizedList, label: "\nSanitizer output"),
     {:ok, lexedList} <- idiomatize(Lexer.scan_words(sanitizedList)),
     IO.inspect(lexedList, label: "\nLexer output"),
     {:ok, parsedAST} <- idiomatize(Parser.parse_program(lexedList)),
     IO.inspect(parsedAST, label: "\nParser output"),
     {:ok, codeAssembly} <- CodeGenerator.generate_code(parsedAST),
     :ok <- Linker.generate_binary(codeAssembly, assembly_path)
    do
      {:ok, "compilation complete"}
    else
      {:error, error} ->
        {:error, "error: couldn't compile the file" <> file_path}
end
0 голосов
/ 21 марта 2020

Во-первых, вам следует ознакомиться с документацией для with/1.

В Elixir обычно функции имеют варианты, которые заканчиваются восклицательным знаком, например File.read!/1, которая вызывает ошибку, и без восклицательного знака, например File.read/1, который возвращает кортеж {:ok, result} в случае успеха или кортеж {:error, error} в случае ошибки.

Для эффективного использования with полезно использовать версии без восклицательного знака, которые возвращают кортеж ошибки, а не вызывают ошибку. Если у вас есть это, вы можете сопоставить успешные кортежи {:ok, result}. Если возникнет ошибка, он вернет кортеж {:error, error}.

Я не могу протестировать ваш код без полной реализации функций, но он, вероятно, должен выглядеть примерно так (не забудьте запятые в конце строк!):

with {:ok, contentF} <- File.read(file_path),
     {:ok, sMessage} <- Sanitizer.sanitize_source(contentF),
     {:ok, lexedList} <- Lexer.scan_words(sanitizedList),
     {:ok, parsedAST} <- Parser.parse_program(lexedList),
     {:ok, codeAssembly} <- CodeGenerator.generate_code(parsedAST),
     :ok <- Linker.generate_binary(codeAssembly, assembly_path) do
  {:ok, "compilation complete"}
end

Если вы хотите ввести IO.inspect вызовы для отладки:

with {:ok, contentF} <- File.read(file_path),
     {:ok, sMessage} <- Sanitizer.sanitize_source(contentF),
     IO.inspect(sMessage, label: "\nSanitizer ouput"),
     {:ok, lexedList} <- Lexer.scan_words(sanitizedList),
     IO.inspect(lexedList, label: "\nLexer ouput"),
     {:ok, parsedAST} <- Parser.parse_program(lexedList),
     IO.inspect(parsedAST, label: "\nParser ouput"),
     {:ok, codeAssembly} <- CodeGenerator.generate_code(parsedAST),
     :ok <- Linker.generate_binary(codeAssembly, assembly_path) do
  {:ok, "compilation complete"}
end

Редактировать: Если вы не можете редактировать исходные функции и хотят соответствовать ему с помощью охранников, ему должно нравиться что-то вроде этого (опять же, трудно сказать, не имея возможности проверить это):

with {:ok, contentF} <- File.read(file_path),
     sanitizedList when is_list(sanitizedList) <- Sanitizer.sanitize_source(contentF),
     IO.inspect(sanitizedList, label: "\nSanitizer output"),
     lexedList when not is_tuple(errorLex) <- Lexer.scan_words(sanitizedList),
     IO.inspect(lexedList, label: "\nLexer output"),
     parsedAST when not is_tuple(errorPar) <- Parser.parse_program(lexedList),
     IO.inspect(parsedAST, label: "\nParser output"),
     {:ok, codeAssembly} <- CodeGenerator.generate_code(parsedAST),
     :ok <- Linker.generate_binary(codeAssembly, assembly_path) do
  {:ok, "compilation complete"}
end
...