18 ms·
Show HN: The Sage Programming Language
Sage is a Rust-like programming language built on a brainfuck-inspired VM with LLVM-like properties.
https://github.com/adam-mcdaniel/sage https://github.com/adam-mcdaniel/sage
Check out the web-demo to run the compiler in the browser, along with a graphical example using JavaScript interop!
https://adam-mcdaniel.net/sage https://adam-mcdaniel.net/sage
Sage is designed to be portable, but also useful. To prove its usefulness, I used it to implement a user-space for a new operating system: SageOS
https://github.com/adam-mcdaniel/sage-os https://github.com/adam-mcdaniel/sage-os
The language also runs on embedded devices, like the flipper zero!
- deleted 3y ago[deleted]
- tines 3y agoSuper cool! How is memory managed? Does it do lifetimes like Rust as well?
- adamthekiwi99 3y agoRight now, memory is managed like C or C++, but I intend to implement lifetimes in the frontend eventually. But definitely no garbage collection (although, a target could implement it against the standard and it would work) Thanks for checking out the project!
- deleted 3y ago[deleted]
- ljm 3y agoMay I suggest replacing the screenshots of code in your readme with just the code? I mean, it's hard to tell but it basically looks exactly like Rust but without lifetimes/borrow checks.
- adamthekiwi99 3y agoHello, yes, I'll go back and add some text examples as well. (I didn't like how GitHub does syntax highlighting though :/) I put all the example programs in text on the web-demo, though, if you want to see more there! You can copy, read, or modify them there! https://adam-mcdaniel.net/sage https://adam-mcdaniel.net/sage It's different from Rust in that it has structural typing and a more Python-like experience! Types are constants, and can be printed with `print` like other values Thanks for your input! I hope you like the project!
- mbroshi 3y agoFYI, Sage is also the name of a mathematical programming language: https://www.sagemath.org/ https://www.sagemath.org/
- adamthekiwi99 3y agoThank you!! Sage will eventually change its name (I just recently found out about this), but I haven't had time to come up with a good replacement name and make art + quippy sayings for it yet! :) I might just go with a completely made up word to avoid any trademarks entirely haha
- e12e 3y agoMint? ;)
- adamthekiwi99 3y agoI like it! And if that's trademarked too I could always fall back on "spearmint" instead haha
- wavemode 3y agooops! https://mint-lang.com/ https://mint-lang.com/
- adamthekiwi99 3y agoHahaha I doubt there's an English word left!!!
- sodapopcan 3y agoRosemary and Oregano are free! Thyme is not.
- adamthekiwi99 3y agoHow long until we enumerate all the herbs?!?!?!?!?! The pantry's almost empty!!!
- king_geedorah 3y agoThe struct composition example with Position + Size is really interesting. I can’t say I’ve seen something that works that way before.
- adamthekiwi99 3y agoThanks so much! I originally added it to experiment with the structural typing -- the type system is a really cool foundation for some awesome future stuff! Thanks for checking out the project, I'm glad I was able to surprise you haha! :)
- NWoodsman 3y agoSage is a programming language with a low level virtual machine based on a Turing tape. The sage virtual machine has a read/write head and a single register. The read/write head moves around the tape, and uses the register as an accumulator. All worldly input is done through a single instruction, and all worldly output is done through a single instruction. Functionality which cannot be achieved with any algorithm, such as using an operating system’s system calls to write to persistent memory, is also done through two instructions: one for sending values to the virtual machine’s foreign function interface, and one for receiving. The goal of the virtual machine is to allow algorithms to be expressed in the same time complexity as they would on x86, but with the smallest possible number of instructions. This allows sage code to be maximally portable: sage’s core instruction set could be implemented in hardware much easier than any modern architecture due to its very small pool of opcodes and their simplicity This is an interesting snippet from your website, did this project intentionally start out as an attempt to design around a Turing machine? Or is it just safe to say that all languages executing on a stack with no heap are Turing machines?
- adamthekiwi99 3y agoThanks for the great question, and for checking out the project! This project started out because I noticed some really interesting properties about compiling brainfuck-like programming languages while writing high level language compilers that targeted these brainfuck-like backends. This is the work that inspired Sage: Harbor (https://github.com/adam-mcdaniel/harbor https://github.com/adam-mcdaniel/harbor). My goal with Sage was to take everything that makes a brainfuck-like programming language easy to compile, while also keeping all the time complexity and optimizations of regular algorithms. So, in that sense, it started out as an attempt to design around a Turing machine -- I wanted this to be a dialect of brainfuck. The VM doesn't use any stack operations, just operations on a tape pointer + an accumulator!
- andyferris 3y agoOne thing I like is exploring the structural typing. However I am confused if the enum's are structural or nominal. It seems necessary to state which enum a variant comes from, like `Direction of South` (for that matter it seems the same is true when constructing structs?). Can you cast enum values to a wider enum type? Or combine different enum values (e.g. through branching - the return of `match`) to construct a value of a wider enum?
- adamthekiwi99 3y agoHello, thanks for the great question! Yes, you do have to specify where a variant comes from, but it doesn't have to be nominal at all! Here's an example: ``` enum Option<T> {Some(T), Nothing} // Create a value with an inline type that doesn't use the nominal `Option` let opt1 = enum {Some(Int), Nothing} of Some(5); // Define a second option value, which is assigned with the first let opt2: Option<Int> = opt1; // Print the result println(opt2); ``` You can create a variant of an inline `enum` type, and then typecheck it against another structurally equal type if you so choose! I hope this example properly illustrates how powerful the structural type-checking is with enums!
- lifthrasiir 3y agoThat's an interesting approach. So the variant name itself doesn't bear the type information but should be uniquely resolved in the type check time, right? It will be pretty much optimal unless you are doing metaprogramming stuffs.
- adamthekiwi99 3y agoThat's right! I think it's a good balance between giving the typechecker the info it needs, but also being flexible in how you can supply it!
- lifthrasiir 3y agoI once pondered about a related generalization: an explicit ellipsis in the identifier. For example `*foo` will match any suitable identifier that ends with `foo`, and it should be uniquely resolved at the end. More ergonomic syntax would use a separator to imply wildcards: for example, an identifier should be a sequence of words separated by `-`, so that `-foo`, `foo-` and `-foo-` really mean `*-foo`, `foo-*` and `*-foo-*` respectively. This can be used to implement a simpler form of module paths.