This software is currently under development and not ready for production use.
Expect to find errors, mistakes, and bugs. Future updates may be incompatible with any code you write now, in whole or in part.
Official Documentation and Introduction
Seabass is a public domain, self-retargeting, self-extending, self-modifying metaprogramming language and general-purpose metaprogramming tool.
What does that mean?
Seabass is a public domain dedication, so that your joy may be full. No attribution required.
For legalese details, see the LICENSE file.
Seabass is, fundamentally, a programming language. You can write software with it. The syntax of the language is fairly unique, however it is close cousins with Lua, BASIC, and C.
Seabass is more than just a single language and single compiler, it is a general-purpose software creation assistance tool. Using Seabass, you can compile arbitrary code for arbitrary targets with as many (or as few) layers of abstraction in-between as you desire.
Seabass aims to "fill the gap" for virtually every conceivable software-authorship use case. It is not a domain specific language.
Seabass requires that the compilation unit write code to define how it should be compiled. This code is given virtually unrestricted access to the internals of the compiler and runs at compiletime. It is responsible for generating "target code" (such as machine code) using all of the functions, types, methods, and global variables in the unit.
Seabass allows the definition of special codegen
functions, methods, and
global variables which exist exclusively at compiletime.
These functions, methods, and variables effectively extend the capabilities of the compiler, and should be thought of as a natural extension of the compiler.
Seabass allows codegen
code to manipulate the internal state of the
compiler, including the abstract syntax tree (AST, the internal
representation of a program) and token stream.
Seabass includes a special operator (@) for invoking codegen
functions
to "take over" parsing. They can perform arbitrary manipulations of the
program's source code (as represented in the token stream).
In practice, this allows you to not only define entirely new syntaxes, language constructs, and abstractions, but entirely new programming languages.
Here is a minimal complete example program, which you can find under 'tests2/toc_fib_example.cbas'
#include <toc_native_user>
/*
Metaprogramming- Defining a custom syntax.
We take the existing DSL for pretty-printing
called "pprint" and mutate it by dependency injection.
*/
@wksht prnt [
[
@pprint [println mutoa ARG1]
][
ARG1
]
]
fn predecl mutoa(char* dest, uint value);
fn predecl matou(char* in)->uint;
fn inline fib(uint n)->uint:
if(n < 2)
return 1;
end
uint a=1
uint b=1
uint c=1;
n = n-2
while(n)
c = a + b;
a = b;
b = c;
n--
end
return c
end
fn pub main(int argc, schar** argv)->int:
if(argc < 2)
@prnt[
/ "Usage: fib 13"
]
sys_exit(1);
end
println("Welcome to the fibonacci number calculator!");
uint qq = matou((char*)argv[1]);
@prnt[
/ "You asked for the fibonacci number..."
/int (qq)
/ "That fibonacci number is:"
/int (fib(qq))
]
return 0;
end
/*
Our code generator...
*/
fn codegen codegen_main():
cg_emitC(SEABASS_STDLIB_PREFIX);
end
/*
EXAMPLE: Low level library code...
*/
fn matou(char* in)->uint:
/*
Decimal only...
*/
uint retval = 0;
while(
(in[0] >= '0') &&
(in[0] <= '9')
)
retval = retval * 10;
retval = retval + (in[0]-'0');
in++
end
return retval;
end
fn mutoa(char* dest, uint value):
if(value == 0)
dest[0] = '0';
dest[1] = 0;
return
end
/*Determine the highest power of 10.*/
if(1)
uint power
power = 1;
while(value/power >= 10)
power = power * 10;
end
/*found the highest powerer of 10*/
while(power)
uint temp
temp = value/power; /*if we had the number 137, we would have gotten
100 as our power. We now divide by power to get the highest digit.*/
dest[0] = (temp + ('0')); dest++;
value = value - temp * power; /*Get rid of the highest digit.*/
power = power / 10 /*Divide power by 10.*/
end
end
:ending
dest[0] = 0
return;
end
Here is another example program which uses the standard library. It prints the contents of a directory:
#include <toc_native_user>
//mutate pprint....
@wksht prnt[
[
@pprint [println itoa ARG1]
][
ARG1
]
]
fn pub main(i32 argc, char** argv)->i32:
if(argc < 2)
@prnt[
/ "Usage:"
/ "dirlist /path/to/the/directory"
]
sys_exit(1);
end
u32[1] nentries;
char**[1] listing;
errno = 0;
if(
getdirlist(argv[1], listing, nentries)
)
@prnt[
/ "Could not load directory:"
/ (argv[1])
]
sys_exit(1);
end
if(errno)
perror("<C library> Error:");
sys_exit(1);
end
u64 i
for(i = 0; i < nentries[0]; i++)
println(listing[0][i]);
end
end
fn codegen codegen_main():
cg_emitC(SEABASS_STDLIB_PREFIX);
end
-
Arbitrary compiletime execution. Seabass lets you write arbitrary code which runs at compiletime, called "codegen" code.
-
Compiler Reflection. Seabass lets compiletime ("codegen") code see the internal state of the compiler and even manipulate it. Furthermore, you can define new state variables and data structures, meaning you can effectively extend the compiler by writing code it understands, during compilation.
-
Parsehooks, aka "Parser Hooks". Seabass lets you write functions which temporarily "take over" parsing from the compiler, letting you define totally new syntaxes. You can manipulate the input fed to the parser as well as the internal representation of code which has already been parsed (The Abstract Syntax Tree, or "AST").
-
Code generators. Seabass allows you to define how your code will compile into some target code. This means that, unlike pretty much every other compiler, cross-compilation does not require an entirely new toolchain. All you need is a new code generator. You don't have to compile a totally new compiler just because you want to compile for the M68k- you just need a new code generator.
Because of these "superfeatures" the following are all true with Cbas:
-
You can write software which is theoretically infinitely portable. If a particular computer platform has the features necessary to implement the program (i.e. enough memory, network access, a filesystem...) then the only "new" thing that needs to be written is a code generator for that target, and once that is written, all you have to do is #include it in your code!
-
You can write totally new programming languages easily. If you want to write your own programming language, all you have to do is write a parsehook in Seabass which compiles your language into seabass (which is made much easier with the wide variety of metaprogramming tools available to you). This isn't just restricted to DSLs either. If you want a new, higher-level general-purpose programming language with features seabass doesn't have, you can write it in seabass.
-
Seabass is "Domain Complete"
Disregarding memory limitations (You might have issues compiling a 3 Gigabyte program), file size constraints, and implementation bugs, there is at least one seabass program which can generate a given program in another language.
It depends on what you compile CBAS code into. If you decide to compile Seabass into BASIC then it might not be very fast.
If you're compiling to C the answer is "Yes".
Cbas translates virtually 1-to-1 to C. The higher level capabilities of the language that don't exist in C are compiled into equivalent C code (Assuming no compiler bugs).
Lower-level capabilities that seabass has which C does not have (tail calls, dispatch tables) rely either on an optimizing compiler (for tail calls) or C compiler extensions (for dispatch tables).
If there is any feature of C not implemented in Seabass, rest assured, it can still be accessed by writing inline C (There is a special tool just for it, @inline_C!)
Yes. The compiler itself requires a 64 bit environment but programs written in the language could target anything from a microcontroller to a modern-day x86_64, ARM, or RISC-V machine.
For user-mode applications on POSIX-compliant (and posix-like) systems, yes.
It supports most of the useful parts of the C standard library, or supplements them, where appropriate.
It is currently in development but is already sufficient to write simple unix applications.
I have plans to extend the library to support most of posix.
Because Cbas is a metaprogramming language, it scales almost infinitely. If the base level language does not provide the set of abstractions needed for your work, you can write new ones.
Writing large, long-lived maintainable codebases which require continual modification should be easier in CBAS than C, C++, or Rust.
Yes. The asm
statement allows you to write code inside of a string literal which will be
emitted directly to the target code file. In practice, this means you can write C code
inside of a string literal and have it bake into your program.
I have written a syntax highlighting file for my editor of choice, micro, however you should be able to fairly trivially modify a syntax highlighting file written for C to highlight Cbas code.
No. The memory management scheme is identical to C, but with automatic constructors and destructors.
You can implement your own memory management schemes in higher level languages if you wish.
Structs can be given an alignment. Simply enter an integer value somewhere inside the struct definition, and that will be its alignment.
In the TOC converter? Officially, no, but you can write
inline C. math.h
is included by default, so you should
have access to all of the standard math library functions
too.
Yes. Type the word union
anywhere in a struct/class definition. It
will be declared as a union.
I don't really think so.
Interop with C is fairly trivial, and examples are provided.
Compiletimes are typically much slower than C, however the increased development speed and quality should more than make up for it.
Yes. Using __builtin_read_file
you can get the contents
of any file given a path at compiletime.
You can then tokenize this however you see fit.
With seabass, it is therefore possible to write your own compiletime compilers, all the way from tokenization to code generation. What other language do you know which can do that?
Of course! The C code generator already uses this feature to do the standard includes.
In the public domain, so that your joy may be full.
https://github.com/gek169/seabass
https://codeberg.org/gek/seabass
All Glory to the Lord Jesus Christ, whose blessings I do not deserve.
For the maximum utilization of Man's God-given talents.
A wide variety of notations are needed not only to enable a programmer to best express his ideas in code, but also to help that programmer think about programming.
To put it another way, high-level computer programming languages perform two distinct functions for an individual programmer, intellectually:
-
They work in terms of concepts which mimick his thinking. The language reads much like he thinks.
-
They help him create new ideas and understand solutions to his problems. He thinks in terms of higher-level building blocks because the language provides them.
You should recognize these two as a cycle. A language can either mimick a programmer's existing thinking, or help him generate new ways of thinking about his code.
Thus, new programming notations literally increase a programmer's raw programming skills in the mind, in addition to enabling faster handiwork because the language "feels" natural to his mind.
I believe this cycle - inventing an abstraction, learning to use the abstraction, becoming a better thinker because of the abstraction - is a general mode for self-improvement as well as productivity increase. It utilizes a man's mind the best that a purely text-based programming language can.
It is thus necessary to be able to create arbitrary abstractions and use them in an existing codebase with as little effort as possible. You must be able to think up entire new programming paradigms or languages and then implement them the same day.
Very few languages I know of are capable of this in a pragmatic sense, and especially not C or C++, the former of which has been my language of choice for years.
It is not enough to simply allow arbitrary syntaxes to be written. In order to make a truly portable language, it must be possible to write your own code generators.
This makes it so that you don't have to go through the hassle of installing a new toolchain just because you want to write code for an unusual platform.
Want to write code for the Dreamcast?
Want to compile your game to webassembly?
Your employer says you have to write code in a language you don't want to?
Write (or get) a code generator. Bam. Done.
I disdain Rust and C++ for their obtuse language design decisions that make those languages difficult to understand. I find languages like Lua and Basic to be far more comfortable and pleasing to my soul.
So instead of making design decisions like C++ or Rust, I chose to do things more like Lua and BASIC. I wanted a language that was approachable, powerful, and portable.
I want my tools to belong to me- not some corporation, not the FSF, not GNU, me.
So Seabass is public domain- CC0. Every person who downloads the compiler owns it all for themselves. You will never have GPL lawyers coming after you because you forgot to publish source code for a patch.
Seabass is my gift to you in perpetuity.
Don't think of it as my language- think of it as your language.
I hate all existing build systems. Here is a 1 minute tutorial for building Seabass from source and running the unit test:
# From the root directory of the project
cc -O3 *.c -o cbas
cd tests
../cbas vm_test.cbas
If it tells you "codegen_main executed successfully" it worked.
Put cbas
somewhere like /usr/local/bin/
(Somewhere on your PATH
so you can call it by name)
Want to install the standard library and compile a program to C? Here's how you do it.
Navigate to the root of the repository. Run these commands with administrator privileges to install the standard library:
# From the root of the repository....
mkdir -p /usr/include/cbas/
# This just copies the contents of library/ to /usr/include/cbas/
cp -a library/. /usr/include/cbas/
Then compile the fibonacci number program like so:
cbas tests2/toc_fib_example.cbas
# the linker flags are needed to get the standard library stuff,
# and also making signed-integer overflow defined.
cc -O3 auto_out.c -lm -lpthread -fwrapv -o fib
# use your shiny new fibonacci number program
./fib 20
Or the directory listing program like this:
cbas tests2/dirlist.cbas
cc -O3 auto_out.c -lm -lpthread -fwrapv -o dirlist
./dirlist .
Never use Cmake again!
This software was written for the public domain using the undeserved blessings of our Lord Jesus Christ. I am a sinner, unworthy of recognition or appreciation.
For you, Programmers:
This is the blessing wherewith I bless you:
You are made in the Image of God, designed to have dominion over the works of God's hands. May this piece of software increase the joy you find in the labor of your hands.
May your adventures be as grand as your imagination can entail.
As stated before, Seabass is a public domain work. It does not require you to make any attributions or include duplicate license files in copies or derivative works that you distribute, nor does it impose any "copyleft" requirements.
However, I have personal requests for anyone using Seabass or deriving from it:
Please do not remove the dedications to our Lord Jesus Christ. I am fine with you removing my name or initials, but the project is dedicated to Christ. If this offends you, it is my request that you simply avoid my software rather than defile it by removing the dedication and mentions of our Lord Jesus Christ.
The only exception is if you have a real technical reason (i.e. porting Seabass to a low-memory platform) for removing the dedication. In which case you should simply move the dedications into a separate file.
None of my work would be possible without the undeserved blessings of God our Father and our Lord Jesus Christ. I'm not a good christian, mentioning him in my work is all that I can really do. I seek to obey the command to acknowledge God in all my works, so that he will bless them.
Again, none of this is a license requirement. It's what you will do if you care about me or remembering God. Whether you appreciate my work will be shown by whether or not you preserve the mentions of Jesus in Seabass's source code, C code generator, help screen, and manpages.