Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
/ Tap Public

An unremarkable but Polished interpreted programming language.

Notifications You must be signed in to change notification settings

MKJM2/Tap

Repository files navigation

Tap

An unremarkable but deliberately Polished interpreted programming language. Originally built as a personal project to learn C++20 (CMake, Gtest, and other ritual sacrifices), it has since been rewritten. Currently in its infancy.

The short term goal is to solve all Advent of Code problems using Tap. There is no long term goal. The purpose of this project was and remains education and fun.

Current state of affairs:

  • bilingual keywords (English + Polish aliases for all keywords) πŸ‡΅πŸ‡±
  • a slow tree walking interpreter
  • rudimentary runtime type checking
  • hand rolled lexer & parser
  • basic closures & lexical scoping

but... why?

Because I can. Also it's fun. Lexing and parsing are already solved problems (see the excellent logos crate for creating lexers, or nom creating parsers. People older to the trade are surely familiar with the GNU Bison parser generator).

Planned features

  • type inference
  • emitting byte code + a VM implementation
  • potentially looking into JIT compilation (but nothing too serious, I have a life)
  • a faster, state machine based lexer (DFA)
    • might require edits to the Tap grammar
    • perfect hashing for keywords?
    • cache friendly token storage

Tap syntax examples

Check out the WIP EBNF Grammar

// Type Declarations
// Sum type with variants
type Option = Some(int) | None;
// Sum type with multiple variants
type Result = Ok(int) | Error(string);
// Record type
type Point = { x: int, y: int };
// Generic type usage
type IntList = [int];
// Variable Bindings
// Immutable variable
x: int = 42;
// Immutable variable with type inference
name = "Alice";
// Mutable variable
mut counter: int = 0;
// Function Definitions
// Function with parameters and return type
add(a: int, b: int): int = {
 a + b
}
// Function with block body and statements
factorial(n: int): int = {
 mut result = 1;
 mut i = 1;
 while (i <= n) {
 result *= i;
 i += 1;
 }
 result
}
// Function using records
distance(p1: Point, p2: Point): float = {
 dx = p1.x - p2.x;
 dy = p1.y - p2.y;
 (dx * dx + dy * dy)
}
// Control Flow
// If expression
max(a: int, b: int): int = {
 if (a > b) {
 a
 } else {
 b
 }
}
// While loop
print_numbers(n: int): int = {
 mut i = 0;
 while (i < n) {
 i += 1;
 }
 i
}
// For loop
for i in [1, 2, 3, 4, 5] {
 print(i);
}
for fruit in fruits {
 print(fruit); // "apple" "banana" "cherry"
}
// Pattern Matching
// Match expression with patterns
unwrap_or(opt: Option, default: int): int = {
 match (opt) {
 | Some(value) => value,
 | None => default
 }
}
// Match with multiple patterns
handle_result(res: Result): string = {
 match (res) {
 | Ok(val) => "Success",
 | Error(msg) => msg
 }
}
// Lambda Expressions
// Lambda with type annotation
map_fn = (f: int -> int, lst: [int]): [int] => {
 lst
}
// Lambda without type annotation
simple_lambda = (x: int) => x + 1;
// Operators and Expressions
compute(): int = {
 // Arithmetic operators
 a = 10 + 5;
 b = 20 - 3;
 c = 4 * 7;
 d = 15 / 3;
 // Comparison operators
 is_equal = (a == 15);
 is_not_equal = (b != 10);
 is_less = (c < 30);
 is_greater = (d > 2);
 is_less_equal = (a <= 15);
 is_greater_equal = (b >= 17);
 // Logical operators
 both_true = is_equal && is_not_equal;
 either_true = is_less || is_greater;
 // Unary operators
 negated = -a;
 positive = +b;
 not_true = !is_equal;
 // Compound assignment
 mut x = 10;
 x += 5;
 x -= 2;
 x *= 3;
 x /= 4;
 x
}
// Data Structures
// Record literal
origin: Point = { x: 0, y: 0 };
// List literal
numbers: [int] = [1, 2, 3, 4, 5];
fruits: [string] = ["apple", "banana", "cherry"];
// Empty list
empty = [];
// Accessing Data
access_demo(): int = {
 // Field access
 p = { x: 10, y: 20 };
 x_val = p.x;
 // Array indexing
 arr = [1, 2, 3];
 first = arr[0];
 // Function call
 result = add(x_val, first);
 result
}
// Variant Construction
// Creating sum type values
some_value: Option = Some(42);
no_value: Option = None;
ok_result: Result = Ok(100);
error_result: Result = Error("Something went wrong");
// Nested Expressions
complex_expr(): int = {
 if (true && (10 > 5)) {
 match (Some(42)) {
 | Some(x) => {
 y = x + 10;
 y * 2
 },
 | _ => 0
 }
 } else {
 -1
 }
}

Dependencies

  • Rust 1.70+
  • Cargo

Building

To build the project, from the root directory

cargo build --release
cargo run # REPL
cargo run -- <file> # Run a script

Testing

Run the test suite:

cargo test

About

An unremarkable but Polished interpreted programming language.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Languages

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /