/rye

Primary LanguageGo

Rye 🌾

Rye is design/work in progress programming language based on ideas from Rebol and flawored by Factor, Bash shell and Golang. It currently features a WIP golang based interpreter and REPL.

Rye at a glance 🌾

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: jim@example.com
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
jim@example.com .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

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

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 ) }

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

Doing Y with Rye

Rye-s first focus is data (pre)processing, so some examples of that.

read a webpage, save it to a file

get https://www.google.com/search?q=ryelang
 |write-all %ryelang-results.html

read and XML file and parse out specific information

rye has a SAX-based dialect

"<people><person age='33'>Jim</person><person age='30'>Jane</person></people>" 
|string-reader |do-sxml { <person> [ .get-attr 0 |prn  ] _ [ .print ] }
; prints:
; 33 jim
; 30 jane

More info

There is a simple website being made, and a blog.

There is also a very small FB group you can join.

Platforms

Currently, tested on Linux, Mac and Docker.

Builds the rye interpreter

go get -v github.com/pkg/profile \
  github.com/yhirose/go-peg \
  github.com/mattn/go-sqlite3
 
go build

# Executable
./rye 

Builds the rye interpreter with all current builtins

You can leave just the tags and install just the bindings you want.

go get -v github.com/pkg/profile \
  github.com/yhirose/go-peg \
  github.com/labstack/echo/middleware \
  github.com/labstack/echo-contrib/session \
  github.com/gotk3/gotk3/gtk \
  github.com/lib/pq \
  github.com/mattn/go-sqlite3 \
  github.com/nats-io/nats.go \
  github.com/shirou/gopsutil/mem \
  github.com/tobgu/qframe

go build -tags "b_echo b_gtk b_psql b_nats b_psutil b_qframe" -o ryefull

# Executable
./ryefull 

Docker image

The repository comes with Docker image that is capable of building rye in its full, the final step however then just wraps executable so that the image remains small and nimble.

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

Run 🏃‍♂️ the rye REPL with:

docker run -ti refaktor/rye

OSX tips

Ryefull relies on GTK3. So make sure your machine has it.

brew install pkg-config gtk+3 adwaita-icon-theme

More instructions here.

Author

  • Janko - <janko.itm@gmail.com>

Contact