diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2d6f2c8 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,26 @@ +name: CI + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + +env: + CARGO_TERM_COLOR: always + RUSTFLAGS: -D warnings + # This is faster for CI: https://github.com/dtolnay/rust-toolchain/issues/26. + CARGO_INCREMENTAL: "0" + +jobs: + check: + name: check + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - uses: actions/checkout@v3 + - uses: rui314/setup-mold@v1 + - name: Install Rust + run: rustup show && rustup install nightly && rustup component add rustfmt --toolchain nightly-x86_64-unknown-linux-gnu # Nightly is needed for our configuration of cargo fmt + - run: cargo check --all-targets --all-features + - run: cargo test --all-targets --all-features diff --git a/.gitignore b/.gitignore index 0512e2d..8ad143a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ target/ +a.out # Rust and Cargo **/*.rs.bk diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..437765f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,251 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "clap" +version = "4.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "oac" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "qbe", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "qbe" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3efb76986096e19177e4e4a1c0c0aa6c313439286ebf3762dcc1526522f2a1" + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a848b85 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +resolver = "2" +members = ["crates/*"] diff --git a/README.md b/README.md index 0ed6e9a..ab31e23 100644 --- a/README.md +++ b/README.md @@ -5,37 +5,6 @@ width="30%">

-

- Travis CI status - - - BSD-3 Clause license - - - - Last release - - - - Gitter chat - - - - IRC channel on Freenode - - - -

# Ousia diff --git a/oac/Cargo.lock b/crates/oac/Cargo.lock similarity index 100% rename from oac/Cargo.lock rename to crates/oac/Cargo.lock diff --git a/oac/Cargo.toml b/crates/oac/Cargo.toml similarity index 100% rename from oac/Cargo.toml rename to crates/oac/Cargo.toml diff --git a/oac/src/main.rs b/crates/oac/src/main.rs similarity index 100% rename from oac/src/main.rs rename to crates/oac/src/main.rs diff --git a/oac/src/parser.rs b/crates/oac/src/parser.rs similarity index 100% rename from oac/src/parser.rs rename to crates/oac/src/parser.rs diff --git a/oac/src/qbe_backend.rs b/crates/oac/src/qbe_backend.rs similarity index 100% rename from oac/src/qbe_backend.rs rename to crates/oac/src/qbe_backend.rs diff --git a/oac/src/resolver.rs b/crates/oac/src/resolver.rs similarity index 100% rename from oac/src/resolver.rs rename to crates/oac/src/resolver.rs diff --git a/oac/src/scanner.rs b/crates/oac/src/scanner.rs similarity index 100% rename from oac/src/scanner.rs rename to crates/oac/src/scanner.rs diff --git a/oac/src/test_files/hello.oa b/crates/oac/src/test_files/hello.oa similarity index 67% rename from oac/src/test_files/hello.oa rename to crates/oac/src/test_files/hello.oa index ecae191..86c12e9 100644 --- a/oac/src/test_files/hello.oa +++ b/crates/oac/src/test_files/hello.oa @@ -5,3 +5,10 @@ fun foobar() { bar = 3 return bar } + +type U32 = struct { + a: U8, + b: U8, + c: U8, + d: U8, +} diff --git a/examples/Color.oa b/examples/Color.oa new file mode 100644 index 0000000..505787c --- /dev/null +++ b/examples/Color.oa @@ -0,0 +1,22 @@ +trait Rgb: $Unit[$Color] = { + let r: $Ratio + let g: $Ratio + let b: $Ratio + + let as | $CMYK = ( + $Cmyk ([R1,G1,B1] map (X -> (1-X-K)/(1-K)),K) + ) where { + let (R1, G1, B1) = ($R, $G, $B) map (_/255) + let K = 1 - (($R1, $G1, $B1) max) } + + as $HSL = { + let R1,G1,B1 = [R,G,B] map (_/255) + set MAX = [R,G,B] max + set MIN = [R,G,B] min + set H,S,L = (MAX+MIN) / 2 + if MAX == MIN then { + H,S = 0 } + else { + let DIFF = MAX-MIN }} + + let gray = ($R*0.299 + $G*0.587 + $B*0.114) as $Ratio } diff --git a/examples/Maze.oa b/examples/Maze.oa new file mode 100644 index 0000000..eb2244b --- /dev/null +++ b/examples/Maze.oa @@ -0,0 +1,51 @@ +# Maze.oa +# ======= +# +# A graph-based representation of mazes with multiple generation and solving +# algorithms. + +use oa.Graph +use oa.Matrix +use oa.random + +trait Maze { + + # See [https://en.wikipedia.org/wiki/Graph_(discrete_mathematics)#Simple_graph]: + # A simple graph is an undirected graph without multiple edges or loops. + # In a simple graph the edges form a set (rather than a multiset) and + # each edge is an unordered pair of distinct vertices. + let graph: $Graph; $graph is-simple + let p0: $Node; $Graph contains $p0 + let p1: $Node; $Graph contains $p1 + + let is-unicursal: $Bool = $graph is-linear + let is-perfect: $Bool = $graph is-not-tree + + let solve = { + if $is-unicursal { + $Graph path + } elif $is-perfect { + $Graph breadth_search + } else { + $Graph breadth_search }} + + let solution-by-following-right-wall: $Edges sort = { + $Node map (_ edges sort) fold 'preorder as 'stream } + + let random-solution = $p0 edges random.choose match ( + $p1 => (), + _ => $p0 ++ $bruteforce )} + +$Maze from-matrix ($Matrix by-growing-tree (_ (-1))) + +crate Maze { + + let by-growing-tree | x: $Nat + | y: $Nat = { + let grid = $Matrix (2, ($x, $y)) + let cells = ($grid nodes random.choose) + let walls = () + $Cells.size match { + 0 => $Cells pop $Cell, + _ => , + }}} diff --git a/examples/Sudoku.oa b/examples/Sudoku.oa new file mode 100644 index 0000000..2dccc5e --- /dev/null +++ b/examples/Sudoku.oa @@ -0,0 +1,15 @@ +use oa.Matrix + +trait Digit: 1 <= $Num <= 9 + +trait Sudoku = { + let grid: $Matrix [$Digit+$Void] size == (9, 9) + + let solve: $Matrix[$Digit] + + let is-solved = { + $Grid rows as $Set size == 9 + $Grid cols as $Set size == 9 + $Grid groups map (_ as set) + } +} diff --git a/examples/Syntax.oa b/examples/Syntax.oa new file mode 100644 index 0000000..e250081 --- /dev/null +++ b/examples/Syntax.oa @@ -0,0 +1,10 @@ +# Syntax.oa +# ========= +# +# Monadic parser combinators. + +trait Syntax: $Monad + = ( +) + +trait Tcp: $Read + $Write ( + ) diff --git a/examples/Tree.oa b/examples/Tree.oa new file mode 100644 index 0000000..6a86527 --- /dev/null +++ b/examples/Tree.oa @@ -0,0 +1,14 @@ +algdata Leaf := () +algdata BTree := + +module Application.Tree { + +} + +trait Tree { + +} + +trait Leaf: $Tree | $Node { + +} diff --git a/examples/bogosort.oa b/examples/bogosort.oa new file mode 100644 index 0000000..ce853d6 --- /dev/null +++ b/examples/bogosort.oa @@ -0,0 +1,16 @@ + +use std.Iter + +|> Iter.take(40) |> Iter.collect + +fun bogosort(list: Seq) { + +} + +let bogosort | list: Iter(Sort) = { + $list (if ($list is-not-sorted) 'shuffle) } + + 'shuffle |> Iter.take() + + loop, while: +} diff --git a/examples/chess/Chess-Notation.oa b/examples/chess/Chess-Notation.oa new file mode 100644 index 0000000..e69de29 diff --git a/examples/chess/Clock.oa b/examples/chess/Clock.oa new file mode 100644 index 0000000..e69de29 diff --git a/examples/chess/Fen.oa b/examples/chess/Fen.oa new file mode 100644 index 0000000..8e7389f --- /dev/null +++ b/examples/chess/Fen.oa @@ -0,0 +1,13 @@ +trait Fen { + let from | STR: $String = { + let fen-chessboard + , fen-player-to-move + , fen-castling + , fen-en-pessant + , fen-halfmove + , fen-move-num = $STR split " " + $Chessboard { + let player-to-move = $player-to-move + } + } +} diff --git a/examples/chess/Game.oa b/examples/chess/Game.oa new file mode 100644 index 0000000..44db48d --- /dev/null +++ b/examples/chess/Game.oa @@ -0,0 +1,167 @@ +# Game.oa +# ======= + +use oa.Matrix + +trait Char { + let as Piece +} + +trait Square | $Coordinate { + let as | $Char = $Piece map (_ as $Char) or ' ' +} + +use Chessboard => $Matrix [$Piece + $Void] size == (8, 8) + +trait Chessboard: $Convert [$String] = { + let from | $Fen = { + } + } + +trait Game = { + use Chessboard => $Matrix [$Piece + $Void] size == (8, 8) + + let chessboard: $Chessboard = $Chessboard from ($Fen ...) + let white: $Player + let black: $Player + let player-to-move: $WHITE+$BLACK = $WHITE + let history: $History + let result: $Future [$Result] + + let result: $Future [$Result] + + let setup = $rewind @0 + + let attempt-move | move: $Move = { + if ($legal-moves contains $move) { + } else { + + } + } + + let rewind = { + $History scan-l () + } + + let after | move: $Move + |: $legal-moves contains $move = { + if $move is-castling { + $players-allowed-to-castle + + $player-to-move + } else { + $board [$move 0] = () + $board [$move 1] = $move piece }}} + +trait Board: $Format = { + let show = { + + } } + +trait Move = $Coordinate => $Coordinate + | $Castling + | $Promotion = { + } + +trait Move: $From[$String] = { + +} + +trait Chess-Notation: $Convert [$String, $Move] | $Game + +trait Algebraic: $Chess-Notation { + let as | $String = { + } +} + +trait Descriptive: $Chess-Notation { + let as | $String = { + $Chessboard @ ($Move destination) piece! + } + + let from | $String = { + }} + +trait Figurine: $Chess-Notation { + let as | $String = { + } + +trait Standard: $Chess-Notation { + let as | $String = { + } + + let from | $Move = { + }} + +trait Custom: $Chess-Notation = { +} + +trait Promotion: $Move { + trait piece = $Rook + | $Knight + | $Bishop + | $Queen +} + +trait History { + let moves: $Coordinate => $Coordinate = () + + let number-of-moves-without-capture-or-pawn-move = { + $rewind-board reverse } + + let rewind-board = $Game board fold ($Game, _ after _) +} + +trait Game ( board: $Board + , history: $History + , white: $Player + , black: $Player ) + +trait Clock { + let delay: $Duration + let increment: $Duration + + let press = { }} + +: $Clock { + let delay + } + +trait Clock + +trait Player { + let clock: $Clock + + let is-allowed-to-castle = $Game moves by $Player contains ( + _ piece: $King + ) + + let move: $Move + + let accept-draw: $Bool + + let offer-draw + + let resign +} + +trait Move = ( + offer-draw = $Bool, +) + + +crate Game { + let from +} + +trait Result = { + let score = 'Win | 'Lose | 'Draw + let cause = } + +trait Cause = 'Resignation + | 'Checkmate + | 'Unknown + +trait Evaluation | $Game { + +} diff --git a/examples/chess/Piece.oa b/examples/chess/Piece.oa new file mode 100644 index 0000000..7a9f467 --- /dev/null +++ b/examples/chess/Piece.oa @@ -0,0 +1,33 @@ +use chess.Game + +trait Piece | $Game = { + let color: $Player + let range + + + let as | $Char +} + +trait Bishop: $Piece { + let range = $chessboard filter ( + c => $position zip $c map '- )} + +trait Bishop: $Piece { + let range = $chessboard filter ( + c => $position zip $c map '- )} + +trait Rook: $Piece | $Game { + let range = $chessboard filter ( + c => $position zip $c map '== )} + +trait Queen: $Piece | $Game { + let range = $chessboard filter ( + c => $position zip $c map '- )} + +trait King: $Piece { + let range = (-1..2, -1..2) + } + +trait Knight: $Piece { + let range = (1..5) map ($c ) +) diff --git a/examples/chess/Player.oa b/examples/chess/Player.oa new file mode 100644 index 0000000..93cd4de --- /dev/null +++ b/examples/chess/Player.oa @@ -0,0 +1,17 @@ +trait Player { + let clock: $Clock + + let is-allowed-to-castle = $Game moves by $Player contains ( + _ piece: $King + ) + + let move: $Move + + let accept-draw: $Bool + + let offer-draw + + let resign +} + + diff --git a/examples/chess/Result.oa b/examples/chess/Result.oa new file mode 100644 index 0000000..e69de29 diff --git a/examples/chess/engine/Cgp.oa b/examples/chess/engine/Cgp.oa new file mode 100644 index 0000000..720be29 --- /dev/null +++ b/examples/chess/engine/Cgp.oa @@ -0,0 +1,16 @@ +# Cgp.oa +# ====== +# +# Provides a Cartesian Genetic Programming (CGP) framework. + +trait Genome | $Fitness { + let genes: $Matrix [$Gene] + let fitness: $Genome -> + + let mutate + +} + +trait Points: $Gene { + let time-command +} diff --git a/examples/chess/engine/Engine.oa b/examples/chess/engine/Engine.oa new file mode 100644 index 0000000..ec5ef36 --- /dev/null +++ b/examples/chess/engine/Engine.oa @@ -0,0 +1,19 @@ +# Engine.oa +# ========= +# +# A simple chess engine + +use chess.Player + +trait Engine: $Player { + let patterns: Set[Pattern] + let sum: +} + +trait Database = { + +} + +let opening-book = $Pattern { + let condition = $1 +} diff --git a/examples/chess/setup.fen b/examples/chess/setup.fen new file mode 100644 index 0000000..f9c9ce2 --- /dev/null +++ b/examples/chess/setup.fen @@ -0,0 +1 @@ +rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 diff --git a/examples/equations.oa b/examples/equations.oa new file mode 100644 index 0000000..75e159a --- /dev/null +++ b/examples/equations.oa @@ -0,0 +1,3 @@ +let solve | a: $Num, b: $Num, c: $Num = { + (-$b ± ($b^2 - 4*$a*$c) sqrt) / (2*$a) +} diff --git a/examples/example.oa b/examples/example.oa new file mode 100644 index 0000000..ba8cc10 --- /dev/null +++ b/examples/example.oa @@ -0,0 +1,413 @@ +use oa.collections.* + +// I want a language where information flows like inside pipes. This is why +// postfix syntax is used all over. The language must rely heavily on +// compilation efforts and have little noise. All the code is just there. +// +// Factoids: +// - use elegant English +// - languages distinguish between methods and functions. They are the same +// thing in Ousia, you just apply them differently. It's very functional. +// - Function input type checking is very useful. If you ask for something, the +// compiler will try to provide that for you and will give error only if unable +// to give you such. +// - End of statement inference using whitespace +// - readable +// - maybe null termination checker? I can still use infinite loops in special +// places, but only if I specify so! +// - terms can be of many types. Types are sets of terms. I ship with many +// operators that, similarly to Haskell and other languages, can create new +// types from existing types or from the ground up. +// - Structural type system. I don't care if two libraries use different types, +// if they do the same thing I should be able to use them. +// - effective type system that can prove basically anything, but also flexible +// and doesn't get in the way. You prove what you want, nothing more. +// +// # is character +// . is a namespace operator +// , forms lists +// ? is a special syntax for anonymous functions. Can be used almost anywhere +// because its behavior is standardized. + +// Indentation IS significant, but it's only used for line continuations. +// +// Comptime evaluation IS the key. + +type RubiksCubeFace := enum { + Up, + Bottom, + Right, + Left, + Down +} + +def sort(l) { + +} + +class Person { + const +} + +trait Person: News { + let +} + +def sort(l: Iter[Subreddit]) + +type Bool = enum { + True, + False +} + +type Person = struct { + age: u32 +} + +type U64 = Int & (_ in (0..2^64)) +type Bool = True | False +type Maybe[T] = Some[T] | None + +struct Family = { + mother: Person, + father: Person, + child: Person.constraint(x => age < $mother age & ? age < $father age), +} + +struct List[T] = + element: T, + next: Maybe[List[T]], + +struct Person = + name: String, + birth: Date, + nationality: Array[String], + mother: Person, + father: Person, + +let GREETINGS := "Hello" + , "Hola" + , "Bonjour" + , "Ciao" + , "こんにちは" + , "안녕하세요" + , "Cześć" + , "Olá" + , "Здравствуйте" + , "Chào bạn" + , "您好" + , "Hallo" + , "Hej" + , "Ahoj" + , "سلام" + +printl["The answer is {}"] + +$engine dispatch_call [method] + +greetings reverse map (? ++ " ") flatten; +GREETINGS match ( + "Hello" ++ ? => STDIN ++ (? - "Hello"), + ? => "error" ) + +f ++ g; + +let euler (range: Range, seq: Iter[?]) + +GREETINGS map [io.printl]; + +for n? in seq {} + +let evens := seq .slide[2, 1] .map[_[0]]; +let odds := seq .slide[2, 1] .map[_[1]]; + +for ever { + $Welcome_msg print +} + +{ + 1000 + Range (1, .) + . is_even & . is_odd +} + +EULER = (1 1000) filter ([$n%3==0 or $n%5==0]) sum + +let euler_2 := range? -> range .map[fibonacci].filter[n? -> n%2==0 and n < 4000000).sum; + + +# Use in 3D and higher for textured image synthesis are +# covered by U.S. Patent 6,867,776, if the algorithm is implemented using the +# specific techniques described in any of the patent claims. +let simplex-noise | grid: $Matrix = { + let skew | coords = { + let skewness = (($coords size) sqrt - 1) / ($coords size) + $coords map (_ + ($coords sum * $skewness)) } + let simplicial-subdivision = { + } +} + +let factorial := + 0 -> 1, + n? -> n * factorial[n-1]; + +let is_prime := n? -> { + 600851475143 int.factors size == 2 +} + +fn euler_4 { + (1..20) math.lcm +} + +fn euler_6 { + (1..100) map (_^2) sum - ((1..100) sum) ^ 2 +} + +fn euler_7 { + use math.PRIMES + $PRIMES@10001 +} + +fn eurler_8 { + $bigass_n as $string triade (13,13) map (_ product) max +} + +fn euler_9 { + +} + +KEYBOARD match + +("Qwerty" => 1, + "Ok" => 0, + "Bah" => -4, + -1 +) +. at .Keyboard + +(1..100) map [fizzbuzz] + +USERS sort map [cart] filter ]total_price + map [with_discount < $MAXIMUM_PRICE] + +(f ++ g) + +$Functions map reduce 'foldr filter _ total_price + +let is_sorted = $list slide (2,1) map (_ <= _) reduce 'and; + +flatten = flatr '++ + +for X in LIST + 3 times do { + +} + +if condition then { + printf [this_obj] +} else { + $abc +} + +if $condition then ... else ... +if X%2 == 0 then while X>3 do i++ + +while $condition {} +do {} while (condition) +do {} until (condition) +for e in $sequence, + i from $x + 2 by 3 do +{ + rst } + +for 'x in $List +for N from X by J +for N min_of SEQUENCE +for N max_of SEQUENCE + +for N from_X by I $STOP; +for N from_X to Y $STOP; + +import YUCK.EEK as AHEM + +import FMT.BLAH.DUH.EUREKA as YIKES +import FMT.AMEN as BINGO +import FMT.JEEZ.OUCH + +2340 u.kilo.metres / 977 u.N + +[undef] def do_something +def yuck = "Hello, world!" +[curious] + +[1,2,abc:$XYZ] + +'(add, 2, (add, 1, 3)) ecl.run + +(1,2,3) sort (order := ? > ?, algo := MERGESORT) + +(1,2,3,4,5,6,7,8,9,10) map (2 * ?) filter (? % 3 == 0) reduce Int.sum; + +('a => ('b,1,2)) as $TREE + +n d faces map (? min) + +let 2D6 = (1..6) zip (1..6) probability_of (_+_ == 2 volume_up) + +1..1000 filter (? is_prime) map (2 * ?) enum +list filter (? != 4) +[x for x in list if x != 4] + +printl "Hello, world!" +[jukebox] queue += [songs] filter (? by "John Williams") +Jukebox volume + +1,2,3 map (? + random:Double) + +println "Hello, world!" + +let dispatch_call(Method: STRING, Params: JSON) = + ( "status" => $ call_status Params + , "init" => $ call_init Params + , "exit" => $ call_exit Params + , "status" => $ call_status Params + , ? => JSONRPC_INVALID_METHOD ) @ $params + +"Hello, world".print +"Hello, world!" split $Space map (? capitalize ++ " ...Filippo.") +"hello" reverse ++ SEQ +? +$list filter (_%2 == 0) map (_+2) +false ? (1,0) + +set x = 2 + +let y = 3 +set x = y + +$Users map (? name) + +$Context ("api.google.com", "user:pippoc") http.post + +struct Node = + children: Array + +type Tree = + +data Tree = $LEAF || ($LNODE && $RNODE) +"x" upper +$f · $g +assert 1 +raise $INDEX_OUT_OF_BOUNDS +1..100 + +class TREE (let NODES) { + + def if (CONDITION) then (X) else (Y) = { + + } + + def UNTIL (let CONDITION) {CODE} = while (condition!) (code) + + def travel(distance) = DISTANCE as RGB + +$tree parent +$tree pedigree +$tree siblings +$tree children +$tree is_leaf +$tree is_root + +# Primitives + +"Hello, world!" # string +"x" # char +10 +2.75 +0xFF +0b11001 +0o1374 # numbers +0x24FA5 +(1,2,3) # list +(1: 'x', 2: 'y', 3: 'z') # hashmaps +true, false # bool + +def @ (funct, arg) = funct (arg) + +def add_vector (a,b) = Vectorize ("float32", "float32") {2+2} + +arr = (true) + +if $condition do {...} else {...} + +let pi = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170... +let euler = 2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251 +let golden_ratio = (5 sqrt + 1) / 2 + +class Generative(X) { + + let MAP(F) = GENERATIVE (F compose X) + let FILTER(P) = GENERATIVE (if P(X) then P else X) +} + +val condition = !true +val condition = true! and false! +if not cargo contains box { + +} + +# range + +# Numbers +$n + $m +$n - $m +$n * $m +$n / $m +$n % $m +$n ^ $m +$n < $m +$n > $m +$n >= $m +$n <= $m +$n compare $m +$n sqrt +$n cbrt + +# Bool +x and y +x or y +x ! +x xor y + +# Lists +(1, 2, 3) + +expand LIST.[] {RANGE (X,Y) => $list from X to Y} + +# Sequences +list contains element +list filter (? < 2) +list fold '++ +list @ (0..4) +list @ (-1) +list enum +list reverse +list head +list [-1] +list size +list map +list tail +list flat_map +list flatten +list range +list sort (SORTING_ALGO_QUICKSORT) +list zip $that_list +list shuffle $rnd + +# Hash maps +$hash keys +$hash values + +enum COLORS = Red, Blue, Green + +Math_Symbolic {√2 + 2} diff --git a/examples/factorial.oa b/examples/factorial.oa new file mode 100644 index 0000000..24770b2 --- /dev/null +++ b/examples/factorial.oa @@ -0,0 +1,33 @@ +module Examples.Factorial { + use std.Range + use std.Iter + + let factorial := n -> range(1, n).to_iter() |> Range::to_iter |> Iter::foldl(UInt::multiply, 1); + [pure][contexpr] + let factorial := + let inner := + (0, c?) -> c, + (n?, c?) -> inner[n - 1, c * n]; + 0 -> 1, + n? -> inner[n, 1]; + + let factorial.match := + let inner := i?, factorial?, n? -> (factorial cmp n) |> ( + Sign.Lt -> inner[i+1, factorial * i, n], + Sign.Gt -> False, + Sign.Eq -> True, + ); + n? -> inner[0, 1, n]; + +} + +module Examples.Routes { + let router := + methods.POST, "/v0/users", routes.create_user, + + let v0_routes := + (m.POST, "/users"), + (m.GET, "/users", users_index), + (m.PATCH | m.UPDATE, "/users", users_update), + (m.DELETE, "/users", users_delete), +} \ No newline at end of file diff --git a/examples/fibonacci.oa b/examples/fibonacci.oa new file mode 100644 index 0000000..96ba4a8 --- /dev/null +++ b/examples/fibonacci.oa @@ -0,0 +1,5 @@ +let fibonacci := + 0 -> 0, + 1 -> 1, + factorial[x?] -> x?; + n? -> fibonacci[n-1] + fibonacci[n-2]; \ No newline at end of file diff --git a/examples/fizzbuzz.oa b/examples/fizzbuzz.oa new file mode 100644 index 0000000..fe6093b --- /dev/null +++ b/examples/fizzbuzz.oa @@ -0,0 +1,10 @@ +module Example.Fizzbuzz { + + let fizzbuzz := n? -> { + let map := (0, 0) -> "fizzbuzz", + (0, _) -> "fizz", + (_, 0) -> "buzz", + (_, _) -> Int.String::from(n); + map[n%3, n%5]; + } +} diff --git a/examples/hello-world.oa b/examples/hello-world.oa new file mode 100644 index 0000000..a68de43 --- /dev/null +++ b/examples/hello-world.oa @@ -0,0 +1,3 @@ +mod Application.Main { + std::IO::println "Hello, world!" +} diff --git a/examples/id.oa b/examples/id.oa new file mode 100644 index 0000000..314310e --- /dev/null +++ b/examples/id.oa @@ -0,0 +1 @@ +let id := x? -> x; diff --git a/examples/is-sorted.oa b/examples/is-sorted.oa new file mode 100644 index 0000000..2defd54 --- /dev/null +++ b/examples/is-sorted.oa @@ -0,0 +1,8 @@ +#use oa.math.Order + +#let is-sorted | list: $Iter[$Order] = { +# $list slide (2,1) map (_<=_) reduce 'and } + +function foo bar { + foo +} diff --git a/examples/matrix-multiplication.oa b/examples/matrix-multiplication.oa new file mode 100644 index 0000000..f21330b --- /dev/null +++ b/examples/matrix-multiplication.oa @@ -0,0 +1,12 @@ +use oa.Matrix + +let * | x: $Matrix[$Num] x == $n + | y: $Matrix[$Num] y == $n + | $x x == $y y = { + $x zip ($y transpose) map (_ zip _ map (_ reduce '*) reduce '+)) } + +module Matrix { + + # n: UInt, m: UInt, a: Tensor[n, m], b: Tensor[m, n] -> Tensor[n, n] + fn mul(a, b) := unimplemented!() +} \ No newline at end of file diff --git a/examples/oa/core/reader.oa b/examples/oa/core/reader.oa new file mode 100644 index 0000000..8d91d64 --- /dev/null +++ b/examples/oa/core/reader.oa @@ -0,0 +1,4 @@ +trait Cst | src: String { +} + +trait Syntax: $Language = diff --git a/examples/quicksort.oa b/examples/quicksort.oa new file mode 100644 index 0000000..0e45d90 --- /dev/null +++ b/examples/quicksort.oa @@ -0,0 +1,3 @@ +let quicksort | Seq: $Iter[$Sort] = $Seq match ( + _ :: $x => $Seq partition (_ <= $x) map 'quicksort patch (1, $x) flat, + _ => $Seq, ) \ No newline at end of file diff --git a/examples/repl.oa b/examples/repl.oa new file mode 100644 index 0000000..0600320 --- /dev/null +++ b/examples/repl.oa @@ -0,0 +1,6 @@ +use oa.repl.REPL + +$REPL import 'cpu-num +$REPL optimization-level = 3 + +$REPL highlight = $0 diff --git a/examples/request.oa b/examples/request.oa new file mode 100644 index 0000000..cd003db --- /dev/null +++ b/examples/request.oa @@ -0,0 +1 @@ +let wri-homepage = $Url "https://wri.org" get diff --git a/examples/roman.oa b/examples/roman.oa new file mode 100644 index 0000000..4119686 --- /dev/null +++ b/examples/roman.oa @@ -0,0 +1,28 @@ +mod RomanNum { + + [contexpr] + string2int(str: Str) := + values = 'I' => 1, + 'V' => 5, + 'X' => 10, + 'L' => 50, + 'C' => 100, + 'D' => 500, + 'M' => 1000 + str + $str fold (0, _ + $VALUES[_]) + $str slide 4 map (_ as $Set size < 4) reduce 'all + $str slide 2 map (_ sort fold '/ < 10) reduce 'all + + 2int(n: UInt) := { + str + |> Str.chars + |> Iter.slide 2, 1 + |> Iter.map a, b => {values @ a ++ values @ b} + } +} + +mod RomanNum: { + fun (str: Str) [ +} + diff --git a/examples/sort.oa b/examples/sort.oa new file mode 100644 index 0000000..914a1d6 --- /dev/null +++ b/examples/sort.oa @@ -0,0 +1,6 @@ +use oa.Iter +use oa.Order + +trait Sort: $Iter[$Order] { + $List branch (_ map (x => x.first-name ++ x.last-name)) sort merge +} diff --git a/examples/swap.oa b/examples/swap.oa new file mode 100644 index 0000000..fbbd738 --- /dev/null +++ b/examples/swap.oa @@ -0,0 +1,4 @@ +let x = "foo" +let y = "bar" + +let x, y = y, x diff --git a/examples/tokens.txt b/examples/tokens.txt new file mode 100644 index 0000000..edd6db0 --- /dev/null +++ b/examples/tokens.txt @@ -0,0 +1,3 @@ +fn foobar(12312) { + "foobar" +} diff --git a/examples/trait.oa b/examples/trait.oa new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/trait.oa @@ -0,0 +1 @@ + diff --git a/examples/units.oa b/examples/units.oa new file mode 100644 index 0000000..197ef83 --- /dev/null +++ b/examples/units.oa @@ -0,0 +1,37 @@ +use u => oa/units + +let Newton = $u.metre / $u.second^2 +let mile = 1609.344 u.metre +let nautical_mile = 1852 u.metre + +$u.metre as $u.shìzhì. u + +let minute = 60 u.second +let hour = 60 u.minute +let day = 24 u.hour + +crate shìzhì = { + let háo = $u.chǐ / 10000 + let lí = $u.chǐ / 1000 + let fēn = $u.chǐ / 100 + let cùn = $u.chǐ / 10 + let chǐ = $u.metre / 3 + let zhàng = 10 u.chǐ + let yǐn = 100 u.chǐ + let lǐ = 1500 u.chǐ + + let cuō = Unit.millilitre + let sháo = Unit.centilitre + let gě = Unit.decilitre + let shēng = Unit.litre + let dǒu = Unit.decalitre + let dàn = Unit.hectolitre +} + +let knot = $u.nautical-mile / $u.hour + +64 u.mebibyte as $u.kilobyte +$u.second +42 u.Newton + +trait Unit | x: $Num diff --git a/examples/use.oa b/examples/use.oa new file mode 100644 index 0000000..3258ce3 --- /dev/null +++ b/examples/use.oa @@ -0,0 +1,3 @@ +use Spam +use Foo.Bar +use Spam => Foo.Fish diff --git a/ousia-demo.oa b/ousia-demo.oa new file mode 100644 index 0000000..9b62fa2 --- /dev/null +++ b/ousia-demo.oa @@ -0,0 +1,164 @@ +// This is a comment. +// +// Ousia does not support multi-line comments. See +// +// for a good explanation. + +// Ousia source files can contain arbitrary logic that is interpreted by the +// compiler during build time. + +//. This is a doc comment. It's a statement, like any other. Not magic. So you +//. need to compile the program to read the comments. + +if env["OUSIA_DEBUG"] != "1", do: { + println "Ousia is not in debug mode." + exit 1 + // The problem with the above is that the compiler doesn't know when to + // recompile it. Should it rerun every time? +} + +print "Hello, world! My name is {name}" +// `printnl` is just like `print`, but without a newline at the end. +printnl "Hello, world!\n" + +type Vec[T] = struct { + len: U64 + capacity: U64 + data_ptr: Usize +} + +type String = Vec[U8] + +// Many Ousia language constructs reuse the same syntax, which represents +// something resembling a Lua table: a sequence and a map, together. +if true, do: { + print "true" +}, else: { + print "false" +} + +// Square brackets create a Vector by default. +let vec = [1, 2, 3] +for x, in: vec, do: { + print x +} +// ...becomes: +let vec = [1, 2, 3] +while i < vec.len, do: { + { + let x = vec[i] + print x + } + i += 1 +} + +while x < 10, do: { + print x + x = x + 1 +} + +// Function call use the same syntax as well. This results in a somewhat Lisp-y flavor. +function_call_with_args 1, 2, 3, optional_arg: true +nested_call 1, 2, call 3 + +// n / m returns: +// - None if m == 0 +// - Some(n / m) if m != 0 + +// This function will: +// - Accept any `n` that implements the required subset of the `Int` interface +// (mod operator, equality, conversion from literal). +// - Return a type that implements the subset of `String` required at the +// callsite. The callsite can use up to the full type of the return value, if +// they want. This is, technically speaking, just an optimization, and has +// little effects on the actual type system. +// +// This is a unique feature of Ousia, and we call it "implicit subtyping". +fn fizzbuzz (n: Uint, foo: String = "") -> String = { + // `mod` in this case is an associated constant, which is basically a + // a method. `Int.mod n, 3` would be the same. + // Question: should this function accept a signed integer? + // It probably should, but it's debatable. + switch (n.mod 3, n.mod 5), do: { + (0, 0) -> "fizzbuzz" + (0, _) -> "fizz" + (_, 0) -> "buzz" + _ -> n.to_string + } +} + +type Maybe[T] = enum { + None + Some: T +} + +type Try[T, E] = enum { + Ok: T + Err: E +} + +/// This is a doc comment. +fn product_order(cmps: Iter[Ord]) -> Maybe[Cmp] = { + let min = cmps.min? + let max = cmps.max? + + switch (min, max), do: { + (Cmp.Lt, Cmp.Eq) -> Some Cmp.Lt + (Cmp.Eq, Cmp.Eq) -> Some Cmp.Eq + (Cmp.Eq, Cmp.Gt) -> Some Cmp.Gt + (Cmp.Lt, Cmp.Gt) -> None + _ -> Impossible + } +} + +// A function could be generic over a great number of things: +// - Thread-safety. +// - Async vs sync. +// - Input and output types. +// - Mutability of return references. + +type Point = struct { + x: Int + y: Int +} + +// All Ousia code is defined in relation to its parent module, whether that's +// implicitly defined by the standard library or custom. A module defines the +// implicit dependencies of Ousia code, which usually are: +// - A heap allocator. +// - Exception handling logic. +// - Logging. +// - GC mechanism. + +// Enums are standard tagged unions. Enum variants are types too, which means a +// function can theoretically always return `-> True`. + +type Bool = enum { + False + True +} + +type Person = struct { + age: U16 + name: String +} + +// Ousia has enums, structs, but it also needs a way to represent algebras on +// top of them, like "this struct minus this value", or "sum of these two +// enums". +// - Struct - value -> struct +// - Enum + enum -> enum +// - struct * struct (tuple) -> struct + +test_provider + +u8_wrapper foo, bar, baz, qux, quux, corge, grault, garply, waldo, fred, plugh, xyzzy, thud + +test "this is a fest function" (tmp: Directory, a: U32) { + // The assert macro is smart, and will print the right error message and diff + // depending on the AST of the expression and the value types. + assert foo.contains bar + + assert a == 1 + assert tmp == snapshot +}