Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/dzangfan/juhz

A general-purpose programming language
https://github.com/dzangfan/juhz

compiler language

Last synced: 5 days ago
JSON representation

A general-purpose programming language

Awesome Lists containing this project

README

        

* Juhz -- Toyish yet flexible programming language

#+begin_src elixir
use package.utils;
use package.array;
use package.stdio;
use package.iterator;

def primeNumbers(n) = such(array()):
iterator(onNumbersAfter(2))
.filter(~(k) { iterator(onArray(it)).all(~(p) { k % p != 0; }); })
.take(n)
.do(~(p) { push(p, it); })
.forAllElements();

println(primeNumbers(15));
#=> [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
#+end_src

~Juhz~ (/yoodz/) is a general-purpose programming language. Although this language is built by pure context-free grammar ([[https://github.com/dzangfan/juhz/blob/79af5d2ba7a417e5000e144fd8944519f537d6e4/language.rkt#L57][language.rkt]] shows you how cumbersome it is for CFG to express operation priority), it at least looks like a modern language and has flexibility and originality at some level.

** Introduction

Data in ~Juhz~ can be classified in seven types:

- ~boolean~, expressed by keywords ~true~ and ~false~
- ~number~, e.g. ~1~, ~1.4~, ~.1~
- ~string~, e.g. "hello". Escaped character: \\\\, \n, \\\" (better than nothing)
- ~array~, e.g. ~[1, 2, 3]~. Most of common operations like ~length~ can be found in standard library ~array~
- ~function~, e.g. ~function (x) { x; }~
- ~package~, e.g. ~package { def foo = bar; }~
- Others. Usually created by racket interfaces and opaque to ~Juhz~ facilities

The first four types work as what you are thinking. Functions are constructed by keyword ~function~:

1. ~function (arg1, arg2) { arg1 + arg2; }~
2. ~function () { 0; }~
3. ~function { 0; }~ (the same as 2.)

Packages are used as both the libraries (e.g. ~package.stdio~) and data structures. In fact, every source files in ~Juhz~ will be wrapped by a ~package~ and every definition in it will be interpreted as a field. Although arbitrary statement can be put between the curly brackets of ~package~, the following two statements are important:

1. ~def foo = bar;~
2. ~use expr;~

The former defines a field in the package and the latter "links" current package with another. For every identifier, the interpreter searchs in local definition introduced by ~def~ at first, and then recursively searchs in "used" packages. Arbitrary expressions can be used, as long as it returns something like a package.

There are several variants of ~def~ statement. Let's call ~foo~ and ~bar~ in the example above left-value and right-value respectively. Left-value can be:

1. ~foo~, identifier.
2. ~f()~, ~f(a1, a2)~, function definition.
3. ~package.foo~, library package definition.
4. ~foo.bar~, customized definition.

The first and the second are intuitive. The third defines ~foo~ as a library. The only difference between *library* and *ordinary package* is whether it is registered by keyword ~package~, like the third. After registry, you can access ~foo~ anywhere by keyword ~package~:

#+begin_src elixir

# A.juhz

def package.foo = 1;

# B.juhz

package.foo; #=> 1
#+end_src

So in this case, ~package~ works as a global variable that can introduce new fields by ~def~.

As to the forth, the following two statements are exactly equal.

#+begin_src elixir
def foo.bar = quz;

##################

use foo.__DEFINE__("bar", quz);
#+end_src

Regarding to the right-value, there are seven valid forms (boring, right?). Basically, for right-values, there are two rules:

- For something end with ~}~, it forms a right-value
- Otherwise, try to add a semicolon ~;~.

Two abvious exceptions are definition, "use", and assignment. something like ~def foo = bar;~, ~use foo~ and ~foo = bar~ is not a right-value. Wrap them by curly brackets.

Finally, tilde (=~=) and at (~@~) are equal to ~function~ and ~package~ respectively in token level. Therefore, the following two expression are equal:

#+begin_src elixir
function { println("hello"); }

##############################

~{ println("hello"); }
#+end_src

** Special syntax

As shown above, most syntax in ~Juhz~ are the same as most of modern programming languages. However, ~Juhz~ do have some original syntax which may confuse you. To begin with, function call can be put at the left side of assignment.

#+begin_src elixir
length(array) = 10;
#+end_src

That is just a syntax sugar and it treats value at the right side as the last argument of the invocation. As a result, the example above is exactly equal to the following:

#+begin_src elixir
length(array, 10);
#+end_src

This feature enables ~Juhz~ to define only one function to maintain a property, compared to languages like ~Java~. For example, ~package.specialVariables~ defines a function called ~ref~ to express modifiable state:

#+begin_src elixir
use package.stdio;
use package.specialVariables;

def nullify(n) = { n.value() = 0; }

def counter = ref(10);

nullify(counter);

println(counter.value()); #=> 0
#+end_src

By the way, functions in ~Juhz~ can take arguments more than it declares (ignore the extra arguments) and arguments less than it declares (fill them by special constant ~NOT_PROVIDED~). Therefore, a simplified definition of ~ref~ is as follows:

#+begin_src elixir
def ref(object) = package {
def value(newValue) = if newValue == NOT_PROVIDED {
object;
} else {
object = newValue;
}
}
#+end_src

Another special syntax is colon (~:~). Every function call can be followed by a colon and a expression (or right-value technically).

#+begin_src elixir
when(x < 0): println(x);

when(x < 0): { x = -x; }

iterate(onArray([1, 2, 3])): ~(x) {
println(x);
}

action(x): @{ def it = x; }
#+end_src

The first three statements are just syntax sugar. For ordinary expressions, ~Juhz~ will wrap them as functions take no arguments, and pass them to the invocation as the last argument. For functions, they will be passed as the last argument directly. However, in the case of package (the forth statement), ~Juhz~ will introduce a intermediate level of environment for the invocation. In particular, assume that function ~action~ is defined as follows:

#+begin_src elixir
def action(arg) = body;
#+end_src

Then when it is called as the forth statement, it will exprience the following steps:

1. Bind ~arg~ to ~x~
2. Bind ~it~ to ~x~
3. Evaluate ~body~

That means ~it~ is visible for ~body~. ~it~ is a famous name for this purpose. For example, ~suchPackage~ provided by ~package.reflection~ can construct a package dynamically:

#+begin_src elixir
# A.juhz

use package.reflection;

def override(pkg, name, value) = suchPackage(): {
it.uses(pkg);
it.has(name, value);
}

# B.juhz using A.juhz to implement a sleepy toString

use package.string;

def package.string = override(package.string, "toString"): ~(object) {
toString(object) + "..zzZ";
}

# C.juhz loaded after B.juhz

use package.stdio;
use package.string;

println(toString([2, 3, 5, 7, 13]));
#=> [2, 3, 5, 7, 13]..zzZ
#+end_src

In this example, ~it~ is a package builder with two fields ~has~ and ~uses~.

Btw. the following statement are also syntactical valid, but I don't think you need them.

#+begin_src elixir
f():
if x > 0 {
println(1);
} else {
println(0);
}

f():
while x > 0 {
x = x - 1;
}

f(): f(): f(): f(): println("Heart");
#+end_src

** Installation

As you guessed, [[https://racket-lang.org/][racket]] is necessary. You can install this repo by ~raco~:

#+begin_src bash
raco pkg install https://github.com/dzangfan/juhz.git
#+end_src

This command will install runtime API (e.g. ~juhz/api~) and a executable ~juhz-run~ to your system. ~juhz-run~ is a easy interface to run ~Juhz~ code. Pass pathes of sources as commandline argument to run them sequentially. Optional flag ~-i~ enables ~juhz-run~ to read standard input port at the end.

Note that there are no library system for ~Juhz~. I mean when you can access a package by ~package.lib~, there must be a statement like ~def package.lib = foo~. So dependencies should be loaded in advance. ~juhz-run~ will automatically load [[https://github.com/dzangfan/juhz/blob/main/collections/.all.juhz][.all.juhz]], which registers for standard libraries.

Finally, [[https://github.com/dzangfan/juhz/blob/main/juhz-mode.el][juhz-mode.el]] provides basic syntax highlighting and indentation for ~.juhz~ files in Emacs. Add it to ~load-path~ and load it when needed. For example:

#+begin_src emacs-lisp
(use-package juhz-mode
:mode "\\.juhz$"
:load-path "/path/to/juhz")
#+end_src

* Language reference (or something like that)

** Operators

Every unary and binary operator corresponds to a hook function, meaning that such operations will be interpreted as a method invocation. Logical operation ~&&~ (and) and ~||~ (or) are exceptions since their operands must be evaluated lazily. In summary, except ~&&~ and ~||~, every operator is implemented by method defined in package, and those two exceptions cannot be customized. The following table lists all operators, as well as their priorities and hooks.

| priority | name | operator | example | hook |
|----------+--------------+----------------+------------------+----------------------|
| 1 | logical or | ~\vert\vert{}~ | ~a || b~ | none |
| 2 | logical and | ~&&~ | ~a && b~ | none |
| 3 | equality | ~==~ | ~a == b~ | ~a.__SAME__(a, b)~ |
| 3 | difference | ~!=~ | ~a != b~ | ~a.__DIFF__(a, b)~ |
| 4 | less than | ~<~ | ~a < b~ | ~a.__LT__(a, b)~ |
| 4 | greater than | ~>~ | ~a > b~ | ~a.__GT__(a, b)~ |
| 4 | less equal | ~<=~ | ~a <= b~ | ~a.__LE__(a, b)~ |
| 4 | greater than | ~>=~ | ~a >= b~ | ~a.__GE__(a, b)~ |
| 5 | add | ~+~ | ~a + b~ | ~a.__PLUS__(a, b)~ |
| 5 | subtract | ~-~ | ~a - b~ | ~a.__MINUS__(a, b)~ |
| 6 | multiply | ~*~ | ~a * b~ | ~a.__TIMES__(a, b)~ |
| 6 | divide | ~/~ | ~a / b~ | ~a.__DIVIDE__(a, b)~ |
| 6 | remainder | ~%~ | ~a % b~ | ~a.__REM__(a, b)~ |
| 7 | logical not | ~!~ | ~!a~ | ~a.__BANG__(a)~ |
| 7 | positive | ~+~ | ~+a~ | ~a.__PLUS__(a)~ |
| 7 | negative | ~-~ | ~-a~ | ~a.__MINUS__(a)~ |

Besides, three types of operation related to subscript and definition are also implemented by hooks. The corresponding relationships are as follows.

| example | hook | common meaning |
|------------------------+---------------------------------------+--------------------------------|
| ~foo[i]~ | ~foo.__INDEX__(foo, i)~ | Find the i-th element of foo |
| ~foo[i] = j~ | ~foo.__INDEX__(foo, i, j)~ | Modify the i-th element of foo |
| ~def class.foo = bar;~ | ~class.__DEFINE__(class, "foo", bar)~ | Define something called "foo" |