Skip to content

FISHARMNIC/HAMprimeC2

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

HAM' Language

About Ham

HAM' (HAM prime) is a fully compiled 32-bit programming language that runs on Linux. HAM is an acronym for “Hard as You Make It”, and permits the programmer to pick and choose the complexity of their program in terms of how high-level they may want it to be. With things such as pointers and optional typing, it seeks to provide a similar amount of control that C gives to the user. However, it also comes with more simplified features such as easy string/number concatenation, classes, and automatic memory management.

Recent Update Log

  • Added string interpolation! See strings example below for stringInterpolation.x
  • Added forEach (See feach.x. Basic support, more coming soon)
  • Added writing into memory address specified by pointer (still WIP, many cases don't work)
  • Operator overloads addded!
  • Math is now evaluated in the proper order! (now for floats too)
  • old big update stuff:
    • Automatic garbage collection is here! After some tweaks from my original design the garbage collection system is now fully working (and rather fast)! All data is now allocated globally unless specified (see the documentation).
    • ftos and itos
      • convert floats and integers to strings
    • Multiple strings can now be added together using "+"
      • Any numbers will be automatically converted to strings like: ("bob " + 123) becomes "bob 123"
      • Any formats will automatically have their method "toString" called
    • Big IDE update! (See below)
    • Fixed segfault with GFX lib

What is working so far?

All of the things below work. Scroll down for examples or click here. Detailed documentation can be viewed here

  • Variables
    • Implicit and forced typing
    • Garbage collection
      • note: the GC is currently set to trigger rather often as a form of debugging possible issues. this can be changed by modifying BYTES_PER_GC in compiler/libs/garbage/rollcall.h
    • Stack and global scope
    • Pointers and addressing
    • External variables
    • Arrays, strings, formats, floats, etc.
    • Casting
  • Formats
    • Nested formats
    • Creating instances
    • Property reading and setting
    • Methods
    • Constructors
    • Operator overloading
    • Privates and publics
  • Functions
    • Variadic functions
    • Forward declarations
    • Seamless callee/caller C-functions
    • Calling from pointers/addresses
  • Control flow
    • Nested If/elif/else statements
    • Comparison: ==, !=, <:, :>, <=, >=, ||, &&
    • While loops
  • I/O
    • C printf and scanf
    • Easy print_ function
      • Automatically prints arrays, formats, floats, ints, strings
      • Currently doesn't work for format arrays
  • Debugger
    • Traces back to faulty line in source file
    • Works best on IDE (see below)
  • Graphics
    • Expose/mouse/keyboard events
    • Rendering simple shapes
  • Strings
    • Easy concatenation
    • Automatic conversion from formats and numbers
    • Easy comparison using normal comparison signs
  • Math
    • Floats and ints
    • Support for GMP (look at bignums in test/working)

Currently working on / roadmap:

  • Nested arrays have a lot of issues, it's sort of a big mess that I have to fix soon
  • operator overloading with different types of parameters
  • easier built-in array features (push,pop,forEach,etc.)
  • Lamdba functions
  • inline assembly

differences from the first compiler

  • Much better optimization
  • Stack allocation / variables
  • Recursion
  • Standard calling convention (cdecl)
  • No more corner cases where certain things won't work
  • Parenthesis math
  • Seamless C inclusion
  • Automatic memory management
  • And much more!

Features a new IDE

The updated IDE now shows you what each line compiles into assembly. Simply click on the line and the window on the right will jump to its portion of code. After compiling, you can click the bug icon to check if there are any segmentation issues, and the IDE will display both the problematic line and assembly instruction. The highlighting system will not work until after you have compiled a program, which can be done with either the checkmark button (just compile), or the arrow (compile and run).

ide_1 ide_2

Note: This is still being worked on, and editing still has some issues. This was more of a side project that I will finish when I am done with the compiler.

Interesting examples

(excuse the poor highlighting)

Formats as classes

/*
Example linked list format. In my opinion, this is a good demo on how simple HAM can be
* Notice how seamless things like adding a new element, or removing one
* Compare it to something like C. No mallocs, frees, pointer confusion, etc.
*/

Linked format 
{
    .current u32;
    .next Linked;
    
    .Linked constructor<u32 value>
    {
        this.current <- value;
        this.next <- 0;
    }
    
    .findLast method<> -> Linked:borrowed
    {
        create reference <- borrow this;
        while(reference.next != 0)
        {
            reference <- borrow reference.next;
        }
        
        return reference;
    }
    
    .find method<u32 index> -> Linked:borrowed
    {
        create reference <- borrow this;
        create i <- 0;
        
        while(i <: index)
        {
            if(reference.next == 0)
            {
                return null;
            }
            reference <- borrow reference.next;
            i <- i + 1;
        }
        return reference;
    }
    
    .index method<u32 index> -> u32
    {
        create ref <- this.find(index);
        return(ref.current);
    }
    
    .add method<u32 value> -> u32
    {
        create end <- borrow this.findLast();
        
        create newAddr <- Linked(value);
        end.next <- newAddr;
    }
    
    .remove method<u32 index> -> u32
    {
        if(index <: 1)
        {
            this <- this.next;
        }
        else
        {
            create previous <- this.find(index - 1);
            
            if(previous == 0) /* could not find */
            {
                print_("Index " + index + " does not exist!\n");
                return 0;
            } 
            elif(previous.next == 0) /* Last item */
            {
                print_("Index " + index + " out of range!\n");
            }
            else
            {
                previous.next <- previous.next.next;
            }
        }
    }
    
    .replace method<u32 index, u32 value> -> u32
    {
        create reference <- this.find(index);
        reference.current <- value;
    }
    
    .toString method<> -> string
    {
        create reference <- borrow this;
        create build <- "[";
        while(reference.next != 0)
        {
            build <- build + reference.current + "->";
            reference <- borrow reference.next;
        }
        build <- build + reference.current + "]";
        return build;
    }
}

entry function<> -> u32
{
    create myList <- Linked(1);
    
    myList.add(2);
    myList.add(3);
    
    print_(myList);
 
    myList.remove(0);
    myList.add(5);
    myList.replace(2,4);
    
    print_(myList);
    
    myList.remove(1);
    myList.add(8);
    myList.add(16);
    myList.add(32);
    myList.remove(4);
    
    print_(myList);
    
    printf("There should be an error below this...\n---> ");
    myList.remove(10);
   
    return 0;
}

Class Example 2

/*
Example for something simialar to Java ArrayList/C++ vector class
*/

List format {
    .buffer array;
    .length u32;
    
    /* Constructor */
    .List constructor<>
    {
        this.buffer <- 0;
        this.length <- 0;
    }
    
    /* Constructor overloading */
    .List constructor<u32 size, ...>
    {
        this.buffer <- u32[size];
        this.length <- size;
        
        create i <- 0;
        while(i <: size)
        {
            this.buffer[i] <- __arguments[i + 1];
            i <- i + 1;
        }
    }

    /* Method */
    .push method<u32 element> -> u32
    {
        this.length <- (this.length + 1);
        if(this.length == 1)
        {
            /*Not the most efficient way, but its meant to show that it can allocate and reallocate just fine*/
            this.buffer <- u32[1];
        }
        else 
        {
            this.buffer <- copy(u32[this.length], this.buffer);
        }
        this.buffer[this.length - 1] <- element;
        return(this.length);
    }

    .pop method<> -> u32 
    {
        this.length <- (this.length - 1);
        create retValue <- this.buffer[this.length];

        this.buffer <- copy(u32[this.length], this.buffer);
        return retValue;
    }

    .every method<any iterator> -> u32 
    {
        create i <- 0;
        while(i <: this.length)
        {
            call iterator(this.buffer[i]);
            i <- (i + 1);
        }
    }
    
    .toString method<> -> string
    {
        create build <- "[";
        create i <- 0;
        while(i <: (this.length - 1))
        {
            build <- build + this.buffer[i] + ",";
            i <- i + 1;
        }
        build <- build + this.buffer[i] + "]";
        return build;
    }

    .List operator(add)<u32 rhs> -> List
    {
        create dupe <- duplicate(this);
        dupe.push(rhs);
        return dupe;
    }
}

putint function<u32 i> 
{
    printf("Printing: %i\n", i);
}

entry function<> -> u32
{
    /* instancing */
    create myList <- List();

    /* calling methods */
    myList.push(123);
    myList.push(456);
    printf("[%i,%i]\n", myList.buffer[0], myList.pop());
    myList.push(321);
    myList.every($putint);
    
    /* constructor overloading */
    create secondList <- List(3, 1, 2, 3);

    /* operator overloading, see ".List operator(add)" */
    create thirdList <- (secondList + 4 + 5);
    print_(thirdList);
    
    return 0;
}

Operator Overloads

MapEntry format
{
    .key string;
    .value u32;
    
    .next MapEntry;

    .MapEntry constructor<string k, u32 v>
    {
        this.key <- k;
        this.value <- v;

        this.next <- 0;
    }
}

Map format 
{   
    privates;

    .head MapEntry;
    .rlast MapEntry;

    .kneErr method<string k> -> u32
    {
        print_("Error:: key<" + k + "> does not exist. Exiting program.");
        quit(0);
    }

    publics;
    
    .Map constructor<>
    {
        this.head <- 0;
    }
    
    .find method<string k> -> MapEntry:borrowed
    {
        if(this.head == 0)
        {
            print_("Map empty");
            return null;
        }

        create reference <- borrow this.head;

        while(reference.key != k)
        {
            if(reference.next == 0)
            {
                this.rlast <- reference; /* won't take ownership since reference is borrowed */
                return null;
            }
            reference <- borrow reference.next;
        }
        return reference;
    }
    
    .remove method<string k> -> u32
    {
        if(this.head.key == k)
        {
            this.head <- this.head.next;
            return 0;
        }

        if(this.head.next == 0)
        {
            this.kneErr(k);
        }

        create reference <- borrow this.head;

        while(reference.next.key != k)
        {
            reference <- borrow reference.next;
            if(reference.next == 0)
            {
                this.kneErr(k);
            }
        }

        reference.next <- reference.next.next;
    }

    .set method<string k, u32 val> -> u32
    {
        if(this.head == 0)
        {
            this.head <- MapEntry(k,val);
            return 0;
        }
        else
        {
            create reference <- this.find(k);

            if(reference == 0)
            {
                this.rlast.next <- MapEntry(k,val);
            }
            else
            {
                reference.value <- val;
            }
        }
    }

    .get method<string k> -> u32
    {
        create found <- this.find(k);
        if(found == 0)
        {
            this.kneErr(k);
            return 0;
        }
            
        return(this.find(k).value);
    }

    .Map operator(index_set)<string k, u32 v>
    {
        this.set(k,v);
    }

    .Map operator(index_get)<string k> -> any
    {
        return(this.get(k));
    }
}

entry function<> -> u32
{
    create map <- Map();

    map["bob"] <- 123;
    print_(map["bob"]);

    map["bob"] <- 456;
    print_(map["bob"]);

    map["jon"] <- 789;
    print_(map["jon"]);

    map["mike"] <- 321;
    print_(map["mike"]);

    map["joe"] <- 654;
    print_(map["joe"]);
}

Strings

User format
{
    .name string;
    .age u32;

    .toString method<> -> string
    {
        create out <- this.name + " (age " + this.age + ")";
        return out;
    }
}

entry function<> -> u32
{
    create me <- User<name:"Nico",age:18>;

    create awesomePerson <- "Nina";
    create age <- 123.456;

    print_("Hello " + me + " and " + awesomePerson + "! I am " + age + " years old");
    
    /* HAM also supports string interpolation */
    print_(`Hello ${me} and ${awesomePerson}! And in 1 year I will be ${age + 1} years old`);

    if(me.name == "Nico")
    {
        print_("Hey! My name is also Nico!");
    }

    return 0;
}

Variadics

myVariadic function<u32 numberOfArgs, ...> -> u32
{
    create i <- 1;
    while(i <= numberOfArgs)
    {
        printf("Argument %i: %s\n", i, __arguments[i]);
        i <- (i + 1);
    }
}

entry function<> -> u32
{
    myVariadic(2, "hi", "bye");
    return 0;
}

Formats

Person format
{
    .age u32;
    .name p8;
}

Student format
{
    .info Person;
    .id u32;
}

entry function<> -> u32
{
    create bob <- Student<id:123,info:Person<age:15,name:"bob">>;
    printf("The student %s (id #%i) is %i years old\n", bob.info.name, bob.id, bob.info.age);
}

C inclusion

// make sure to assemble with -lpthread
forward pthread_create function<u32 a, u32 b, u32 c, u32 d>;
forward pthread_exit function<u32 a>;
forward sleep function<u32 a>;
forward write function<u32 a, p8 b, u32 c>;

thread function<> -> u32
{
    create i <- 0;
    while(i <: 10)
    {
        write(1, "hello from child\n", 17);
        sleep(1);
        i <- (i + 1);
    }
    return 0;
}

entry function<> -> u32
{
    create tid <- 0;
    pthread_create($tid, 0, thread, 0);
    
    create i <- 0;
    while(i <: 10)
    {
        write(1, "hello from main\n", 16);
        sleep(1);
        i <- (i + 1);
    }

    pthread_exit(0);
    return 0;
}

Graphics

/* Assemble with scripts/lima_x11.sh */

#include sys x11;

/* 
* __ccalled__ specifies that the function will be called from C 
    * By default, HAM` optimizes if registers should be saved before a function call
    * Without this, any registers that should be preserved by the callee might not be
*/
__ccalled__ render function<u32 event> -> u32
{
    gfx_clear();
    /* Draw a rectangle at the mouse */
    gfx_rect((gfx_mouse_x - 10),(gfx_mouse_y - 10),20,20);
}

entry function<> -> u32
{
    gfx_setup(500, 360);
    gfx_begin($render);
}

Floats

factorial function<u32 n> -> u32
{
    if(n >= 1)
    {
        return (n * factorial(n - 1));
    }
    return 1;
}

entry function<> -> u32
{
    create sum_index <- 0;
    create sum_end <- 10;

    /* 
    Decimal number forces a float
    These also work:
        explicit type: create f32 sum <- 0.0;
        explicit cast: create sum <- f32:(0.0);
    */
    create sum <- 0.0;

    while(sum_index <: sum_end)
    {
        sum <- sum + 1.0 / factorial(sum_index);
        sum_index <- sum_index + 1;
    }

    print_(sum);

    return 0;
}