cobalt-language / cobalt-lang Goto Github PK
View Code? Open in Web Editor NEWA low-level, compiled programming language made as an alternative to C++ and Rust.
Home Page: https://cobalt-language.github.io
License: MIT License
A low-level, compiled programming language made as an alternative to C++ and Rust.
Home Page: https://cobalt-language.github.io
License: MIT License
Whin compiling a program with a non-constant global, co
exits from a segmentation fault.
Example code:
let x = 2 + 2;
As of right now, there are separate TokenKind
s for intrinsics and annotations, despite being difficult to tell apart at the lexing phase. As a workaround, the intrinsics registry is checked, but this has a problem: it assumes that anything not recognized is an annotation.
While this could be manageable on its own, the error doesn't print correctly for some reason. This makes it difficult to tell what went wrong or where the error is, which could easily detract from Cobalt's usability.
To fix this, I propose merging the Intrinsic
and Annotation
variants into IntrinOrAnn(&'src str, &'src str, usize)
. When lexing, the tokenizer will lex the annotation arguments, roll back to the start, lex it as tokens, then store the token count. In the case of an annotation, the following tokens can be skipped, and for an intrinsic, the extra fields can be ignored.
null
should be a keyword, and parse to a NullAST
, and type
should parse to TypeLiteralAST
Consider this test file, test.co
:
fn add(x: i32 = 1i32: i32 = x;
Try compiling like this:
> co aot test.co -e obj
For me, the compiler hangs with no output.
The hanging happens here:
The issue is that process()
never returns None
. This, in turn, is because param()
never returns an error. The relevant line seems to be
According to LLDB, str
eventually becomes ;\n
and param()
keeps getting called with the same str
. We can add a check for ;
as well, which does solve the hanging, but doesn't properly update pointer (I think) for the parsing string. This results in an ugly error message:
> co aot test.co -e obj
(cobalt errors)
thread 'main' panicked at 'misaligned pointer dereference: address must be a multiple of 0x8 but is 0x1', cobalt-ast/src/varmap.rs:117:66
(stack backtrace)
thread caused non-unwinding panic. aborting.
Abort trap: 6
Consider the following code:
fn add_one(num: &i32) = {
num += 1;
};
The compiler will correctly throw an error, because num
is not mutable, but the error isn't the best:
binary operator "+=" is not defined for types `i32` and `i32`
The error message should ideally communicate that num
is constant.
The situation is similar for variable definitions:
let x = 3i32;
x = 0i32;
will throw the same binary operator not defined error.
Decaying types should be used less, in favor of bidirectional typing. Basically, code like this should work:
# local scope
let x = 10;
let y: i32 = x + 1;
Currently, this fails because 10
decays to i64
when stored in x
, which then can't be narrowed to an i32
, but there's nothing in the code forcing this to be the case. With some lookaheads, this could allow for more type hints to be omitted.
Additionally, a "hole" type should be added for cases in which only a partial type hint is necessary. Tentatively using ?
as a placeholder, we could get something like this:
# global scope
fn returns_a_reference(): &mut i32;
# local scope
let x: &mut ? = returns_a_reference();
++x;
How do we manage to forget this every parser rewrite? Anyways, binary, octal, and hex literals need to be re-implemented (0b
, 0o
, and 0x
prefixes respectively).
A trait with no functions can be used as a marker, and is auto-implemented in the following way:
trait trait_1;
type Type1 = i32;
impl Type1: trait_1;
A few operations can be performed on traits to compare them and create new ones.
The result of adding two traits is a trait which requires its summand traits to be implemented for a type before it itself can be implemented.
trait trait_a;
trait trait_b;
trait compound_trait = trait_a + trait_b;
We say that trait_a < trait_b
if implementing trait_b
would be sufficient but not necessary to implement trait_a
.
Note that simply implementing trait_b
on a type does not automatically implement trait_a
on that type. However, after implementing trait_b
, one can automatically implement trait_a
:
trait trait_a :: {
fn foo();
};
trait trait_b :: {
fn foo();
fn bar();
};
type Type1 = i32;
impl Type1: trait_b :: {
fn foo() = {
# ...
};
fn bar() = {
# ...
};
};
# automatic implementation
impl Type1: trait_a;
trait_c
is considered equal to trait_d
if and only if implementing trait_c
is necessary and sufficient to implement trait_d
. For example, taking the traits as defined above,
trait trait_c = trait_b;
trait trait_d = trait_a + trait_b;
The reason trait_c == trait_d
is because trait_a < trait_b
, so trait_a + trait_b <= trait_b + trait_b == trait_b
. But also trait_b <= trait_a + trait_b
(trait addition has the same semantics as adding positive integers).
ย
There are situations where calling trait functions will result in dynamic dispatch, but this is not always the case. If the only thing known about an object is that it implements a trait, then calling a trait function on that object will result in dynamic dispatch. However, if the type of the object is known at compile time when calling a trait function, the compiler can directly include a call to the specific implementation of the trait function.
In the following few examples, we will use the following trait, and type which implements that trait:
trait trait_1 :: {
fn trait_fn();
};
type Type1 = i32;
impl Type1: trait_1 :: {
fn trait_fn() = {
3i32
};
};
This is an example of where dynamic dispatch occurs.
# Box syntax tbd
fn foo(): Box<trait_1> = {
# ...
};
fn main(): i32 = {
let my_obj = foo();
my_obj.trait_fn()
}
This is an example of where dynamic dispatch does not occur.
fn bar(x: impl trait_1): i32 = {
x.trait_fn()
};
fn main(): i32 = {
let my_obj = 3i32 :? Type1;
bar(my_obj)
};
It's useful to ensure at compile-time that dynamic dispatch does not occur. For instance, dynamic dispatch on hot paths can have a significant impact on performance.
To that end, we propose the following syntax:
fn bar(x: @kact impl trait_1): i32 = {
# ...
};
(`@kact` stands for "known at compile time").
All Cobalt APIs return a Vec<CobaltError>
, which leads to a lot of unnecessary allocations. Replace this by taking a &mut Vec<CobaltError>
as a parameter instead, which allows for allocation reuse and such.
Right now, imports are resolved lazily. They're simply stored in a Vec
, and checked for every variable. This leads to issues with both performance, since the compiler traverses all of the possibilities, and error specificity, since little is known about the variables at the time. The only benefit that they had was that they could be declared out of order, which is already allowed now.
To fix this, they should be changed to be eager. Symbol
s should be able to have a path to another import, and any invalid path components or empty globs should be made errors at the declaration site.
Currently there is no way to pass a pointer to mutable data to a function:
fn changer(x: *mut i32) = {
*x = 4;
};
fn main() = {
let mut x = 0i32;
# compilation error
changer(&mut x);
0
};
I was able to get rid of the parsing errors in b10823b but the above code now results in a seg fault. Right now that change is only for ints-- I wanted to make get this example working before extending that solution to other types.
It's been tried twice. It's failed twice. Make it work.
Goals:
default
or lib
(TODO: figure out what it should be?).$COBALT_DIR
.A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.