Skip to content
Robert Durst edited this page Mar 16, 2019 · 15 revisions

Welcome to the sailfish wiki!

Table of Contents

  1. Overview
  2. The Basics
  1. Example Snippets
  1. Language Internals

Overview

Sailfish is an experimental, procedural language with a hint of OOD by way of structural pseudoclasses. It is currently compiled and then transpiled to C. Its development was largely motivated from parts of Python, Rust, Haskell, and Go.

The Basics

Primitives

There are four built in primitives:

  • integer (int)
  • float (flt)
  • string (str)
  • boolean (bool)

Variable Declarations

To declare a variable, follow this structure:

dec TYPE VAR_NAME = VALUE

NOTE: For structures, this is a bit different. Keep reading to learn how!

Functions

Functions in sailfish are basically what you'd expect with some syntactic frosting sugar. There are a few rules you must follow when defining a function:

  • include a name
  • at least one input and output
  • only one void per input/output
  • only one output

Here is the structure of a function definition:

fun foo
<- int input_one, flt input_two // inputs
-> void // output
{
    // do something coold
}

Since there are no loops (yet?!?) in sailfish, you must use functions and recursion quite frequently. How functional!

Control Flow

The basis of control flow in sailfish is the if statement. These are as one may expect in C, with multiple branches being designated with if - else if - else. One oddity is the way of expressing conditionals. Conditionals rely on something named a grouping expression. Grouping expressions are sort of like parenthesis, but only can contain a single expression that must result in a boolean. Otherwise everything is left associative. How annoying cool, am i rite?

Example:

if | true == false == | 1 < 2 | | {}
else {}

User Defined types

These are the core parts of the sailfish language. UDT's in sailfish are based on the structures of rust. If you are coming from a more OOD type of programming style, think of these like structures with methods and no inheritance.

UDT's have two parts. The first part is where attributes are defined, sort of like a normal struct, and the second part is where the methods are defined. Here is the basic structure:

Cat NAME {
   int attribute_one
   flt attribute_two
}

Cfn NAME {
   fun Foo
   <- int i
   -> void
   { }
}

Initial Execution Body

Since this is a C-like language (sorry to all my LISP loving friends!), there must be a main function of sorts. Instead of a function named main, there is a start keyword followed by a block. An example for hello world looks a bit like this:

start {
    display_str("Hello World!\n")
}

Example Snippets

Here are some example code snippets that demonstrate the power of sailfish.

FizzBuzz

fun fizzbuzzhelper
<- int i
-> void
{
    if | 0 == i % 15 | { display_str("FizzBuzz\n") }
    else if | 0 == i % 3 | { display_str("Fizz\n") }
    else if | 0 == i % 5 | { display_str("Buzz\n") }
    else { display_int(i) display_str("\n")}
}

fun fizzbuzz
<- int i
-> void
{
    fizzbuzzhelper(i)
    i = i - 1
    if |i > 0| { fizzbuzz(i) }
    else {}
}

A Counter

Cat Counter {
    int count
    str finish_msg
}

Cfn Counter {
    fun decrement
    <- void
    -> void
    {
        own.count = own.count - 1
        display_int(own.count)
        display_str("\n")
    }

    fun increment
    <- void
    -> void
    {
        own.count = own.count + 1
        display_int(own.count)
        display_str("\n")
    }

    fun countdown
    <- void
    -> void
    {
        if | own.count > | 0 + 1 | | { 
            own...decrement()
            own...countdown()
        }

        else {
            display_str( own.finish_msg )
        }
    }
}

start {
    dec Counter c = new Counter { count : 10, finish_msg: "Blast off!\n" }

    c...countdown()
}

A Stack

Cat Node {
    Node next
    int data
}

Cfn Node {
    fun has_next
    <- void
    -> bool
    {
        dec bool ret = | own.next != void |
        return ret
    }

    fun set_next
    <- Node node
    -> void
    {
        own.next = node
    }

    fun next
    <- void
    -> Node
    {
    	return own.next
    }

    fun data
    <- void
    -> int
    {
        return own.data
    }
}

Cat Stack {
    Node head
    int size
}

Cfn Stack {
    fun is_empty 
    <- void
    -> bool
    {
        return | own.size == 0 |
    }

    fun size
    <- void
    -> int
    {
        return own.size
    }

    fun peek
    <- void
    -> int
    {
        // since return is at end, must declare a return value here
        dec int i = 0
        if | own...is_empty() == true | {
            // do nothing
        } else {
            i = own.head...data()            
        }

        return i
    }


    fun push
    <- Node node
    -> void
    {
        node...set_next(own.head)
        own.head = node
        own.size = own.size + 1
    }

    fun pop
    <- void
    -> void
    {
        if | own...is_empty() == true | { }
        else {
            own.head = own.head...next()
            own.size = own.size - 1 
        }
    }

    fun print_
    <- Node node
    -> void
    {
    	if | node!= void| {
    	    display_int(node...data())
	        display_str(" ")
	        own...print_(node...next())
	    } 
	
	    else {}
    }
    
    fun print
    <- void
    -> void
    {
    	display_str("Stack contents: ")
        own...print_(own.head)	
        display_str("\n")
    }
}


start {
    dec Node a = new Node { next: void, data: 10 }
    dec Node b = new Node { next: void, data: 20 }
    dec Node c = new Node { next: void, data: 30 }
    dec Node d = new Node { next: void, data: 40 }
    dec Node e = new Node { next: void, data: 50 }

    dec Stack s = new Stack { head: void, size: 0 }

    s...push(a)
    s...push(b)
    s...push(c)

    s...print()

    display_str("Size: ")
    display_int(s...size())
    display_str("\tTop: ")
    display_int(s...peek())
    display_str("\n")

    s...pop()
    s...pop()
    s...pop()
    s...pop()
    s...pop()

    display_str("Size: ")
    display_int(s...size())
    display_str("\tTop: ")
    display_int(s...peek())
    display_str("\n")

    s...push(d)
    s...push(e)
    s...push(c)
    s...push(b)

     s...print()
}

Language Internals

Since this repo is for the sailfishc compiler, this wiki will also cover certain aspects of the design of the language and compiler. That information may be found in this section.

Grammar

Currently there are two versions of the grammar, the grammar of which was originally aimed for in 0.0.1 and the actual grammar for 0.0.1. Since I will likely build from the original grammar, I did not delete it. However, this grammar is not current, instead refer to the Mini Grammar. Both of these links can be found on the side.

Design Decisions (aka some random truths that may be surprising)

This is a list of certain design decisions that might be lost or forgotten:

  • No byte literals
  • Expressions are left associated with no user controlled ordering
  • No negations in groupings (even though this make sense)
  • Cannot have a return statement in a block expected to return void aka no empty returns
  • Can only assign to identifiers and attribute accessors
  • Even if nested returns in if statement or nested bodies, still need one at the end
Clone this wiki locally