1. От исходного файла к работающей программе.
Узнайте об исходных файлах, функциях, выводе, проверке и выполнении.
Wave Foundation
Прежде чем начать эту главу
Подготовьте компилятор и стандартную библиотеку в соответствии с Инструкции по установке. Если вы можете запустить wavec --version в своем терминале, вы можете приступить к работе. Подойдет любой редактор, который может сохранять текстовые файлы.
В этой главе мы начнем с создания одной строки вывода и изучим взаимосвязи между исходными файлами, функциями, компиляцией, выполнением и кодом выхода. Цель — не просто скопировать инструкции, а уметь объяснить, что на каком этапе происходит.
Создать рабочий каталог
Использование отдельного каталога для каждой программы упрощает поиск исходных и сгенерированных исполняемых файлов. Создайте и перейдите в каталог в терминале.
mkdir wave-study
cd wave-study
Создайте main.wave в этом каталоге с помощью своего редактора. Проверьте расширение и убедитесь, что имя файла не main.wave.txt. Ниже приведен весь файл, а не только фрагмент внутри функции.
fun main() {
println("Hello, Wave!");
}
Результат выполнения:
Hello, Wave!
Запустите его с помощью следующей команды:
wavec run main.wave
Не смешивайте команды терминала с кодом Wave. Введите wavec run в терминал и запишите fun main в исходный файл. Нет необходимости вставлять вывод программы Hello, Wave! обратно в исходный код.
читать построчно
fun — ключевое слово, объявляющее функцию. Функция — это именованный набор операций, а main — это точка входа исполняемого файла. Остальные функции вы определите сами позже.
() после main — это место, где вы записываете параметры. main в этой программе не принимает параметров и поэтому пуст. Запишите операции функции между { и }. Отступы облегчают чтение блоков людьми, а сами границы блоков обозначаются фигурными скобками.
println("Hello, Wave!"); — это предложение, которое выводит строку. Двойные кавычки обозначают начало и конец строкового литерала. Сами кавычки не включаются в вывод. Точка с запятой указывает на конец этого оператора.
Операторы выполняются в том порядке, в котором они написаны.
Попробуйте изменить его на печать три раза. Не объединяйте с предыдущей программой, а замените содержимое main.wave всей программой ниже.
fun main() {
println("start");
println("working");
println("done");
}
Результат выполнения:
start
working
done
Закончив первое предложение, переходите к следующему предложению. Здесь нет одновременно выполняемых задач. Если вы хотите изменить порядок вывода, просто измените порядок предложений. Вы можете управлять этой последовательностью, изучив условные операторы и циклы позже.
print и println
println добавляет разрыв строки в конце. print не меняет строки автоматически. Разница очевидна при соединении небольших частей в одну линию.
fun main() {
print("Wave");
print(" ");
println("study");
println("second line");
}
Результат выполнения:
Wave study
second line
Трехкратная печать не всегда приводит к появлению трех строк. Различают количество выходных вызовов и количество строк. При написании разрыва строки непосредственно в строке используйте \n escape. Строка escape и разрывы строк в исходном коде подробно описаны в струнный лист.
Вставить значение в строку
Чтобы поместить результат вычисления в строку, передайте значение, соответствующее {}.
fun main() {
println("{} + {} = {}", 2, 3, 2 + 3);
}
Результат выполнения:
2 + 3 = 5
Первый {} содержит 2, второй — 3, а третий — 5. Строка формата и значение разделяются запятыми. Если вы измените количество значений, количество placeholder также должно совпадать. Это другой синтаксис вывода, чем операция сложения строк.
Разделите проверку, сборку и исполнение
run, используемый до сих пор, выполняет сборку и выполнение последовательно. Если вы хотите узнать, какой шаг не удался, вы можете разбить его следующим образом:
wavec check main.wave
wavec build main.wave -o hello
check проверяет грамматику, типы и т. д., но не проверяет весь ввод в программу. Например, одна только проверка источника не может определить, существует ли файл во время выполнения. build создает исполняемый файл. В Linux/macOS выполните следующее.
./hello
В Windows назовите выходной файл hello.exe и запустите PowerShell как .\hello.exe. Если вы измените исходный код после создания исполняемого файла, вам придется пересобрать его, чтобы изменения были отражены.
Код выхода также является результатом
Люди читают оператор вывода, но оболочка или другая программа могут определить успех по коду выхода. Следующее указывает, что main возвращает i32.
fun main() -> i32 {
println("completed");
return 0;
}
Результат выполнения:
completed
0 — это условное обозначение нормального завершения работы. При прямой сигнализации об ошибке верните ненулевой код. В оболочке Linux/macOS код проверяется как echo $? сразу после выполнения, а в оболочке PowerShell он проверяется как $LASTEXITCODE. Если вы тем временем запустите другие команды, то, что вы проверяете, может измениться.
Выполнение return завершает функцию. main не объявляет никаких параметров, и даже если они имеют значения по умолчанию, они не разрешены. Опустите тип возвращаемого значения main или используйте i32.
Как прочитать первую ошибку
Следующий код намеренно неверен: в отличие от текущего примера, он должен завершиться ошибкой check.
fun main() {
println("hello")
}
В конце предложения нет точки с запятой. Посмотрите на строку, отображаемую диагнозом, и на предложение, непосредственно предшествующее ей. Место, отмеченное компилятором, может быть не местом возникновения ошибки, а местом, где неправильная структура больше не может быть интерпретирована.
Сначала исправьте первую ошибку, а затем повторите проверку. Если предыдущие скобки или кавычки не закрыты, могут возникнуть различные ошибки даже в последующем нормальном коде. Если вы попытаетесь исправить все линии одновременно, легко упустить первоначальную причину.
практические задачи
- Создайте программу, которая печатает три строки представления себя.
- Передайте 12 и 8 в качестве аргументов формата для вывода
12 * 8 = 96. - Напишите его, чтобы распечатать сообщение об успехе и вернуть код выхода 0.
- Перед выполнением спрогнозируйте, каким будет количество выходных строк, если вы используете print только три раза.
Решение: выходные данные процесса расчета
fun main() -> i32 {
println("Learning Wave");
println("My first program");
println("Ready to calculate");
println("{} * {} = {}", 12, 8, 12 * 8);
return 0;
}
Результат выполнения:
Learning Wave
My first program
Ready to calculate
12 * 8 = 96
Вместо того, чтобы записывать результат вычисления непосредственно в строку как 96, я передал его как выражение. Это первый шаг, позволяющий гарантировать, что результаты вычислений и их отображение не изменятся даже при изменении входного значения. В следующей главе мы дадим переменным имена, чтобы избежать многократного написания одного и того же значения.
Элемент верхнего уровня в исходном файле
Источник Wave может состоять из следующих элементов верхнего уровня:
import(...)const,statictypestruct,enum,protoextern(...),export(...)fun#[target(...)]состояние перед поддерживаемым элементом
Префикс импортируемых объявлений с pub. Поместите локальное объявление var внутри функции или блока.
Автономная программа
Цели без ядра, загрузочного кода или среды выполнения могут использовать опцию автономной сборки.
wavec build kernel.wave \
--freestanding \
--entry=_start \
--linker-script=linker.ld \
--no-start-files
--freestanding ссылается на план сборки, который отключает зависимости библиотеки по умолчанию, а --entry устанавливает символ записи компоновщика. Чтобы создать фактический загрузочный результат, вам необходимо спроектировать целевую архитектуру, сценарий компоновщика и даже формат объекта.
Точки входа, которые намеренно терпят неудачу
Вы не можете иметь параметр в main, даже если он имеет значение по умолчанию. При копировании следующего файла check произошла ошибка.
fun main(value: i32 = 0) -> i32 {
return value;
}