Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 81 additions & 3 deletions bin/main.ml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,47 @@ let parse_file path =
Affinescript.Span.pp_short span msg;
`Error (false, "Parse error")

(** Check a file *)
(** Run a file through the interpreter *)
let run_file path =
try
let prog = Affinescript.Parse_driver.parse_file path in
let env = Affinescript.Value.empty_env () in
(* Load stdlib prelude *)
Affinescript.Stdlib.load_prelude env;
Affinescript.Eval.eval_program env prog;
`Ok ()
with
| Affinescript.Lexer.Lexer_error (msg, pos) ->
Format.eprintf "@[<v>%s:%d:%d: lexer error: %s@]@." path pos.Affinescript.Span.line pos.Affinescript.Span.col msg;
`Error (false, "Lexer error")
| Affinescript.Parse_driver.Parse_error (msg, span) ->
Format.eprintf "@[<v>%a: parse error: %s@]@."
Affinescript.Span.pp_short span msg;
`Error (false, "Parse error")
| Affinescript.Eval.Runtime_error (msg, span_opt) ->
(match span_opt with
| Some span -> Format.eprintf "@[<v>%a: runtime error: %s@]@." Affinescript.Span.pp_short span msg
| None -> Format.eprintf "@[<v>runtime error: %s@]@." msg);
`Error (false, "Runtime error")
| Failure msg ->
Format.eprintf "@[<v>error: %s@]@." msg;
`Error (false, "Error")

(** Evaluate an expression from command line *)
let eval_expr expr_str =
let env = Affinescript.Value.empty_env () in
Affinescript.Stdlib.load_prelude env;
match Affinescript.Repl.eval_string ~env expr_str with
| Ok v -> Format.printf "%s@." (Affinescript.Value.show v); `Ok ()
| Error msg -> Format.eprintf "Error: %s@." msg; `Error (false, msg)

(** Start the REPL *)
let repl_run file_opt =
match file_opt with
| Some file -> Affinescript.Repl.run_with_file file; `Ok ()
| None -> Affinescript.Repl.run (); `Ok ()

(** Check a file (type check - placeholder) *)
let check_file path =
let source = read_file path in
let _ = source in
Expand All @@ -68,6 +108,12 @@ open Cmdliner
let path_arg =
Arg.(required & pos 0 (some file) None & info [] ~docv:"FILE" ~doc:"Input file")

let optional_path_arg =
Arg.(value & pos 0 (some file) None & info [] ~docv:"FILE" ~doc:"Input file to load")

let expr_arg =
Arg.(required & pos 0 (some string) None & info [] ~docv:"EXPR" ~doc:"Expression to evaluate")

let output_arg =
Arg.(value & opt string "out.wasm" & info ["o"; "output"] ~docv:"FILE" ~doc:"Output file")

Expand All @@ -81,6 +127,21 @@ let parse_cmd =
let info = Cmd.info "parse" ~doc in
Cmd.v info Term.(ret (const parse_file $ path_arg))

let run_cmd =
let doc = "Run a file through the interpreter" in
let info = Cmd.info "run" ~doc in
Cmd.v info Term.(ret (const run_file $ path_arg))

let eval_cmd =
let doc = "Evaluate an expression" in
let info = Cmd.info "eval" ~doc in
Cmd.v info Term.(ret (const eval_expr $ expr_arg))

let repl_cmd =
let doc = "Start the interactive REPL" in
let info = Cmd.info "repl" ~doc in
Cmd.v info Term.(ret (const repl_run $ optional_path_arg))

let check_cmd =
let doc = "Type check a file" in
let info = Cmd.info "check" ~doc in
Expand All @@ -93,8 +154,25 @@ let compile_cmd =

let default_cmd =
let doc = "The AffineScript compiler" in
let info = Cmd.info "affinescript" ~version ~doc in
let sdocs = Manpage.s_common_options in
let man = [
`S Manpage.s_description;
`P "AffineScript is a systems programming language with affine types, \
dependent types, row polymorphism, and extensible effects.";
`S Manpage.s_commands;
`P "Use $(b,affinescript COMMAND --help) for help on a specific command.";
`S Manpage.s_examples;
`P "Start the REPL:";
`Pre " $(b,affinescript repl)";
`P "Run a file:";
`Pre " $(b,affinescript run hello.afs)";
`P "Evaluate an expression:";
`Pre " $(b,affinescript eval \"1 + 2 * 3\")";
] in
let info = Cmd.info "affinescript" ~version ~doc ~sdocs ~man in
let default = Term.(ret (const (`Help (`Pager, None)))) in
Cmd.group info ~default [lex_cmd; parse_cmd; check_cmd; compile_cmd]
Cmd.group info ~default [
repl_cmd; run_cmd; eval_cmd; lex_cmd; parse_cmd; check_cmd; compile_cmd
]

let () = exit (Cmd.eval default_cmd)
68 changes: 68 additions & 0 deletions examples/enums.afs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Enums and algebraic data types in AffineScript

// Simple enum
enum Color {
Red,
Green,
Blue,
}

// Enum with data (sum type)
enum Option[T] {
None,
Some(T),
}

enum Result[T, E] {
Ok(T),
Err(E),
}

// Using enums with pattern matching
fn describe_color(c: Color) -> String {
match c {
Color::Red => "The color of fire",
Color::Green => "The color of nature",
Color::Blue => "The color of the sky",
}
}

// Option handling
fn safe_divide(a: Int, b: Int) -> Option[Int] {
if b == 0 {
None
} else {
Some(a / b)
}
}

fn show_result(opt: Option[Int]) -> String {
match opt {
Some(x) => "Result: " + str(x),
None => "Division by zero!",
}
}

println(describe_color(Color::Blue));
println(show_result(safe_divide(10, 2)));
println(show_result(safe_divide(10, 0)));

// Result for error handling
enum MathError {
DivisionByZero,
NegativeRoot,
}

fn safe_sqrt(x: Int) -> Result[Int, MathError] {
if x < 0 {
Err(MathError::NegativeRoot)
} else {
// Simplified integer square root
let mut guess = x / 2;
if guess == 0 { guess = 1; }
while guess * guess > x {
guess = (guess + x / guess) / 2;
}
Ok(guess)
}
}
19 changes: 19 additions & 0 deletions examples/factorial.afs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Factorial calculation in AffineScript

fn factorial(n: Int) -> Int {
if n <= 1 { 1 }
else { n * factorial(n - 1) }
}

// Using fold for a functional approach
fn factorial_fold(n: Int) -> Int {
if n <= 1 { 1 }
else {
fold(\acc: Int, x: Int -> acc * x, 1, range(1, n + 1))
}
}

println("Factorials:");
for i in range(1, 11) {
println(str(i) + "! = " + str(factorial(i)));
}
36 changes: 36 additions & 0 deletions examples/fibonacci.afs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Fibonacci sequence in AffineScript

// Recursive implementation
fn fib_recursive(n: Int) -> Int {
if n <= 1 { n }
else { fib_recursive(n - 1) + fib_recursive(n - 2) }
}

// Iterative implementation (more efficient)
fn fib_iterative(n: Int) -> Int {
if n <= 1 { n }
else {
let mut a = 0;
let mut b = 1;
let mut i = 2;
while i <= n {
let temp = a + b;
a = b;
b = temp;
i += 1;
}
b
}
}

// Print first 10 Fibonacci numbers
println("Fibonacci sequence (recursive):");
for i in range(10) {
println(str(fib_recursive(i)));
}

println("");
println("Fibonacci sequence (iterative):");
for i in range(10) {
println(str(fib_iterative(i)));
}
7 changes: 7 additions & 0 deletions examples/hello.afs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Hello World in AffineScript

fn main() {
println("Hello, AffineScript!");
}

main()
33 changes: 33 additions & 0 deletions examples/higher_order.afs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Higher-order functions in AffineScript

// Map: transform each element
let numbers = [1, 2, 3, 4, 5];
let doubled = map(\x: Int -> x * 2, numbers);
println("Doubled: " + str(doubled));

// Filter: keep elements matching predicate
let evens = filter(\x: Int -> x % 2 == 0, numbers);
println("Evens: " + str(evens));

// Fold: reduce to single value
let sum = fold(\acc: Int, x: Int -> acc + x, 0, numbers);
println("Sum: " + str(sum));

// Composition example
fn compose(f: fn(Int) -> Int, g: fn(Int) -> Int) -> fn(Int) -> Int {
\x: Int -> f(g(x))
}

let add_one = \x: Int -> x + 1;
let times_two = \x: Int -> x * 2;
let add_then_double = compose(times_two, add_one);

println("(5 + 1) * 2 = " + str(add_then_double(5)));

// Pipeline pattern
let result = numbers
|> map(\x: Int -> x * x) // Square each
|> filter(\x: Int -> x > 5) // Keep > 5
|> fold(\a: Int, x: Int -> a + x, 0); // Sum

println("Sum of squares > 5: " + str(result));
35 changes: 35 additions & 0 deletions examples/records.afs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Records and pattern matching in AffineScript

// Define a person record
struct Person {
name: String,
age: Int,
email: String,
}

// Create some records using anonymous record syntax
let alice = { name: "Alice", age: 30, email: "alice@example.com" };
let bob = { name: "Bob", age: 25, email: "bob@example.com" };

println("Person: " + alice.name + ", age " + str(alice.age));

// Record update syntax
let older_alice = { ..alice, age: alice.age + 1 };
println("After birthday: " + older_alice.name + " is " + str(older_alice.age));

// Pattern matching on records
fn greet(person: { name: String, ..r }) -> String {
"Hello, " + person.name + "!"
}

println(greet(alice));
println(greet(bob));

// Row polymorphism - function works with any record that has 'name' field
fn get_name(r: { name: String, ..rest }) -> String {
r.name
}

let company = { name: "Acme Corp", founded: 1990 };
println("Company: " + get_name(company));
println("Person: " + get_name(alice));
Loading
Loading