HiveBrain v1.2.0
Get Started
← Back to all entries
patternModerate

Reference Implementation for Honey Badger programming language

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
referencehoneybadgerprogramminglanguageforimplementation

Problem

I've been working on my own dynamically-typed, dynamically-scoped, imperative programming language called Honey Badger and feel that it's reached a stable enough state that I want someone else's eyes on my implementation of the interpreter.


For context, I'm going to walk you through a sample program in the
language that does a 2-player game of tic-tac-toe.

{
  print_board = fun b : {
    print(b[0] + "|" + b[1] + "|" + b[2]);
    print("-----");
    print(b[3] + "|" + b[4] + "|" + b[5]);
    print("-----");
    print(b[6] + "|" + b[7] + "|" + b[8])
  }; This defines a function that takes an argument b (representing a board) and prints it out prettily and assigns it to a




variable print_board.

//Check if the board is completely full.
  isfull = fun b : { 
    ind = 0;
    full = true;
    while (ind < len(b)) 
    {
      if b[ind] == " " then
        full = false;
      ind = ind + 1
    };
    full
  }; This defines another function that takes a board (representing as an array of size 9) walks through it and returns a




variable full saying whether or not. Function bodies consist of a
single expression (here it's a sequence of exprs chained together and
wrapped in {}s) and "return" whatever that evaluates to.

```
//This is lazy.
haswon = fun b, t : {
b[0] == b[1] & b[1] == b[2] & b[2] == t |
b[3] == b[4] & b[4] == b[5] & b[5] == t |
b[6] == b[7] & b[7] == b[8] & b[8] == t |
b[0] == b[3] & b[3] == b[6] & b[6] == t |
b[1] == b[4] & b[4] == b[7] & b[7] == t |
b[2] == b[5] & b[5] == b[8] & b[8] == t |
b[2] == b[4] & b[4] == b[6] & b[6] == t |
b[0] == b[4] & b[4] == b[8] & b[8] == t
};

gameover = fun b : { isfull(b) | haswon(b, "X") | haswon(b, "O")};
These are two more functions used below, that check if the game is over yet.

board = ["0", "1", "2", "3", "4", "5", "6", "7", "8"];
print("These are the indices for the positions");
print_board(board);
xTurn = true;
board = [" ", "

Solution

Obviously, if you have any questions just ask. This is my first "big" project in OCaml, but I'd rather you didn't sugarcoat criticism.

It looks like a very fun project, congratulations! :)

I have a few comments, which all concerns the style of writing. This is an important topic, because a good style eases maintainability.

-
The name of constructors: all constructors but the expression constructor have a one letter prefix. Having a prefix is good, because it prevents name clashes and eases automatic edition of code. You should therefore consider using a prefix for expressions too. Also, I would avise not to be lazy and to use a longer prefix – use regexp editing to convert your code in a few seconds.

-
The string of expression would be much more readable if you use the printf function instead of concatenating strings with the ^ operator. You need for this the %a conversion which allows to use custom printers (and consume two arguments, the custom printer and the value to print). You may even choose to write a pretty printer with the Format module from the standard library. If you do so, you can easily derive the to_string, print and output and print_err functions from this pretty-printer, either manually or using the Mixture_Format mixin.

open Format
let rec pp_print_expr fft = function
  | N a -> fprintf fft "N%d" a (* why a instead of n? *)
  | F f -> fprintf fft "F%f" f (* why f instead of x? *)
  | B b -> fprintf fft "B%b" b
  | Str s -> fprintf fft "String %S" s
  | Readline -> fprintf fft "readline()"
  | Len expr -> fprintf fft "len(%a)" pp_print_expr expr 
  | Print expr -> fprintf fft "print(%a)" pp_print_expr expr
  …


You probably got the idea with that header. As side notes, string_of_kind is not recursive. Also, consider sticking to standard mathy notations for one-letter variables, otherwise you will be the only one able to read your code. Also see how you can replace some uses of match with function.

-
In the eval expression you have a lot of mildly complicated treatments, you could consider delegating them to other functions (which are mutually recursive to eval). The benefits of doing this, is that you can test these functions individually.

-
You are not consistent in the style of messages in the exception you throw, sometimes you give detailed context, sometimes not. You should be consistent here and deciding who these messages are intended to will help consistency. Note that you can easily prepare detailed messages with ksprintf failwith like

ksprintf invalid_arg "Invalid args for and %a %a." string_of_val a string_of_val b


-
You should consider defining a State module defining a type which is your hashtable state for now, by doing this you have a natural receptacle to define functions operating on the state (like the pack-return you use to update the state) and it is later easy to experiment with alternative implementations.

On the logic of the types you define, since your language seems to be dynamically typed, I do not see why you have all your immediate values type embedded into expression. Is there a good reason why you do not replace the VN, VF, … constructors by an Immediate of value ?

Code Snippets

open Format
let rec pp_print_expr fft = function
  | N a -> fprintf fft "N%d" a (* why a instead of n? *)
  | F f -> fprintf fft "F%f" f (* why f instead of x? *)
  | B b -> fprintf fft "B%b" b
  | Str s -> fprintf fft "String %S" s
  | Readline -> fprintf fft "readline()"
  | Len expr -> fprintf fft "len(%a)" pp_print_expr expr 
  | Print expr -> fprintf fft "print(%a)" pp_print_expr expr
  …
ksprintf invalid_arg "Invalid args for and %a %a." string_of_val a string_of_val b

Context

StackExchange Code Review Q#90479, answer score: 14

Revisions (0)

No revisions yet.