Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

A programming language that's wise beyond its bytes!🌱🌿🪴

License

NotificationsYou must be signed in to change notification settings

adam-mcdaniel/sage

Repository files navigation

Table of Contents

Community

Join theDiscord server to chat about Sage! Let us know if you have any thoughts or comments about the language!

What is Sage?

Sage is a programming language that tries to be maximally portable, expressive, and intuitive. It borrows some aspects of Rust, C, and Python. It currently has an x86 compiler backend, a C source backend, and a VM interpreter backendwhich can run on the web.

Sage is licensed under theMIT License, and has been under development since April 2022.

Why Sage?

Sage is very portable -- run it on your thermostat! Here's the complete list of core virtual machine instructions and their C equivalents:

InstructionC Equivalent
whilewhile (reg[0]) {
ifif (reg[0]) {
else} else {
end}
set N_0, N_1, ..., N_Xreg[0] = N_0; reg[1] = N_1; ... reg[x] = N_X;
callfuns[reg[0]]();
retreturn;
load Nmemcpy(reg, tape_ptr, N * sizeof(cell));
store Nmemcpy(tape_ptr, reg, N * sizeof(cell));
move Ntape_ptr += N;
wherereg[0].p = tape_ptr;
derefpush(tape_ptr); tape_ptr = *tape_ptr;
refertape_ptr = pop();
index Nfor (int i=0; i<N; i++) reg[i].p += tape_ptr->i;
offset O, Nfor (int i=0; i<N; i++) reg[i].p += O;
swap Nfor (int i=0; i<N; i++) swap(reg + i, tape_ptr + i);
add Nfor (int i=0; i<N; i++) reg[i].i += tape_ptr[i].i;
sub Nfor (int i=0; i<N; i++) reg[i].i -= tape_ptr[i].i;
mul Nfor (int i=0; i<N; i++) reg[i].i *= tape_ptr[i].i;
div Nfor (int i=0; i<N; i++) reg[i].i /= tape_ptr[i].i;
rem Nfor (int i=0; i<N; i++) reg[i].i %= tape_ptr[i].i;
or Nfor (int i=0; i<N; i++) reg[i].i ||= tape_ptr[i].i;
and Nfor (int i=0; i<N; i++) reg[i].i &&= tape_ptr[i].i;
not Nfor (int i=0; i<N; i++) reg[i].i = !reg[i].i;
bitand Nfor (int i=0; i<N; i++) reg[i].i &= tape_ptr[i].i;
bitor Nfor (int i=0; i<N; i++) reg[i].i |= tape_ptr[i].i;
bitxor Nfor (int i=0; i<N; i++) reg[i].i ^= tape_ptr[i].i;
lsh Nfor (int i=0; i<N; i++) reg[i].i <<= tape_ptr[i].i;
l-rsh Nfor (int i=0; i<N; i++) reg[i].i = (uint64_t)reg[i].i >> tape_ptr[i].i;
a-rsh Nfor (int i=0; i<N; i++) reg[i].i >>= tape_ptr[i].i;
gez Nfor (int i=0; i<N; i++) reg[i].i = reg[i].i >= 0;

The compiler can target this limited "core" instruction set, with an expanded "standard" instruction set for floating point operations and foreign functions. The core instruction set is designed to be as simple as possible for anyone to implement their own backend.Try to see if you can implement it yourself for your backend of choice!

The virtual machine has some important optimization properties: Although Sage's VM is avery simple zero-address-code representation, it preserves all the information toreconstruct an LLVM-like three-address-code representation of the original higher level IR. This makes the instruction set capable of applying LLVM's optimizations while beingmuch easier to implement.Sage's innovation is in the backend, not the frontend.

This combination of simplicity and capacity for optimization was my motivation for creating Sage. I wanted to create a virtual machine with the largestspeed + expression + portability toimplementation difficulty ratio, and a high level language that could compile to it. I think Sage is a good solution to this problem.

This project is based on some ideas I had while working onHarbor for a hackathon.

How useful is Sage?

Sage is a very young project, and is not ready for production. It's still possible to write very useful programs in it, though.

SageOS is an operating system with a userspace written in Sage. Its graphical shell and presentation app (both written in Sage) use the FFI to draw to the screen, receive input from the mouse and keyboard, interact with the filesystem, and schedule new processes.You can look at the shell code here.

Shell1Shell2

The presentation app parses PPM image files from the filesystem and renders them to the screen.You can look at the presentation code here.

Presentation

Sage's foreign function interface is simple and can directly call C functions or backend-specific builtins. Check out theweb-demo's foreign function interface example that calls some JavaScript code to draw graphics or alert the user!

How do I use Sage?

To start using sage, install it with cargo:

$ cargo install --git https://github.com/adam-mcdaniel/sage

Then, you can run a sage file with thesage command:

$ sage examples/frontend/interactive-calculator.sg

You can also compile a sage file to C with the--target flag:

$ sage examples/frontend/interactive-calculator.sg --target c$# Or `-t c` for short$ sage examples/frontend/interactive-calculator.sg -tc$ gcc out.c -o out$ ./out

Check out thecode for the web-demo to see how to use Sage in a web page.

What does Sage look like?

Here's an example using thecollections submodule of Sage's builtinstd module!The example uses a custom structPoint as the key for aHashMap instance.

from std.collections import*;structPoint{x:Float,y:Float}implPoint{    fun new(x:Float, y:Float):Point{return{x=x, y=y};}fun move(&mutself, dx:Float, dy:Float){self.x += dx;self.y += dy;}}fun main(){letmut hm =HashMap.make<Point,Int>();    hm.insert(Point.new(4.0,5.0),5);    hm.insert(Point.new(1.0, -1.0), -100);    hm.println();let idx =Point.new(1.0, -1.0);iflet ofSome(result) = hm.get(idx){println(idx," -> ",*result);}else{println("Could not find hm[", idx,"]");}}main();

Here's an example of Sage's structural typing: aRectangle can be created by concatenating the fields of aPosition and aSize!

funmain(){// Add the position and the size to get a rectanglelet rect =Position.make(10,20) +Size.make(30,40);// Print the rectangle and its statsprintln("Rectangle: ", rect);println("Area:      ", rect.area());println("Perimeter: ", rect.perimeter());}// A rectangle has an \`x\` and \`y\` position, a \`width\`, and a \`height\`.structRectangle{x:Int,y:Int,width:Int,height:Int}implRectangle{// Calculate the area of the rectangle    fun area(&self):Int{self.width*self.height}// Calculate the perimeter of the rectanglefun perimeter(&self):Int{2*(self.width +self.height)}}// A type for representing the dimensions of a 2D shapestructSize{width:Int,height:Int}implSize{// Create a new size with the given width and height    fun make(width:Int, height:Int):Size{{ width=width, height=height}}}// A type for representing the position of a 2D shapestructPosition{x:Int,y:Int}implPosition{// Create a new position with the given x and y coordinates    fun make(x:Int, y:Int):Position{{ x=x, y=y}}}main();

Here's an example of Sage's pattern matching: it's easy to deconstruct a value usingmatch,if let, or a simplelet binding. Sage'smatch expressions are very powerful!

// Create a C-like enumenumDirection{North,South,East,West}// Pattern match over a tuple of a Direction, Int, and structmatch(Direction ofSouth,2,{x =5, y = -6}){(ofNorth, _, _)    |(ofEast, _, _)    |(ofWest, _, _)    |(ofSouth,3, _) =>print("Incorrect!\\n"),(ofSouth,2,{x =5, y = -6}) =>{// This will be the branch that matchesprint("Correct!\\n");},    _ =>print("Incorrect!\\n")}// Create a polymorphic Rust-like enumenumOption<T>{Some(T),Nothing}// Define a fallible division operationfun divide(n:Int, d:Int):Option<Int>{if(d ==0){returnOption<Int> ofNothing;}else{returnOption<Int> ofSome(n / d);}}// Match over a division operation with an if-let statementiflet ofSome(n) =divide(6,2){print("6 / 2 = ", n,"\\n");}else{print("6 / 2 = undefined\\n");}

Go to theweb-demo or theexamples/frontend folder to see more code examples.

Feature Roadmap

  • Compiler Backends
    • LLVM (highly desired!)
    • C (fully-implemented but unoptimized)
    • Interpreter (fully-implemented but unoptimized)
    • Web Backend
  • SIMD vector instruction support
  • Static variables and constant expressions
  • Conditional compilation
  • Polymorphic functions
  • Mutability checks
  • Rust-likeenums
  • Patternmatching
  • Structural typing
  • Associated constants and methods
  • Recursive polymorphic types
  • Iterators and list/vector/array comprehensions
  • Hindley-Milner type inference
  • VSCode extension (syntax highlighting, code completion, etc.)
  • Typeclasses
  • no-std implementation of compiler
  • const generics
  • Modules
  • A standard library
    • Type Reflection Module
    • Collections Module
    • Networking Module
    • Filesystem Module
    • Graphics Module
    • Audio Module
    • GUI Module
    • WebAssembly Module
    • Foreign Function Interface Module (create backend with.toml file)
    • Memory Management Module
  • Better frontend parser (switch toNom?)
  • A package manager
  • AST Macros
  • C frontend (compile C to Sage VM)
  • Self-hosting implementation

Where can I learn more?

You can readmy blog post (~20 minute read) about the programming language to learn more about the implementation!

Here's a 23 minute YouTube video that covers how compilers work, and delves into Sage!

Join theDiscord server to chat about Sage!

How do I contribute?

If you want to contribute, you can open an issue or a pull request.Adding backends for other architectures is a great way to contribute! We also need a VSCode syntax highlighting extension!

About the Author

I'm a computer science PhD student at theUniversity of Tennessee, Knoxville🍊. Rust is my favorite language, andI'vewrittenmanyothercompilers. This is the last project I started as a teenager, and I was the only author to touch any of the code up to versionv0.0.2-alpha (12/25/2023)! I'm looking for work opportunities for Summer 2024 (after I finish my Masters degree), so if you're interested in hiring me, please reach out to me atamcdan23@vols.utk.edu!


[8]ページ先頭

©2009-2025 Movatter.jp