Skip to content
forked from refaktor/rye

homoiconic dynamic programming language with some new ideas

License

Notifications You must be signed in to change notification settings

step-security-bot/rye

 
 

Repository files navigation

Rye language 🌾

Build and Test golangci-lint OpenSSF Scorecard Go Reference Go Report Card GitHub Release

visit ryelang.org, our blog or join our reddit group for latest examples and development updates

What is Rye

Rye is a high level, dynamic programming language based on ideas from Rebol, flavored by Factor, Linux shells and Golang. It's still an experiment in language design, but it should slowly become more and more useful in real world.

It features a Golang based interpreter and console and could also be seen as (modest) Golang's scripting companion as Go's libraries are quite easy to integrate.

Status: Alpha

Core ideas of the language are formed. Most experimenting, at least for this stage is done. Right now, focus is on making the core and runtime useful for anyone who might decide to try it.

That means I am improving the Rye console, documentation and stabilizing core functions.

Overview

Some specifics

Rye is homoiconic, Rye's code is also Rye's data.

data: { name "Jim" score 33 }
code: { print "Hello " + data/name }
if data/score > 25 code
; outputs: Hello Jim
print second data + ", " + second code
; outputs: Jim, Hello

Rye has no keywords or special forms. Ifs, Loops, even Function definiton words are just functions. This means you can have more of them, load them at library level and create your own.

if-jim: fn { name code } { if name = "jim" code }

visitor: "jim"
if-jim visitor { print "Welcome in!" }
; prints: Welcome in!

Rye has no statements. Everything is an expression, returns something. Even asignment returns a value, so you can assign inline. Either (an if/else like function) returns the result of the evaluated block and can be used like an ternary operator.

direction: 'in
full-name: join3 name: "Jane" " " surname: "Doe"  
print either direction = 'in { "Hi" +_ full-name } { "Bye bye" +_ name }
; outputs: Hi Jane Doe

Rye has more syntax types than your average language. And it has generic methods for short function names. Get and send below dispatch on the kind of first argument (http uri and an email address).

email: [email protected]
content: html->text get http://www.example.com
send email "title" content
; sends email with contents of the webpage

Rye has an option of forward code flow. It has a concept of op and pipe words. Every function can be called as ordinary function or as op/pipe word. It also has a concept of injected blocks like for below.

http://www.example.com/ .get .html->text :content
[email protected] .send "title" content
; sends email with contents of the webpage
{ "Jane" "Jim" } |for { .prn }
; outputs: Jane Jim
get http://www.example.com/users.json
|parse-json |for { -> "name" |capitalize |print }
; outputs capitalized names of users

Rye has higher order like functions, but they come in what would usually be special forms (here these are still just functions).

nums: { 1 2 3 }
map nums { + 30 }
; returns { 31 32 33 }
filter nums { :x all { x > 1  x < 3 } }
; returns: { 2 }
strs: { "one" "two" "three" }
{ 3 1 2 3 } |filter { > 1 } |map { <-- strs } |for { .prn }
; prints: three two three

more about HOF-s

Rye has some different ideas about failure handling. This is a complex subject, so look at other docs about it. Remember it's all still experimental.

read-all %mydata.json |check { 404 "couldn't read the file" }
|parse-json |check { 500 "couldn't parse JSON" }
-> "score" |fix { 50 } |print1 "Your score: {}"
; outputs: Your score: 50
; if file is there and json is OK, but score field is missing

Most languages return with an explicit keyword return. Rye, like lisps always returns the result of the last expression. But Rye also has so called returning words which for visual clarity start with ^ and always or conditionally return to caller.

add-nums: fn { a b } { a + b }
digits: fn1 { = 0 |either { "zero" } { "one" } }
percentage: fn { a b } { 100 * a |/ b |^fix { "ERR" } |concat "%" }
percentage 33 77  ; returns: 42%
percentage 42 0   ; returns: ERR
unlock-jim: fn1 { = "Jim" |^if { print "Hi Jim" } print "Locked" }
unlock-jim "Jim"  ; prints: Hi Jim
unlock-jim "Jane" ; prints: Locked

Rye has multiple dialects, specific interpreters of Rye values. Two examples of this are the validation and SQL dialects.

dict { "name" "jane" "surname" "boo" }
|validate { name: required score: optional 0 integer } |probe
; prints: #[ name: "jane" score: 0 ]

name: "James"
db: open sqlite://main.db
exec db { insert into pals ( name ) values ( ?name ) }

more dialects: html parsing, conversion

Rye has a concept of kinds with validators and converters.

person: kind 'person { name: required string calc { .capitalize } }
user: kind user { user-name: required }
converter person user { user-name: :name }
dict { "name" "jameson" } >> person >> user |print
; prints: <Context (user): user-name: <String: Jameson>>

Rye's scope/context is a first class Rye value and by this very malleable. One of the results of this are isolated contexts.

iso: isolate { print: ?print plus: ?add }
do-in iso { 100 .plus 11 |print }
; prints 111
do-in iso { 2 .add 3 |print }
; Error: Word add not found

Introductions

These are all work in progress, also need a refresher, but can maybe offer some insight:

Examples

These pages are littered with examples. You can find them on this README page, on our blog, on Introductions, but also:

Rye vs. Python

Python is the lingua franca and the measuring stone amongst dynamic programming languages, where Rye is also searching it's place. When learning about something new it makes sense to approach it from a familiar place.

Modules

The author of Factor once said that at the end it's not about the language, but the libraries. I can only agree, adding libraries, and distribution. Rye is still an experiment in language design, so it doesn't have anything like production level libraries. But to test the language with practical problems in mind, or because I needed something for my use of Rye, there are quite many modules already made.

Base - official integrations

  • Core builtin functions ⭐⭐⭐ 🧪~80%
  • Bcrypt - password hashing
  • Bson - binary (j)son
  • Crypto - cryptographic functions ⭐
  • Email - email generation and parsing
  • Html - html parsing
  • Http - http servers and clients ⭐⭐
  • IO (!) - can be excluded at build time ⭐⭐
  • Json - json parsing ⭐⭐
  • Mysql - database ⭐
  • Postgresql - database ⭐
  • Psutil - linux process management
  • Regexp - regular expressions ⭐ 🧪~50%
  • Smtpd - smtp server (receiver)
  • Sqlite - database ⭐⭐
  • Sxml - sax XML like streaming dialect
  • Validation - validation dialect ⭐⭐ 🧪~50%
  • Webview - Webview GUI

Contrib - will be community / third party integrations

  • Amazon AWS
  • Bleve full text search
  • OpenAI - OpenAI API
  • Postmark - email sending service
  • Telegram bot - telegram bots
  • Ebitengine - 2d game engine

legend: ⭐ priority , 🧪 tests

Follow development

Rye blog

For most up-to date information on the language and it's development visit our old and new blog.

Ryelang reddit

This is another place for updates and also potental discussions. You are welcome to join our reddit group.

Github

If code speaks to you, our Github page is the central location for all things Rye. You are welcome to collaborate, post Issues or PR-s, there is tons of things to do and improve :)

Getting Rye

Rye is developed on Linux, but has also been compiled on macOS and Docker. If you need additional architecture or OS, post an Issue.

Binaries

You can find precompiled Binaries for Linux and macOS under Releases.

Docker images are published under Packages.

Docker images

Binary Docker image

This image includes Linux, Rye binary ready for use and Emacs-nox editor.

Docker image: ghcr.io/refaktor/rye

Run it via:

docker run -ti ghcr.io/refaktor/rye

Dev Docker image

The repository comes with a local Docker image that builds rye and allows you to do so.

docker build -t refaktor/rye -f .docker/Dockerfile .

Run 🏃‍♂️ the rye REPL with:

docker run -ti refaktor/rye

Building Rye

Use official documentation or lines below to install Golang 1.19.3 https://go.dev/doc/install

wget https://go.dev/dl/go1.21.5.linux-amd64.tar.gz
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.21.5.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
go version

Clone the main branch from the Rye repository. There is a submodule (a different repo) for contributed packages, hence the additional flag is needed:

git clone https://github.com/refaktor/rye.git && cd rye

Build the tiny version of Rye

go build -tags "b_tiny"

Run the rye file:

./rye hello.rye

Run the Rye Console

./rye

Install build-esential if you don't already have it, for packages that require cgo (like sqlite):

sudo apt install build-essential

Build Rye with specific modules

Rye has many bindings, that you can determine at build time, so (currently) you get a specific Rye binary for your specific project. This is an example of a build with many bindings. I've been working on a way to make this more elegant and systematic, but the manual version is:

go build -tags "b_tiny,b_sqlite,b_http,b_sql,b_postgres,b_openai,b_bson,b_crypto,b_smtpd,b_mail,b_postmark,b_bcrypt,b_telegram"

Build WASM version of Rye

Rye can also work inside your browser or any other WASM container. I will add examples, html pages and info about it, but to build it:

GOOS=js GOARCH=wasm go build -tags "b_tiny,b_norepl" -o wasm/rye.wasm main_wasm.go

Related links

Rebol - Rebol's author Carl Sassenrath invented or combined together 90% of concepts that Rye builds upon.

Factor - Factor from Slava Pestov taught me new fluidity and that variables are no-good, but stack shuffle words are even worse ;)

Red - Another language inspired by Rebol from well known Rebol developer DocKimbel and his colleagues. A concrete endaviour, with it's low level language, compiler, GUI, ...

Oldes' Rebol 3 - Rebol3 fork maintained by Oldes (from Amanita Design), tries to resolve issues without unnecessarily changing the language itself.

Arturo - Another unique language that builds on Rebol's core ideas.

Charm - Not related to Rebol, but an interesting Go based language with some similar runtime ideas and challenges.

Ren-c - Rebol 3 fork maintained by HostileFork, more liberal with changes to the language.

Questions, contact

You can post an Issue visit github Discussions or contact me through gmail or twitter.

About

homoiconic dynamic programming language with some new ideas

Resources

License

Security policy

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Go 81.6%
  • JavaScript 13.1%
  • HTML 4.1%
  • CSS 1.0%
  • Shell 0.1%
  • C 0.1%