Skip to content

A high-level programming language written in Scala

License

Notifications You must be signed in to change notification settings

melvic-ybanez/dry

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Dry

Dry is a dynamically-typed, high-level programming language currently being written in Scala.

The image below shows an overview of Dry's syntax via examples. You can learn more about the language here.

Sample Dry Program

Contents

  1. Introduction
  2. Installation
  3. Getting Started
  4. Running the tests
  5. Examples and Tests
  6. Grammar

Introduction

What is Dry

You can think of Dry as a Python-like programming language with curly braces and better support for functional programming (e.g. multi-line lambdas and partial function application). Dry is both dynamically and strongly typed, just like Python.

Dry was also heavily influenced by the Lox language, and you'll see why in the next section.

The name doesn't actually mean "Don't Repeat Yourself" (though maybe it can be a good slogan). Dry was rather named after the eye condition from which I suffered for about 2 years.

Why Use Dry

Dry started as a hobby project, when I was going through the first part of Crafting Interpreters. This is how Lox, the object language in that book, had also influenced the design of Dry.

However, as Dry started to grow, it became more and more of a language for cases where Python would normally shine. It became a language for people like myself back then (when I used Python at work), who would sometimes wish Python had multi-line lambdas, supported braces and not overly rely on indentations.

Of course Dry doesn't have the libraries and tools that Python has, but if the following are true about you or your requirements, then you might want to give Dry a try:

  1. You want an expressive language to write scripts, and you don't need the help of a static type system.
  2. You don't need much tools and libraries for your project.
  3. You like Python but want to use first-class functions a lot.

Installation

  1. The easiest way to install Dry on your system is to download a Dry executable (a Jar file) from the releases page. I recommend you choose the one from the latest release. You can put the jar file in a directory of your choosing.

  2. Install Java, if you haven't already.

  3. Since Dry is a Scala application, it is compiled to Java bytecode. This means you can run the Jar file you downloaded in the previous section the same way you run any Java Jar application, using the java -jar command. Note however that Dry programs are not compiled to Java bytecode, only Dry (the interpreter) itself is.

    Go to the downloaded jar's directory and enter the following:

    $ java -jar dry-<version>.jar

    where version is the version of the release you downloaded. You should see a welcome message in the screen:

    Welcome to Dry.
    Type in expressions and statements for evaluation. Type 'exit' to quit.
    dry> 
    

    You are now ready to start playing with Dry.

Getting Started

In Dry, like in some languages, you can either start a REPL, or run a script. This section will show you both.

Starting the REPL

The welcome message you saw at the end of the installation section indicates that you have entered the REPL (read-eval-print-loop) environment. That's what would happen if you ran Dry without specifying a path to a Dry script.

Many languages such as Scala, Python, Haskell, or any of the known Lisp dialects like Clojure, have their own supports for REPLs, so programmers coming from a background of any of these languages might already be familiar with it.

The REPL allows you to enter expressions and declarations, and see their evaluated results immediately:

dry> 100 + 20 - 8 * 5
80
dry> "hello" + "world"
helloworld
dry> let x = 10;
dry> x * x
100
dry> def sum(x, y) { return x + y; }
dry> sum(10, 9)
19
dry> sum(11, (lambda(y) { return y * y; })(2))
15
dry> let one_plus = sum(1, _);   // partial application
dry> one_plus(5)
6
dry> one_plus(6)
7
dry> 

To quit the REPL, enter exit:

dry> exit
Bye!

Running a Dry Script

To run a Dry script, you need to save the script to a file first and pass its path as an argument to the jar command. For instance, the code you saw at the beginning of this ReadMe file is available in examples/class_demo.dry under this repository. We can try running that with Dry:

$ java -jar dry-<version>.jar <path-to-repo>/examples/class_demo.dry
quack!
Woof
quack!
1 1 2 3 5

As shown above, Dry source files end with the .dry.

Running the tests

The project has a custom sbt command for running the test:

$ sbt testDry

If everything works correctly, your console should print a bunch of assertion results. The end of the logs should look like this:

[Success] Binding third param
=========== test_modules.dry ===========
[Success] Access variable from imported module
[Success] Access function from imported module
[Success] Modify values from imported module
[Success] Access variable from imported module
[Success] Modify values from imported module
[Success] Access method from module found via Dry Path
============= test_init.dry ============
[Success] Init should save the set variables
[Success] Init should be capable of accepting parameters
============ test_class.dry ============
[Success] Type of class
[Success] Type of instance
[Success] Ducks should quack!
[Success] Denji should say 'Woof!'
[Success] Class properties should be updated
Ran 179 tests. Successful: 179. Failed: 0.

The tests themselves are written in Dry (while the testDry command is written in Scala). You can see the directory containing them here: https://github.com/melvic-ybanez/dry/tree/main/tests. All the files in that directory that start with test_ and have the Dry extension will be picked up by the testDry command.

There is also a set of tests written in Scala, and you can run them like normal Scala tests:

$ sbt test

Dry Paths

If you want to import a specific module into your Dry program, you need to include the directory containing that module in the list of available paths. You do that by adding a .dry_paths file in the same directory as your main Dry script and write all the paths there, each on its own line.

See the .dry_paths file within the tests directory as an example. You might notice it includes the stdlib. That's because I needed the assertion functions for the tests.

Grammar

The syntax of Dry should be familiar to Python and Scala developers. Here's the grammar, written in BNF:

<declaration> ::= <class> | <function> | <let> | <statement>
<class>       ::= "class" <identifier> "{" <function>* "}"
<function>    ::= "def" <identifier> <params> <block>
<let>         ::= "let" <identifier> ("=" <expression>)? ";"
<statement>   ::= <expr-stmt> | <block> | <if> | <while> | <for> | <return> | <import> | <delete> | <try-catch>
<expr-stmt>   ::= <expression> ";"
<if>          ::= "if" "(" <expression> ")" <statement> ("else" <statement>)?
<while>       ::= "while" "(" <expression> ")" <statement>
<for>         ::= "for" "(" (";" | <let> | <expr-stmt>)
      (<expression>? ";") <expression> ")" <statement>
<return>      ::= "return" <expression>? ";"
<import>      ::= "import" <identifier>("."<identifier>)* ";"
<delete>      ::= "del" <call><index> ";"
<try-catch>   ::= "try" <block> ("catch" "(" <identifier>? ":" <identifier>? ")" <block>)+
<expression>  ::= <assignment> | <lambda>
<assignment>  ::= <call> "=" <expression>
<call>        ::= <primary> ("(" (<expression> | ("," <expression>)*)? ")" | "." <identifier>  | <index>)*
<index>       ::= "[" <expression> "]"   
<identifier>  ::= <alpha>(<alpha>?<digit>?)*
<lambda>      ::= "lambda" <params> <block> | <or>
<block>       ::= "{" <declaration>* "}"
<params>      ::= "(" (<identifier> | ("," <identifier>)*)? ")"
<or>          ::= <and> ("or" <and>)*
<and>         ::= <equality> ("and" <equality>)*
<equality>    ::= <comparison> ("!=" | "==" <comparison>)*
<comparison>  ::= <term> (">" | ">=" | "<" | "<=" <term>)*
<term>        ::= <factor> ("-" | "+" | "&" | "|" | "^" | "<<" | ">>"
      | ">>>" | "<=" <factor>)*
<factor>      ::= <unary> ("/" | "*" | "%" <unary>)*
<unary>       ::= ("!" | "-" | "+" | "not")* <call>
<primary>     ::= "false" | "true" | "none" | <number> | <string> | "self" | <identifier> 
      | <list> | <tuple> | <dictionary> | "(" <expression> ")"
<list>        ::= "[" (<expression> ("," <expression>*))? "]"
<tuple>       ::= "(" (<expression> ("," | ("," <expression>)*))? ")"
<dictionary>  ::= "{" (<key-value> ("," <key-value>)*)? "}"
<key-value>   ::= <expression> ":" <expression>
<number>      ::= <sign>?<nat>("."<nat>)?
<sign>        ::= "-" | "+"
<string>      ::= '"'(.?"\n"?)*'"'
<alpha>       ::= 'a' ... 'z' | 'A' ... 'Z' | '_'
<nat>         ::= <digit><digit>*
<digit>       ::= '0' ... '9'

About

A high-level programming language written in Scala

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages