Skip to content
/ paw Public

A statically-strong typed embeddable scripting language for C

License

Notifications You must be signed in to change notification settings

andy-byers/paw

Repository files navigation

paw

NOTE: Paw is under active development and is not ready for production use. See the roadmap to get an idea of where things are going. Also see known issues for a list of known problems that will eventually be fixed.

A cute little scripting language

Paw is a high-level, statically-typed, embeddable scripting language. It is designed to run on a virtual machine written in C.

Features

  • No dependencies
  • Static strong typing
  • Bidirectional type checking
  • Block expressions
  • Module system
  • Exhaustive pattern matching and sum types
  • Traits (interfaces checked at compile time)
  • Generics and generic bounds
  • Container literals ([T] and [K: V])

Examples

Hello world

pub fn main() {
    print("Hello, world!\n");
}

FizzBuzz

pub fn main() {
    // Create a closure. The type of "n" is inferred as "int" and the 
    // return type as "str".
    let fizzbuzz = |n| {
        if n % 15 == 0 { 
            "FizzBuzz" 
        } else if n % 3 == 0 { 
            "Fizz"
        } else if n % 5 == 0 { 
            "Buzz" 
        } else { 
            n.to_string() 
        }
    };

    // Call the closure for each integer 1 to 100, exclusive.
    for i in range(1, 100, 1) {
        print(fizzbuzz(i) + "\n");
    }
}

Sum types

pub enum Expr {
    Zero,
    Succ(Expr),
    Add(Expr, Expr)
}

pub fn eval(e: Expr) -> int {
    // match expressions must be exhaustive
    match e {
        Expr::Zero => 0,
        Expr::Succ(x) => eval(x) + 1,
        Expr::Add(x, y) => eval(x) + eval(y),
    }
}

pub fn three() -> int {
    let zero = Expr::Zero;
    let one = Expr::Succ(zero);
    let two = Expr::Add(one, one);
    eval(Expr::Add(one, two))
}

Generics

struct Pair<X, Y> {
    pub first: X,
    pub second: Y,
}

type Vec2<Ty> = Pair<Ty, Ty>;

pub fn swap<Ty>(v: Vec2<Ty>) {
    let temp = v.first;
    v.first = v.second;
    v.second = temp;
}

pub fn main() {
    let v = Vec2{
        first: 123,
        second: 456,
    };

    swap(v);

    print(v.first.to_string() + "\n"); // 456
    print(v.second.to_string() + "\n"); // 123
}

Traits

pub trait Get<T> {
    fn get(self) -> T;
}

struct Inner<X>: Get<X> {
    pub value: X,

    pub fn get(self) -> X {
        self.value
    }
}

struct Outer<X: Get<Y>, Y>: Get<Y> {
    pub value: X,

    pub fn get(self) -> Y {
        self.value.get()
    }
}

fn get<X: Get<Y>, Y>(x: X) -> Y {
    x.get()
}

pub fn main() {
    let inner = Inner{value: 123};
    let outer = Outer{value: inner};
    let value = get(outer);

    print(value.to_string() + "\n"); // 123
}

Operators

Precedence Operator Description Associativity
14 () [] . ? Call, Subscript, Member access, Question mark Left
13 ! - ~ # Not, Negate, Bitwise not, length Right
12 as Cast Left
11 * / % Multiply, Divide, Modulus Left
10 + - Add, Subtract Left
9 << >> Shift left, Shift right Left
8 & Bitwise and Left
7 ^ Bitwise xor Left
6 | Bitwise or Left
5 < <= > >= Relational comparisons Left
4 == != Equality comparisons Left
3 && And Left
2 || Or Left
1 = Assignment Right

Roadmap

  • static, strong typing
  • special syntax for builtin containers ([T] and [K: V])
  • type inference for polymorphic fn and struct
  • sum types/discriminated unions (enum)
  • product types (tuple)
  • custom garbage collector (using Boehm GC for now)
  • methods
  • module system and use keyword
  • type inference for polymorphic enum
  • exhaustive pattern matching (match construct)
  • more featureful use declarations: use mod::*, use mod::specific_symbol, use mod as alias, etc.
  • generic constraints/bounds
  • constant folding, constant propagation
  • traits (more like Swift protocols, maybe needs a different name)
  • integrate traits into stdlib (iterators, hash map keys, etc.)
  • error handling (try needs to be an operator, or we need something like a 'parameter pack' for generics to implement the try function)
  • let bindings/destructuring
  • function inlining
  • refactor user-provided allocation interface to allow heap expansion

Known problems

  • Either need to support or report an error on generic bounds like X: Trait<X>
    • Causes a stack overflow due to infinite recursion
  • Generic bounds should not be allowed on type aliases
  • The C API has pretty much 0 type safety
    • It may be necessary to reduce the scope of the C API somewhat
  • Pointer tracking (test only) feature is broken on MSVC
    • Might indicate a problem somewhere in the library
    • Need a machine that can run Windows for debugging

About

A statically-strong typed embeddable scripting language for C

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages