Skip to content
/ pyle Public

Pyle is a dynamically typed programming language with it's own stack-based VM fully implemented in Go from scratch.

License

Notifications You must be signed in to change notification settings

Fus3n/pyle

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

46 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

The Pyle Programming Language

Pyle is a dynamic programming language implemented in Go. It features a custom bytecode compiler and a stack-based virtual machine.

This project began as an educational exercise to gain a deeper understanding of language design, interpreters, and the internal workings of compilers by writing everything completely from scratch. The syntax is influenced by the simplicity and readability of languages like Python and JavaScript.

Core Features

  • Variables: Supports mutable (let) and immutable (const) variable declarations.
  • Data Types: Includes integers, floats, strings, booleans, null, arrays, and maps.
  • Control Flow: Provides standard if/else statements, for...in loops, while loops, and break/continue statements.
  • Functions: Supports first-class functions, closures, and recursion.
  • Built-in Methods: Common methods are available on built-in types, such as .map() on arrays and .format() on strings.

Code Showcase

Here are a few examples to demonstrate Pyle's syntax.

Variables and Functions:

// A recursive function to calculate Fibonacci numbers
fn fib(n: number) { // optional type annotation/hints
    if n <= 1 {
        return n
    }
    return fib(n - 1) + fib(n - 2)
}

const result = fib(10)
echo("Fibonacci of 10 is:", result)

Loops and Methods:

// A function to get the ASCII codes for each character in a string
fn getAsciiCodes(str: string) -> result  {
    return Ok(str.split(" ").map(fn(x) {
        const wordCodes = array(x)!.map(fn (y) {
            return asciiCode(y)!
        })!
        return wordCodes
    })?)
}


echo(getAsciiCodes("Hello Pyle")?)

How to Run

To execute a Pyle script, you can run the command-line tool from the root of the project:

go run ./cmd/pyle/ examples/basic.pyle

Deep Dive

Syntax

Pyle's syntax draws from Python and JavaScript while keeping things minimal. Blocks are delimited with curly braces, but most semicolons are optional. Variables use let for mutable and const for immutable declarations.

let count = 0
const name = "Pyle"

Functions are defined with fn and can be assigned to variables or passed around as first-class values.

fn greet(who) {
    echo("Hello, " + who)
}

const sayHi = fn(x) { echo("Hi " + x) }

Loops come in two flavors. The for...in loop iterates over arrays, strings, ranges, or map iterators. The while loop runs as long as a condition holds true.

for i in 0:10 { echo(i) }          // range from 0 to 9
for i in 0:10:2 { echo(i) }        // range with step of 2
for item in ["a", "b", "c"] { 
    echo(item) 
}

let count = 0
while count < 5 {
    count += 1
}

Maps use a JavaScript-like literal syntax. Bare identifiers become string keys, and computed keys go inside brackets.

const person = {
    name: "Alice",
    ["is_" + "active"]: true,
    [42]: "answer"
}
echo(person.name)
echo(person["is_active"])

Type Hinting

Type hints are optional annotations that can be added to function parameters and help with documentation. They have no runtime effect and are purely informational.

fn add(a: number, b: number) {
    return a + b
}

fn greet(name: string) {
    echo("Hello, " + name)
}

Built-in Methods

Pyle provides methods on its core types. Learn more in the docs.

const text = "Hello World"
echo(text.len())                    // 11
echo(text.split(" "))               // ["Hello", "World"]
echo("hi {}".format("there")?)       // "hi there"

const nums = [1, 2, 3]
nums.append(4)
echo(nums.map(fn(x) { return x * 2 }))  // result containing [2, 4, 6, 8]

const m = { a: 1, b: 2 }
for key in m.keys() { echo(key) }

IO

The os module provides filesystem operations. You can read and write files, check if paths exist, create directories, and list directory contents.

os.writeFile("data.txt", "Hello, file!")
const content = os.readFile("data.txt")!
echo(content)

if os.exists("data.txt")! {
    os.remove("data.txt")!
}

os.mkdir("mydir")!
echo(os.listdir(".")!)

const info = os.stat("somefile.txt")!
echo(info.size, info.isDir)

User input comes from scan(), which displays a prompt and returns the entered line.

let name = scan("Enter your name: ")!
echo("Hello, " + name)

Error Handling

Pyle uses a result-based error handling system. Functions that can fail return a result type which wraps either a value or an error. You can handle these results in several ways.

The ? operator propagates errors. If the result contains an error, it immediately returns that error from the current function. Otherwise it unwraps the value.

fn loadData(path) -> result {
    const content = os.readFile(path)?
    return Ok(content.split("\n"))
}

The ! operator (unwrap) extracts the value or panics if there's an error. Use it when you're confident the operation will succeed.

const age = int(scan("Age: ")!)!

You can also deconstruct results into value and error pairs for explicit handling.

let value, err = int("not a number")
if err != null {
    echo("Conversion failed:", err)
}

Result objects also have methods: .unwrap() panics on error, .unwrapOr(default) returns a fallback value, and .catch(fn) lets you handle errors with a callback.

const num = int("abc").unwrapOr(0)
const data = os.readFile("missing.txt").catch(fn(e) {
    echo("Using default because:", e)
    return Ok("default content")
})

Standard Modules & Interop

Pyle comes with improved built-in modules and a powerful Go interoperability layer.

HTTP Module

Create web servers easily using the http module.

use http

http.handle("/", fn(req, res) {
    res.setHeader("Content-Type", "application/json")
    res.send({ message: "Hello Pyle!" })
})
echo("Server starting on :8080")
http.listen(":8080")

Game Module (Ebiten)

Built-in support for the Ebiten game engine (v2).

use pylegame as pg

pg.init(640, 480, "Pyle Game")
pg.run(update_fn, draw_fn)

Go Interop (UserObject)

Pyle can wrap arbitrary Go objects (structs, images, database connections) and interact with them using reflection.

  • Go methods are mapped automatically (e.g. obj.MethodName() in Go is callable as obj.methodName() in Pyle).

Examples

There are many examples available in the examples directory covering arithmetic, HTTP servers, and a simple game.

Game Showcase

Star Sentinel Menu
Main Menu
Star Sentinel Gameplay
Gameplay

Star Sentinel - A classic arcade shooter built entirely in Pyle (View Source)

Generating Documentation

To generate HTML documentation for the standard library:

go run ./cmd/pyledoc/

About

Pyle is a dynamically typed programming language with it's own stack-based VM fully implemented in Go from scratch.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages