// SPDX-License-Identifier: Apache-2.0 ; specs/course/t27-basics.t27 -- course 0: the basics of t27, in 9 modules of 3 lessons ; Source of truth for apps/website/scripts/course-from-spec.mjs (gHashTag/trinity), read the ; way it reads specs/course/course.t27: the constant schema, every test block below, every ; widget in specs/widgets/gallery.t27, every lesson spec compiled clean, the Russian bundle named ; by specs/course/t27-basics-ru.t27. It writes this course into src/lib/course.generated.ts (the ; page at #/t27-basics) and a byte-identical copy of this file at public/learn/t27-basics.t27. ; ASCII only (L3), English only (LANG-EN). The Russian words live in the bundle. ; WHAT THE COURSE IS: course 0 of specs/course/courses.t27, taken before the FPGA course. It ; teaches the language itself: a module, constants and types, functions, tests, the compiler's ; stages and its backends. Every lesson opens one infographic (a table, and for some a flow ; diagram) whose words and rows live in specs/widgets/basics-.t27, and one lesson spec ; under specs/basics/ that compiles clean on every backend and whose tests pass. ; WHAT THE COURSE DOES NOT DO: it says no number of its own. A lesson quotes only the constants ; of the spec it opens and the counts the site's own compiler prints for it. ; phi^2 + 1/phi^2 = 3 | TRINITY module t27_basics; pub const KIND : str = "course"; pub const ID : str = "t27-basics"; pub const SCHEMA_VERSION : u8 = 1; ; The derived files the generator rewrites (relative to apps/website). pub const GENERATED : [2]str = ["src/lib/course.generated.ts", "public/learn/t27-basics.t27"]; pub const ROUTE : str = "t27-basics"; ; Share pages: learn/ for the first course of specs/course/courses.t27, learn// for the next. pub const SHARE_PATH : str = "learn/"; pub const GALLERY : str = "specs/widgets/gallery.t27"; pub const LOCALES : [2]str = ["en", "ru"]; pub const RU_CONTRACT : str = "specs/course/t27-basics-ru.t27"; ; Lesson marks the reader sets are kept in this browser only, under this key, one key for ; every course. pub const PROGRESS_KEY : str = "t27-course-done"; pub const SENDS_NOTHING : bool = true; pub const COHORT_ROUTE : str = "fpga-training"; pub const TOOL_FRAME_HEIGHT : u16 = 640; pub const PLAYER_FRAME_HEIGHT : u16 = 480; ; --- Words on the page --------------------------------------------------------------------- pub const TITLE : str = "t27 basics: the language in 27 lessons"; pub const DESCRIPTION : str = "Course 0, 27 lessons, one infographic and one t27 spec each: a module, constants and types, functions and tests, then the compiler's stages and the 7 languages it writes. It comes before the FPGA course."; pub const SAY_KICKER : str = "Course"; pub const SAY_LEAD : str = "Before the FPGA course: what a t27 spec is, how the compiler reads it, and what it gives back. Every lesson opens one table or diagram drawn from its own spec and one spec you can run in your browser."; pub const SAY_SHAPE : str = "9 modules of 3 lessons, 27 cells. A filled cell is a lesson you marked done."; pub const SAY_START : str = "Start lesson 1"; pub const SAY_CONTINUE : str = "Continue"; pub const SAY_MODULE : str = "Module {0}"; pub const SAY_LESSON : str = "Lesson {0} of {1}"; pub const SAY_GOAL : str = "You will learn"; pub const SAY_TRY : str = "Try it"; pub const SAY_ALSO : str = "Also try"; pub const SAY_ALSO_HINT : str = "Each one swaps the widget above."; pub const SAY_BACK_TO_MAIN : str = "Back to this lesson's widget"; pub const SAY_SPEC : str = "Open the lesson's spec in the player"; pub const SAY_OPEN_PAGE : str = "Open the widget on its own page"; pub const SAY_WIDGET_LANG : str = "Widgets keep their own English words: each one lives in its own t27 spec."; pub const SAY_PREV : str = "Previous"; pub const SAY_NEXT : str = "Next"; pub const SAY_ALL : str = "All lessons"; pub const SAY_MARK : str = "Mark as done"; pub const SAY_MARKED : str = "Done"; pub const SAY_PROGRESS : str = "{0} of {1} done"; pub const SAY_PRIVATE : str = "Progress stays in this browser; nothing is sent."; pub const SAY_SOURCE : str = "This course is itself a t27 spec: read it"; pub const SAY_COHORT : str = "Looking for the paid FPGA training? It has its own page."; pub const SAY_NOT_FOUND : str = "No lesson has this address."; pub const SAY_OPEN_LESSON : str = "Open the interactive lesson"; pub const SAY_OPEN_COURSE : str = "Open the interactive course"; pub const SAY_SEO_TITLE : str = "{0}: t27 basics course, lesson {1} of {2}"; pub const SAY_SHARE : str = "Link to share, with a preview card"; pub const SAY_NEXT_COURSE : str = "Next course"; pub const SAY_PREV_COURSE : str = "Previous course"; ; --- Modules ------------------------------------------------------------------------------- pub const LESSONS_PER_MODULE : u8 = 3; pub const MODULE_COUNT : u8 = 9; pub const MODULE_IDS : [9]str = [ "a-spec-file", "values-and-types", "arrays-trits-expressions", "tests-and-functions", "inside-a-function", "your-own-types", "modules-rules-gen-ts", "the-compiler-and-tri", "errors-a-program-what-next" ]; pub const MODULE_TITLES : [9]str = [ "A spec file", "Values and types", "Arrays, trits and expressions", "Tests and functions", "Inside a function", "Visibility and your own types", "Modules, rules and gen-ts", "The compiler and tri", "Errors, a program, what next" ]; pub const MODULE_LINES : [9]str = [ "What a .t27 file holds: a module line, prose lines and comments.", "Constants, the width of a whole number, and true, false and text.", "Lists of values, the three values of a trit, and what operators compute.", "A test block, several small tests in one spec, and a function with typed inputs.", "Names with let and var, choices with if and switch, and loops.", "What pub shares with other modules, and the structs and enums you declare.", "Reading another module, stating a rule that always holds, and a spec turned into TypeScript.", "The backends t27c writes, the stages it reads a spec in, and the tri command.", "Reading a compiler error, one small complete program, and the course to take next." ]; ; --- Lessons ------------------------------------------------------------------------------- pub const LESSON_COUNT : u8 = 27; pub const LESSON_IDS : [27]str = [ "what-a-spec-is", "the-module-line", "comments-and-prose", "constants", "whole-numbers", "true-false-and-text", "arrays", "trits", "expressions", "a-test-block", "many-tests", "functions", "local-names", "if-and-else", "loops", "pub-or-private", "structs", "enums", "use-other-modules", "invariants", "gen-ts", "seven-backends", "stages-of-t27c", "tri-commands", "reading-errors", "a-small-program", "where-next" ]; pub const LESSON_MODULES : [27]str = [ "a-spec-file", "a-spec-file", "a-spec-file", "values-and-types", "values-and-types", "values-and-types", "arrays-trits-expressions", "arrays-trits-expressions", "arrays-trits-expressions", "tests-and-functions", "tests-and-functions", "tests-and-functions", "inside-a-function", "inside-a-function", "inside-a-function", "your-own-types", "your-own-types", "your-own-types", "modules-rules-gen-ts", "modules-rules-gen-ts", "modules-rules-gen-ts", "the-compiler-and-tri", "the-compiler-and-tri", "the-compiler-and-tri", "errors-a-program-what-next", "errors-a-program-what-next", "errors-a-program-what-next" ]; pub const LESSON_WIDGETS : [27]str = [ "basics-what-a-spec-is", "basics-the-module-line", "basics-comments-and-prose", "basics-constants", "basics-whole-numbers", "basics-true-false-and-text", "basics-arrays", "basics-trits", "basics-expressions", "basics-a-test-block", "basics-many-tests", "basics-functions", "basics-local-names", "basics-if-and-else", "basics-loops", "basics-pub-or-private", "basics-structs", "basics-enums", "basics-use-other-modules", "basics-invariants", "basics-gen-ts", "basics-seven-backends", "basics-stages-of-t27c", "basics-tri-commands", "basics-reading-errors", "basics-a-small-program", "basics-where-next" ]; pub const LESSON_ALSO : [27]str = [ "t27c-stages", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "play-js", "play-rust", "t27c-hello-world", "", "t27-tri-misread", "play-verilog", "play-hello-world" ]; pub const LESSON_SPECS : [27]str = [ "specs/basics/01_what_a_spec_is.t27", "specs/basics/02_the_module_line.t27", "specs/basics/03_comments_and_prose.t27", "specs/basics/04_constants.t27", "specs/basics/05_whole_numbers.t27", "specs/basics/06_true_false_and_text.t27", "specs/basics/07_arrays.t27", "specs/basics/08_trits.t27", "specs/basics/09_expressions.t27", "specs/basics/10_a_test_block.t27", "specs/basics/11_many_tests.t27", "specs/basics/12_functions.t27", "specs/basics/13_local_names.t27", "specs/basics/14_if_and_else.t27", "specs/basics/15_loops.t27", "specs/basics/16_pub_or_private.t27", "specs/basics/17_structs.t27", "specs/basics/18_enums.t27", "specs/basics/19_use_other_modules.t27", "specs/basics/20_invariants.t27", "specs/basics/21_gen_ts.t27", "specs/basics/22_seven_backends.t27", "specs/basics/23_stages_of_t27c.t27", "specs/basics/24_tri_commands.t27", "specs/basics/25_reading_errors.t27", "specs/basics/26_a_small_program.t27", "specs/basics/27_where_next.t27" ]; pub const LESSON_TITLES : [27]str = [ "What a spec is", "The module line", "Comments and prose lines", "Constants", "Whole numbers and their widths", "True, false and text", "Arrays", "Trits: minus one, zero, one", "Expressions", "A test block", "Many tests in one spec", "Functions", "Local names: let and var", "Choices: if, else and switch", "Loops: while and for", "pub or private", "Structs", "Enums", "Using other modules", "Invariants and benches", "gen-ts: a spec becomes TypeScript", "Seven backends", "The stages of t27c", "tri: the command line", "Reading compiler errors", "A small complete program", "Where to go next" ]; pub const LESSON_GOALS : [27]str = [ "What goes into a .t27 file and what the compiler gives back for it.", "How to write the module line, which names it accepts, and where it goes.", "Where each kind of note may stand, and the one line that breaks the parser.", "The six pieces of a constant declaration and what each one does.", "The integer types, their ranges, and the error a value too big for its type gets.", "How to declare a bool and a str, and what a test can compare them with.", "How to declare an array, read an item, and which index is the last one.", "How a trit is written as a small signed constant and why 27 shows up everywhere.", "Arithmetic operators and comparisons on the lesson constants, and the bool a comparison gives back.", "How a test block is written and what a failing assert reports.", "Why several small tests read better than one long one, and how the report counts them.", "The two ways t27 writes a function signature, and the body with return.", "When to use let and when var, and how a var is updated.", "The forms of if the compiler accepts, the if expression, and switch with else.", "The three loop forms the compiler accepts and how a range is written.", "What pub declares, and what the TypeScript backend writes with and without it.", "How to declare a struct and a packed struct, and how many a module may hold.", "The two ways to declare an enum and how a switch reads a value.", "How a use line is written, and what the browser can and cannot do with it.", "How invariant and bench blocks are written and who evaluates them.", "What gen-ts writes for constants and functions, and how a host program uses it.", "The names of the 7 backends and how much code each wrote for one small spec.", "The order of the compiler stages and what each one produced for the lesson spec.", "What tri test does, how tri hands other words to t27c, and which commands are its own.", "How to read the line and column of an error, using four real mistakes.", "How the pieces of this course make one spec that becomes a circuit.", "Which course to take next, and what each one adds to the basics." ]; pub const LESSON_TEXTS : [27]str = [ "A spec is a plain text file that ends in .t27. It opens with a module line, declares values and functions, and carries test blocks that check them. The compiler, t27c, reads the file and answers with a verdict and code for 7 backends. The spec under this lesson has 2 constants and 1 test, and the site compiles it in your browser.", "The module line is the keyword module, a name and a semicolon. Comment and prose lines may come before it; declarations come after. Names use letters, digits and underscores; the compiler in this site also accepts hyphens, as in tutorial-06-structs. A course spec on this site must use the module name its generator expects, so a wrong name is caught by the build and not by the compiler.", "A line that starts with a semicolon is prose: the compiler skips the rest of the line. Prose lines stand at the top level, between declarations. Two slashes start a comment anywhere, after code or inside a test. One trap: a semicolon alone on its line is not an empty prose line, it is a parse error, and the whole spec fails. Put a word after it, or leave the line empty.", "pub const WIDTH : u8 = 8; reads left to right: pub makes the name visible to other modules, const says the value is fixed, WIDTH is the name, u8 is the type, 8 is the value, and the semicolon ends it. Constants are what this site's test runner can check: the lesson spec declares WIDTH, HEIGHT and AREA and tests that 8 times 4 is 32.", "A u in the type name means unsigned, an i means signed, and the digits are the number of bits. u8 holds 0 to 255, u16 holds 0 to 65535, i32 holds negative numbers too. The compiler checks a constant against its type: give a u8 the value 300 and it stops with an error that says no u8 can hold 3 digits. The browser's checker is lenient elsewhere, so do not read its silence as proof.", "A bool is true or false. A str is text in double quotes, and the text may hold characters that mean something elsewhere in t27, such as a semicolon or two slashes: inside quotes they are just text. A test compares a bool or a str with == and !=. One honest limit: the checker in this browser does not stop a str given to a u8 constant, so keep types and values matched yourself.", "An array type is the length in square brackets followed by the item type: [5]u8 is five unsigned bytes. The value lists the items in square brackets. Index 0 holds the item at the start, so in an array of 5 the last index is 4. A test reads an item with PRIMES[4]. Keep the declared length and the list in step: the checker in this browser does not catch a length that disagrees with the list.", "A bit has two values; a trit has three: minus one, zero and one. The lesson spec writes them as i8 constants NEG, ZERO and POS, the way the tutorial specs of t27 do. Adding POS and NEG gives ZERO. Three trits have 3 times 3 times 3 combinations, which is 27: the number of lessons in this course and the cells of a TRI-27 word.", "An expression combines names and numbers with operators. With A = 17 and B = 5, A + B is 22, A - B is 12, A * B is 85, A / B is 3 because whole numbers divide without a fraction, and A % B is 2, the remainder. Comparisons give true or false. One limit of the test runner on this site: it does not read a minus sign in front of a number, so write negative values as constants.", "A test block starts with the keyword test and a name, and holds assert lines in braces. Each assert takes an expression that must be true. When one is false, the report names the test and the number of the assert, for example assert #1 is false. A spec's tests are its claims about itself, which is why every course spec on this site carries some.", "A spec may hold any number of test blocks. Each one checks one idea and has a name that says which. The report counts tests and asserts separately: the lesson spec has 3 tests and 5 asserts. When one fails, its name tells you which idea broke, and the other tests still report on their own ideas.", "fn add(a: u8, b: u8) -> u8 declares a function with two u8 inputs and a u8 result; the body in braces ends with return. The compiler also accepts the Zig style, pub fn add(a: u8, b: u8) u8, without the arrow. There are no generics: fn id is a parse error. The test runner on this site checks constants and does not call functions, so the lesson keeps the expected results as constants beside them.", "let a = w * h; gives a name to a value inside a function body. var i : u8 = 0; declares a name that may change, and i = i + 1; changes it. Use let when the value is computed once, var when a loop updates it. These names live only inside the function; the module level uses const.", "if (a > b) { ... } else { ... } runs one of two blocks. The parentheses are optional: if a > b { ... } also compiles. if can be an expression too: return if (a > b) a else b; gives the larger one. switch (m) { 0 => 10, else => 20 } picks a value by matching m, and else covers every other case.", "while (i < n) { i = i + 1; } repeats its block while the condition is true, and the block must move toward making it false. for i in 0..n { } walks i from 0 up to n, with n itself left out. The compiler also accepts the Zig form, for (0..4) |i| { }. A loop usually updates a var declared before it.", "pub in front of const, fn, struct or enum says other modules may use the name. Without pub the name is meant for the module itself. Today the compiler does not enforce this in the code it writes: gen-ts exports both the pub and the private constant of the lesson spec. Use pub to say what a module offers, and read the generated code before relying on privacy.", "pub struct Point { x: i32, y: i32 } declares a type with two fields. Fields are a name and a type, separated by commas. A packed struct lays its fields next to each other with no padding, the shape a hardware register has. A module may declare several structs; the lesson spec has two, and the verdict stays clean on all 7 backends.", "pub enum Color { Red, Green, Blue } names three cases. The Zig form pub const Mode = enum(u8) { Idle, Run } also fixes the type that stores a case, here u8. A switch over a value lists what each case gives and uses else for the rest. Both forms compile clean on all 7 backends in the lesson spec.", "use base::types; names a module by its path, with :: between the parts. The parser reads it as a use declaration. The compiler in your browser has no checkout of the other files, so it does not open base::types and does not check names that come from it. The native t27c resolves imports from a repository; this lesson could not run that check here.", "invariant name followed by an assert states a rule of the spec that must hold for every value, not one example. bench name marks a block to time. Both compile. The test runner on this site evaluates test blocks only: it skips invariants and benches, so a false invariant passes here silently. The lesson spec repeats its invariant as a test for that reason.", "t27c gen-ts turns each constant into an export const line that TypeScript type-checks. Functions are not emitted: the backend says it lowers declarations, not bodies. This is how this site uses specs: a generator reads a .t27 file and writes the numbers and words a page imports. The table under this lesson shows what gen-ts wrote for the lesson spec.", "The compiler in your browser has 7 backends: c, js, rust, ts, verilog, verilog_hir and zig. Each reads the same parsed spec and writes code in its language; the verilog_hir backend is written from the hardware view, HIR. The table shows the bytes each backend wrote for the lesson spec, measured with the compiler on this site. Bigger is not better: each language needs a different amount of text.", "The lexer cuts the text into tokens. The parser builds a tree of nodes. The type checker reports errors and warnings without stopping the backends. HIR is the hardware view that the verilog_hir backend is written from; the other backends read the parser's tree. The table shows the counts each stage gave for the lesson spec, measured with the compiler on this site.", "In the gHashTag/t27 repository, scripts/tri is a small wrapper. tri test runs the conformance suite, t27c suite --repo-root .; a word tri does not know, such as gen or seal, is passed on to t27c. Its own commands include audit, recall, frontier, lesson and help. This lesson read the script and did not run it: native t27c is built on a Linux machine, and this course does not run it on yours.", "A parse error says where the parser was, near which line, and the token it did not expect, with a line and a column. A type error names the constant and why its value does not fit. Read the position first, then look one token to the left: the mistake is often just before the place the parser stopped. The table shows what the compiler on this site answered for four small mistakes.", "The lesson spec has a module line, a width constant, test values and on_comb, a function of two u8 inputs. gen-verilog turns it into a module whose ports are a clock, a reset and an enable, the two 8-bit inputs a and b, a ready output and the 8-bit output result. An assign line sets result from on_comb, so the sum itself waits for no clock edge. The tests check the example sums as constants; the site runner does not call on_comb, so the check of the circuit itself is left to a simulator.", "You can now read a spec: its module line, constants, types, functions, tests and the code its backends write. Course 1 follows a spec onto an Artix-7 FPGA: synthesis, placement, timing and a bitstream checked on a board. Course 2 builds number formats for AI in t27, from OCP MX blocks to a ternary network. Both courses have 27 lessons and open a spec in every one." ]; pub const LESSON_TASKS : [27]str = [ "Open the lesson spec in the player and find its three parts: the module line, the constants, the test.", "In the player, change the module name of this lesson spec and watch the verdict stay clean.", "Add a line with a lone semicolon to the lesson spec in the player, read the error, then remove it.", "Change AREA in the lesson spec to 33 and watch which test fails.", "Set BYTE_MAX to 300 in the player and read the error the compiler prints.", "Change NAME in the lesson spec and fix the test so that it passes again.", "Add a sixth prime to PRIMES, fix the length, and write an assert for index 5.", "Write an assert that POS minus NEG equals 2, and check it passes.", "Change B to 4 and predict each of the five results before you run the tests.", "Break the test on purpose by changing DAYS, then read the failure line.", "Make one of the three tests fail and check that the other two still pass.", "Write a function sub in the player in either style and check the verdict stays clean.", "Change let to var in area and check the verdict; then explain why let reads better there.", "Add a case 1 => 15 to the switch in the player and check the verdict.", "Work out by hand what sum_to returns for n = 4, then write it as a constant with a test.", "Open the ts tab of the lesson spec in the player and find both constants.", "Add a field z: i32 to Point and check which backends still compile it.", "Add a fourth color to Color and update CASES_IN_COLOR so the test passes.", "Find the use declaration in the parser tree of the lesson spec in the player.", "Make the invariant false in the player and check that nothing on this site complains, then make the test false.", "Add a constant to the lesson spec in the player and find its line in the ts tab.", "Open each backend tab of the lesson spec in the player and find the function add.", "Open the t27c stages widget, paste the lesson spec, and match its counts with the table.", "Read the table and say which commands go to t27c and which stay inside tri.", "Make each of the four mistakes in the player and match the line and column with the table.", "Open the verilog tab of the lesson spec in the player and find the input, output and assign lines.", "Pick the next course from the table and open its lesson 1." ]; test the_course_is_three_cubed { assert LESSONS_PER_MODULE == 3; assert MODULE_COUNT == 9; assert LESSON_COUNT == 27; assert MODULE_COUNT * LESSONS_PER_MODULE == LESSON_COUNT; assert LESSON_COUNT == 3 * 3 * 3; } test it_starts_with_a_spec_and_ends_with_a_program { assert MODULE_IDS[0] == "a-spec-file"; assert LESSON_MODULES[0] == "a-spec-file"; assert LESSON_WIDGETS[0] == "basics-what-a-spec-is"; assert MODULE_IDS[8] == "errors-a-program-what-next"; assert LESSON_MODULES[26] == "errors-a-program-what-next"; assert LESSON_WIDGETS[26] == "basics-where-next"; } test the_course_sends_nothing { assert SENDS_NOTHING == true; assert LOCALES[0] == "en"; assert LOCALES[1] == "ru"; } test every_module_holds_three_lessons_in_order { assert LESSON_MODULES[0] == MODULE_IDS[0]; assert LESSON_MODULES[2] == MODULE_IDS[0]; assert LESSON_MODULES[3] == MODULE_IDS[1]; assert LESSON_MODULES[24] == MODULE_IDS[8]; assert LESSON_IDS[24] == "reading-errors"; }