HuoLanguage / huo

interpreted language written in C
MIT License
212 stars 21 forks source link

huo

Huo is an interpreted language which I started as a hobby project. The original vision for Huo was to be simple and have some cool native features like matrix math and parallel processing. The interpreter is fairly small and easy to work with and the syntax is easy to understand. Huo means "living" or "fire" in Chinese. It's pretty hard to pronounce, so if you don't know Chinese you can just say "hoo-ah".

If you would like to contribute to Huo open an issue describing the work you would like to do. If the change is accepted then you can make a pull request with your changes and it will be merged in after review.

compile

make   

run

Create a file containing Huo code and run it with the interpreter:

$ ./huo test.huo

Or you can run the included REPL which is written in Huo. To exit the REPL simply type "exit".

./huo repl.huo
:huo:> (print "Hello, world!")
"Hello, world!"

Functions

basic math

(+ 3 (* 4 3)) ; 15

(+ 3 -3.5) ; -0.500000

(+ 3 (/ 10 4)) ; 5.500000

(* 3 (- 10 4)) ; 18

math operations map over arrays

(+ [1,2] [3,4]) ; [4, 6]
(* [1,2] [3,4]) ; [3, 8]

print

(print (* 3 6)) ; prints 18 to the console
(print "hello world") ; prints "hello world" to the console
(let x (print "Hello")) ; prints "Hello" and stores undefined in x

concat strings and arrays

(cat "hello " "world!") ;  "hello world!"
(cat [1,2] [3,4]) ;  [1, 2, 3, 4]

length of strings and arrays

(length "Hello") ; 5
(length [1,2,3,4,5] ; 5

item at index of strings and arrays

(index 1 [1,2,3,4]) ; 2
(index 1 "Open") ; "p"

set item at index of strings and arrays

(set 3 "B" "oooooo") ; "oooBoo"
(set 3 "B" [1, 2, 3, 4]) ; [1, 2, 3, "B"]

substring and split on strings

(substring 0 4 "hello world") ;  "hell"
(split "o" "hello world"); [ "hell", " w", "rld" ]

equals, not, greater than and less than

(= 3 2) ; false
(= "Hey" "Hey") ; true
(! 1 3) ; true
(! "Hey" (cat "He" "y")) ; false
(> 4 3) ; true
(< 10 1) ; false

and, or, boolean primitives

(& true false) ; false
(| true false) ; true
(& (! true false) (= true true)); true

if block

; the if block requires three functions: conditional, true result and false result
; to do an else-if just put another if block in the false result position
(if (> 3 4)
    (print "normal math")
    (print "backwards land math")
)

let bindings

; let bindings are stored in a key-value structure and are passed
; by reference which means they are mutable
(let x 5)
(let y [])
(push 5 y)
(print (+ x 3) ; 8
(print y) ; [ 5 ]

the def function allows you to define functions with any number of parameters the arguments to def are: the function name, any number of parameters, the function body

(def biggest x y (if (> x y) x y))
(def sum x (reduce x acc curr (+ acc curr) 0))
(sum [1,2,3,4]) ; 10
(biggest 10 5) ; 10

def expects a function as its final parameter if you want to simply return a composition of values, use the return function

(def pair x y (return [x, y]))
(let x (pair 0 "start"))
(print x) ; [ 0, "start" ]

ast and run together allow you to pass functions as arguments the keywork ast returns its first argument as a value of type ast the keyword run will execute an ast value with whatever arguments you pass in

(def mapper arr fnc
  (do
    (each arr item i
    (set i (run fnc item) arr)
    )
    (return arr)
  )
)

(msg_first "Hello" (ast (cat x " World")))

switch block

; the switch block is convenient for matching a value against a large number
; of possibilities ~ the switch block takes a value and a list of cases
; each case is a comparator, a value, and a return value/function if the comparator is true
; include a default case to catch unhandled conditions
(def describe_number x 
    (switch x
        (> 100 "That's a huge number!")
        (> 50  "Hmm, not so big, less than one hundred.")
        (> 20  "I supoooose that's a decently sized number.")
        (< 10  "Pfft, you call that a number?")
        (default "I have no words...")
    )
)
(describe_number 110) ; "That's a huge number!"
(describe_number "Apple"); "I have no words..."

core functions will map over nested arrays

(let y [[1,2], [3,4]])
(let z [[3,4], [5,6]])
(print (+ y z)) ; [[4,6], [8,10]]
(> [[1, 2], [56, 123]] [[8, 9], [3,4]]) ; [ [ False, False ], [ True, True ] ]

push takes an item and an array in that order

(push 5 [1, 2, 3, 4]) ; [1, 2, 3, 4, 5]

map and each both take four arguments: first the array you want to iterate over the names for the current item and the index and then the function to call each iteration

(let x [1, 2, 3])
(let y 0)
(each x num idx (let y (+ y num)))
(print y) ; 6
(map x num idx (+ num idx))
(print x) ; [1, 3, 5]

reduce is takes the array to iterate over, the names of the accumulator and the current item, the function to call each iteration, and an optional initial value for the accumulator

(def avg x
    (/
        (reduce x acc cur (+ acc cur))
        (length x)
    )
)
(let x [1,2,3,4,13])
(print (reduce (push 4 [1,2,3]) acc cur (+ acc cur) 0)) ; 10
(print (avg x)) ; 4.600000

the for loop takes three arguments start number, end number, function to call each iteration start and end can be functions that return numbers

(let x [])
(for 0 10 
    (set (length x) 0 x)
)
(print x) ; -> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

the do block is a function that takes n functions and executes them in order, returning the value from the last function inside it

(def factorial x
    (do
        (let z [ ])
        (for 0 x
            (set (length z) (+ (length z) 1) z)
        )
        (reduce z acc cur (* acc cur) 1)
    )
)

reading a file is simple and returns a string

(let file (read "./files/example.txt"))
(print file) ; prints contents of example.txt

you can import a huo file that contains functions definitions the import function will ignore everything but functions

(import "./math.huo") ; import file containing average function
(let x (average [1,2,3,4,5,6] )) ; use imported function