Stand up the Cargo workspace for the Legis compiler and implement the lexer, the first stage of the pipeline described in the v1.0 specification. - legis-diag: Diagnostic type plus the banned-jargon guard that the whole toolchain is tested against (no forbidden programming term may appear in any message). Whole-word, underscore-aware matching so quoted code identifiers are not false positives. - legis-lexer: logos-based lexer covering the full token list. Numbers carry no sign (unary minus is a parser concern), text and multi-line text values, doc comments preserved while line/block comments are dropped, longest-match tags and operators, plain-English lexer errors. - legis-cli: the `legis` binary with a `tokens` inspection command. - examples/calculator.lgi: the specification's reference program, the milestone the pipeline is built toward. - PLAN.md: phased implementation roadmap (LLVM-from-start). 18 tests pass; fmt and clippy clean. The lexer tokenizes the reference calculator with no errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
147 lines
3.5 KiB
Plaintext
147 lines
3.5 KiB
Plaintext
use library std.console;
|
|
use library std.text;
|
|
|
|
|
|
/// The mathematical operation the user has chosen.
|
|
choices Operation {
|
|
Add;
|
|
Subtract;
|
|
Multiply;
|
|
Divide;
|
|
}
|
|
|
|
|
|
/// Asks the user which operation to perform.
|
|
function choose_operation() returns Result<Operation, Text> {
|
|
std.console.println("");
|
|
std.console.println("Choose an operation");
|
|
std.console.println(" + Add");
|
|
std.console.println(" - Subtract");
|
|
std.console.println(" * Multiply");
|
|
std.console.println(" / Divide");
|
|
std.console.print("> ");
|
|
|
|
let response = pass up std.console.read_line()
|
|
.or_fail("Could not read from the keyboard");
|
|
|
|
match std.text.trim(response) {
|
|
is "+": give back Operation.Add;
|
|
is "-": give back Operation.Subtract;
|
|
is "*": give back Operation.Multiply;
|
|
is "/": give back Operation.Divide;
|
|
otherwise:
|
|
give back fail("Please enter +, -, *, or /");
|
|
}
|
|
}
|
|
|
|
|
|
/// Asks the user for a decimal number.
|
|
function choose_number(prompt: Text) returns Result<Decimal, Text> {
|
|
std.console.print("{prompt}: ");
|
|
|
|
let response = pass up std.console.read_line()
|
|
.or_fail("Could not read from the keyboard");
|
|
|
|
let cleaned = std.text.trim(response);
|
|
|
|
when std.text.parse<Decimal>(cleaned) {
|
|
succeeds with number:
|
|
give back number;
|
|
fails:
|
|
give back fail("'{cleaned}' is not a valid number");
|
|
}
|
|
}
|
|
|
|
|
|
/// Carries out the calculation.
|
|
/// Division by zero gives back a failure rather than crashing.
|
|
pure function calculate(
|
|
first_number: Decimal,
|
|
operation: Operation,
|
|
second_number: Decimal
|
|
) returns Result<Decimal, Text> {
|
|
match operation {
|
|
is Operation.Add:
|
|
give back first_number + second_number;
|
|
is Operation.Subtract:
|
|
give back first_number - second_number;
|
|
is Operation.Multiply:
|
|
give back first_number * second_number;
|
|
is Operation.Divide:
|
|
if second_number == 0.0 {
|
|
give back fail("Division by zero is not allowed");
|
|
}
|
|
give back first_number / second_number;
|
|
}
|
|
}
|
|
|
|
|
|
/// Runs one calculation and gives back whether to keep going.
|
|
function run_calculator() returns Bool {
|
|
|
|
let first_number = when choose_number("First number") {
|
|
succeeds with value: value;
|
|
fails with problem: {
|
|
std.console.println(problem);
|
|
give back true;
|
|
}
|
|
};
|
|
|
|
let chosen_operation = when choose_operation() {
|
|
succeeds with value: value;
|
|
fails with problem: {
|
|
std.console.println(problem);
|
|
give back true;
|
|
}
|
|
};
|
|
|
|
let second_number = when choose_number("Second number") {
|
|
succeeds with value: value;
|
|
fails with problem: {
|
|
std.console.println(problem);
|
|
give back true;
|
|
}
|
|
};
|
|
|
|
when calculate(first_number, chosen_operation, second_number) {
|
|
succeeds with answer: {
|
|
let symbol = match chosen_operation {
|
|
is Operation.Add: "+";
|
|
is Operation.Subtract: "-";
|
|
is Operation.Multiply: "*";
|
|
is Operation.Divide: "/";
|
|
};
|
|
std.console.println("");
|
|
std.console.println("{first_number} {symbol} {second_number} = {answer}");
|
|
}
|
|
fails with reason:
|
|
std.console.println("Problem: {reason}");
|
|
}
|
|
|
|
std.console.println("");
|
|
std.console.print("Another calculation? (y/n): ");
|
|
|
|
let response = when std.console.read_line() {
|
|
succeeds with value: std.text.trim(value).to_lower();
|
|
fails: give back false;
|
|
};
|
|
|
|
give back response == "y" || response == "yes";
|
|
}
|
|
|
|
|
|
/// Entry point — runs the calculator until the user says to stop.
|
|
function main() {
|
|
std.console.println("Legis Calculator");
|
|
std.console.println("----------------");
|
|
|
|
vary running = true;
|
|
|
|
repeat while running {
|
|
running = run_calculator();
|
|
}
|
|
|
|
std.console.println("");
|
|
std.console.println("Goodbye.");
|
|
}
|