dragonstone v0.1.5-Alpha

The Dragonstone Programming Language




What is Dragonstone?

Dragonstone is a general purpose, high-level, object-oriented programming language. It is both an interpreted and compiled language, it's inspired by Ruby and Crystal but designed for programmer happiness, productivity, and choice.

WARNING: Some compile targets are still a work in progress, as of v0.1.5 the LLVM backend has limited support, all examples, excluding the stdlibs are working fine. Please report any you find so they outside of that so they can be fixed. In regards to the other backends these still need built out as they only create temporary artifacts for minimal functions, I haven't merged that work yet. The priority right now is 100% llvm backend support.


Information Description
Implementation language Current Version v5: Crystal, C
Platform Supports: x86, x86_64, AArch64
Operating System Supports: Windows, Linux, MacOS
File Extension .ds, .dragonstone
Influenced by Ruby, Crystal, Python, Go, Nim, Rust
Website dragonstone-lang.org

โš™๏ธ Project Setup

Requirements

  1. The Crystal needs to be installed (1.17.1+ but has been tested down to 1.11.2 on Ubuntu).
  2. (optional) LLVM/Clang; While Crystal also installs LLVM/Clang, installing a standalone version is recommended if you want to target dragonstone build --target llvm or dragonstone build --target c.
  3. (optional) The Ruby is needed if you want to use dragonstone build --target ruby (3.4.6+).

To Build from Source (All Platforms)

(MacOS/Linux/Windows)
  1. Clone this repository.
  2. cd to the project directory.
  3. Run shards build --release

Tip: Always use the --release flag for production builds as it significantly improves performance, without it a standard build is made and the dragonstone interpreter will run files roughly about 3-5x slower.

To Build from Source (Linux)

(Linux Recommended)
  1. Clone this repository.
  2. cd to the project directory.
  3. Run ./bin/dragonstone.sh --rebuild
  4. (optional) Run ./bin/dragonstone.sh --install which adds .\bin to your user PATH.
    • After running that command please restart your terminal, then you can use dragonstone from anywhere.
(Linux Alternative)
  1. Run shards build --release for a standard build and without PATH.

Tip for Linux Users: If you want to rebuild the project, after already doing so, you can use ./bin/dragonstone.sh --clean which will remove the build files, then you can use ./bin/dragonstone.sh --rebuild to build it again. For an automated version you can use ./bin/dragonstone.sh --clean-rebuild which will clean first then rebuild for you.

To Build from Source (Windows)

(Windows Recommended)
  1. Clone this repository.
  2. cd to the project directory.
  3. Run .\bin\dragonstone.bat --rebuild
    • This builds with the icons/resources and adds .\bin to your user PATH.
    • After running that command please restart your terminal, then you can use dragonstone from anywhere.
(Windows Alternative)
  1. Run shards build --release for a standard build, without any icons/resources or PATH.

Tip for Windows Users: If you want to rebuild the project, after already doing so, you can use .\bin\dragonstone.bat --clean which will remove the build files, then you can use .\bin\dragonstone.bat --rebuild to build it again. For an automated version you can use .\bin\dragonstone.bat --clean-rebuild which will clean first then rebuild for you.

Tip for Windows Users: An installer is also available in the Releases section.

๐Ÿงช Usage

Run Files via Interpreter.

    # On PATH:
    dragonstone run examples/hello_world.ds

    # Not on PATH:
    ./bin/dragonstone.exe run examples/hello_world.ds

Select a Backend to Use.

For now this is a temporary flag so I can verify some of my --backend build targets, this may change in the future when the self-host/bootstrap process begins.
    # Select between native (interpreter) or core (compiler) backends.
    dragonstone run --backend native examples/hello_world.ds
    dragonstone run --backend core examples/hello_world.ds

    # Without specifying it will automatically use the native (interpreter) or 
    # with auto it can choose the correct backend based on what is being ran 
    # within the file.
    dragonstone run examples/hello_world.ds
    dragonstone run --backend auto examples/hello_world.ds

    # However, if you don't want to specify the flag every time you can just 
    # set it change it anytime, this works on the fly.
    DRAGONSTONE_BACKEND=core dragonstone run examples/hello_world.ds

    # Or export the flag once, to set it permanently for the CI:
    export DRAGONSTONE_BACKEND=core

Build and Run Files via the Compiler with a target.

    # Supported Target Flags: bytecode, llvm, c, crystal, and ruby
    dragonstone build --target bytecode examples/hello_world.ds

    # Build and immediately execute the produced artifacts.
    dragonstone build-run --target bytecode examples/hello_world.ds

Run Test/Spec (still building out unit tests).

    # To run the full spec/unit testing suite.
    crystal spec

    # Helper script that runs the spec for just a specific backend.
    ./scripts/backend_ci.sh spec --backend native
    ./scripts/backend_ci.sh spec --backend core

โœจ Examples

Example of a sting output using echo:

    echo "Hello World!"

Example of comments/block comments using # and #[ ]#:

    # This is a Single Line Comment.

    #[
        This is a Multi-Line Comment.
    ]#

    message = "Hello!" # Trailing Comment.

    #[ Multi-Line Comment on same line. ]#     numbers = 10    #[ Inside or outside. ]#

Some Examples:

Examples of a String.
    # Using echo
    name = "Ringo"

    echo name

    # OUTPUT: Ringo
    # Using e!, for printing debug information.
    name = "Jules"
    e! name         
    
    # OUTPUT: name # -> "Jules"
    # Using eecho, without a newline.
    greet = "Hello, "
    name = "Ringo!"

    eecho greet
    eecho name

    # OUTPUT: Hello, Ringo!
    # Using ee!, for printing debug information without a newline.
    greet = "Hello, "
    name = "Jules!"

    ee! greet  
    ee! name        
    
    # OUTPUT: greet + name # -> "Hello, " + "Jules!"
In dragonstone you can optionally be very explicit. Using con for constants, var for variables, fix for block-scoped constants, and let for block-scoped variables.
    con first = 20
    var mul = 2

    def example
        fix second = 10
        let div = first // second

        echo first
        echo second
        echo div
        echo first * mul
    end

    example
Example of a Module with con, an immutable constant, and :: for scope resolution for modules and . for classes. Dragonstone also supports both module and mod for user preference.
    module Grades
        con Score = 100

        class Greeting
            def greet
                "Hello! I got a #{Grades::Score}%!"
            end
        end
    end

    echo Grades::Score
    echo Grades::Greeting.greet
Example of a Class, dragonstone also support both class and cls for user preference.
    class Person
        happy = true

        def greet
            if happy
                echo "Hello!"
            end
        end
    end

    person = Person.new
    person.greet
Also Ascii and Unicode are supported.
    class ๐Ÿ”ฅ
        ใ‚ = true
  
        def ้“
            if ใ‚
                echo "Hello!"
            end
        end
    end

    ๐Ÿ”ฅ.้“
Example of a def Method and String Interpolation, and supports both def and define.
    def greet(name)
        echo "Hello, #{name}!"
    end

    greet("Jules")
Example of fun, which is a first-class citizen and a function object that can be passed around. Dragonstone also supports both fun and function for preference.
    # Store a function object in a variable.
    add = fun(a, b)
        return a + b
    end
    echo add.call(2, 3)

    # Define a reusable function object.
    fun shout(text)
        return text + "!"
    end

    def print_one(word, fmt)
        echo fmt.call(word)
    end
    print_one("Ringo", shout)

    # And an advanced version, to pass an anonymous function inline.
    def print_formatted(words, fmt)
        words.each do |w|
            echo fmt.call(w)
        end
    end

    names = ["Ringo", "Peet", "Jules"]

    print_formatted(
        names, fun(text) do
            return ">> " + text + " <<"
        end
    )
Example of a Abstract Class and Super, dragonstone supports both abstract and abs for preference.
    abstract class Animal
        def make_sound(sound)
            echo "#{sound}"
        end
    end

    class Dog < Animal
        def make_sound(sound)
            super(sound)
            super("The sound a dog makes!")
        end
    end

    dog = Dog.new
    echo dog.make_sound("Woof!")
Two examples of a ternary and being nested.
    age = 20
    status = age >= 18 ? "Adult" : "Minor"
    echo "Status: #{status}"

    # Nested ternary
    score = 85
    grade = score > 90 ? "A" : (score > 80 ? "B" : "C")
    echo "Grade: #{grade}"
Example of a Map literal with key -> value pairs.
    ages = { "Jules" -> 32, "Ringo" -> 29, "Peet" -> 35 }
    echo ages["Jules"]

    ages["Ringo"] = 30
    echo ages["Ringo"]

    ages.each do |name, age|
        echo "#{name} is #{age}"
    end

    e! ages.keys
    e! ages.values
Example of enum.
    enum Direction
        North
        East
        South
        West
    end

    Direction.each do |direction|
        echo "Direction: #{direction}"
    end

Some More Examples but with Optional Types:

Example of a String with types.
    name: str = "Peet"

    echo name
Example of some math/integers with types.
    a: int = 10
    b: int = 10
    numbers = a + b

    echo numbers
Two examples of a Class with types, one of which showing Unicode support again.
    def ๐Ÿ˜ฑ(name: str) -> str
        echo "Hello, #{name}!"
    end

    ๐Ÿ˜ฑ("V")
With this example using a bit more complex features.
    class Person
        property name: str

        def initialize(name: str)
            self.name = name
        end

        def greet
            echo "Hello, my name is #{self.name}"
        end
    end

    person = Person.new("Jules")
    person.greet
Examples of struct.
    struct Point
        property x: int
        property y: int

        def initialize(@x: int, @y: int)
        end
    end

    point = Point.new(10, 20)
    echo "x: #{point.x}, y: #{point.y}"
Example of a record, similar to struct but are are immutable data structures that automatically generate the struct, the constructor, and getters for those fields.
    record Person
        name: str
        age: int
    end

    greet = Person.new("V", 30)
    echo "Name: #{greet.name}, Age: #{greet.age}"
Examples of Type Casting/Switching.
    def process(input)
        case input

        when input: str
            echo "It is a string: #{input}"
        when input: int
            echo "It is a number: #{input}"
        when input: bool
            echo "It is a boolean"
        else
            echo "Unknown type"
        end
    end

    process("Hello")
    process(42)
    process(true)

Example of using/importing other files with use.

From (./examples/use.ds)
    use "./test_use"

    echo add(magic, 8)
What's being grabbed from (./examples/test_use.ds).
    magic = 42

    def add(a, b)
        a + b
    end

Two examples of para, this is the Dragonstone version of what another languages calls a Proc.

For any {} used within Dragonstone, these can also be split between lines or placed on the same line.
greet = ->(name: str) {
    "Hello, #{name}!" 
}

echo greet.call("Jalyn")
Another bit more complex para.
square: para(int, int) = ->(x: int) { x * x }

echo square.call(6)

Examples the interop (some done but still a work in progress).

    # Call puts from Ruby.
    # This works too: ffi.call_ruby("puts", ["Hello from Ruby!"])
    Invoke Ruby
        with puts

        as {
            "Hello from Ruby!"
        }
    end

    # Call puts from Crystal.
    # This works too: ffi.call_crystal("puts", ["Hello from Crystal!"])
    Invoke Crystal
        with puts

        as {
            "Hello from Crystal!"
        }
    end

    # Call printf from C.
    # This works too: ffi.call_c("printf", ["Hello from C!"])
    Invoke C
        with printf

        as {
            "Hello from C!"
        }
    end
Imports via use are built out, so you can import a file, selectively import as well, and the same applies by importing through a url.
    # Both by file and selectively using `from` with it.
    use "./test_use"
    use { divide } from "./raise"

    # Any both by file and selectively via a url, I used `cdn.jsdelivr.net`
    # because it was the only thing I could find that would grab the examples from GitHub.
    use "https://cdn.jsdelivr.net/gh/vallereya/dragonstone@main/examples/unicode"
    use { MyModule } from "https://cdn.jsdelivr.net/gh/vallereya/dragonstone@main/examples/resolution"

See the examples/ directory for more sample .ds files.

โšก Benchmark Information

  • Built with --release.
  • Results are for this specific benchmark + machine; expect variance across CPUs/Operating Systems.
  • "Nested" means an extra loop layer (measuring loop-overhead vs a single loop), not extra work.
  • <2% overhead at scale.
  • Near identical for loops vs single.

You can run these yourself from the ./scripts/benchmark directory.

1 Billion Nested Loop Iteration Benchmark (Interpreter)

    Iterations:             ~4.47M iterations/s
    Per-iteration cost:     ~223.71 ns
    Actual Time:            223.71 s (~3.73 min)

1 Billion Nested Loop Iteration Benchmark (LLVM Compiler)

    Iterations:             ~811M iterations/s
    Per-iteration cost:     ~1.23 ns
    LLVM Compiler Time:     1.2326 s
    Results:                ~182ร— vs Interpreter

Comparison Context (Rough context, not direct comparable)

For 1 billion iterations of this benchmark (Interpreter):
    Ruby v2.X.X         = ~15-30 minutes    (varies by version)
    Python v3.X.X       = ~5-15 minutes     (varies by version)
 -> Dragonstone         = ~3.7 minutes
    Lua                 = ~1-2 minutes
    JavaScript          = ~10-30 seconds    (using V8)
For 1 billion iterations of this benchmark (Compiler/LLVM):
    C                   = ~0.5-1.5 seconds
    Rust                = ~0.5-1.5 seconds
 -> Dragonstone LLVM    = ~1.23 seconds
    GO                  = ~1-2 seconds
    Java                = ~2-5 seconds      (using JIT)
    PyPy                = ~10-20 seconds    (using JIT)
    Node.js             = ~10-30 seconds    (using V8)



ยฉ 2025 Vallereya


Code and Contributions have Apache-2.0 License
See LICENSE for more information.


LICENSE
Repository

dragonstone

Owner
Statistic
  • 4
  • 1
  • 0
  • 0
  • 0
  • 4 days ago
  • October 24, 2025
License

Apache License 2.0

Links
Synced at

Thu, 01 Jan 2026 06:54:52 GMT

Languages