Open ghost opened 4 years ago
Yes. There is a full language spec PDF under releases which may interest you, although it is pretty high level at explaining stuff. More documentation will be coming soon and if you have any questions, I'm always here to answer them in the meantime.
Yes. In Adept, the full main function is:
func main(argc int, argc **ubyte) int {}
Which is the same definition, just with slightly different syntax of course.
Since you seem to have some knowledge of C, if you like an easy start, you can use functions straight out of C, like malloc, free, printf, fgets, etc. from 'sys/cstdio.adept', 'sys/cstdlib.adept', respectively.
Let me know if/when you have questions!
@IsaacShelton What means in
, out
and inout
Adept keywords?
cstdlib.adept:
foreign getenv(in *ubyte) *ubyte
in
, out
, and inout
are annotations to help the programmer understand the data flow of a function.
func getMessage(out message **ubyte) successful { /* ... */ }
is equivalent to
func getMessage(message **ubyte) successful { /* ... */ }
The reason why you might want them in some cases, is when pointer parameters are taken in and it is unclear how the function uses them. For example:
func myFunction(a_pointer_value **int) successful { /* */ }
If we are looking to call myFunction
, it doesn't say how it uses the pointer we supply it, it could be wanting to read values, or it could be looking to write values, or both.
func myFunction(in a_pointer_value **int) successful { /* */ }
Since in
is now present, we know that myFunction
only intends to read from the pointer and not write to it. So we can now know how to properly supply it with the kind of data it's looking for.
@IsaacShelton I understood. Also I have a question about standart library.
Whats difference between 2.1/
, 2.2/
and 2.3/
libraries, which one is the main? Can I remove 2.1/
or it is necessary for Adept?
Why this happens?
error:
file.adept:4:12: Can't create string literal without String type present!
4| printf("%d\n", 0)
^^^^^^
file.adept:
import "sys/cstdio.adept"
func main(argc int, argc **ubyte) int {
printf("%d\n", 0)
}
In C printf("%d\n", 0);
works correctly.
2.1
, 2.2
, and 2.3
are different versions of the standard library (that correspond to the respective Adept versions that they were made for. It's best to use the latest that your compiler supports, which should be 2.3
.Although you could remove 2.1
, I won't recommend it, because if you ever want to compile programs that depend on that specific version the standard library, they won't be able to be compiled. It's only a few kilobytes anyway so you won't really gain much storage from it.
C-style null terminated strings use single quotes, they are of type *ubyte
which is equivilent to char*
in C.
printf('%d\n', 0)
Adept-style strings use double quotes, and require that you import '2.x/String.adept' to use them. When they run out of scope, they will deallocate memory if they have ownership. They are not null-terminated. The structure of a String
is:
struct String (array *ubyte, length usize, capacity usize, ownership StringOwnership)
Ownership can be passed from one Adept-style string to another using to = from.commit()
. It is important to remember to transfer ownership when strings are returned or run out of scope, because otherwise the contents of the string will be deleted and the return value will reference deallocated memory.
Adept-style strings can be converted to C-style strings using .cstr()
which will return an allocated C-string.
C-style strings can be converted to Adept-style strings using stringConstant(...)
or string(...)
. The difference is that string(...)
will create a copy of the bytes and then have ownership.
The benefit of having Adept-style strings the way they are, is that memory allocation/deallocation is minimized and the String
type also works as a StringView
type.
Lists in Adept also have the same ownership based model as Strings.
Also a random side note: Character literals are defined using 'A'ub
instead of 'A'
like in C.
Hope this helps!
@IsaacShelton What means pragma compiler_supports '2.2'
? Should I use it with 2.2/
standart library or I can ignore it? Is this similar to gcc -std=<version>
?
Yeah it is similar to gcc -std=<version>
. All it does is tell the compiler that it expects features from Adept 2.2 to be available. You can ignore it.
@IsaacShelton Why don’t you add the -std
option? I thinks that this will be better than just importing from <version>/
.
@t0md3an I hadn't thought of doing that... That's a good idea. My one concern is that the version of the stdlib that the program intends on using might not be obvious from the source code, which could lead to confusion on how to compile programs. One of the big things that I want to keep is the ability to compile any adept program by just running adept
. This could be fixed though with something like pragma default_std '2.3'
.
I think not having to put 2.3/
for every standard import would be good, but I want to retain the ability to compile programs without having to guess what version of the stdlib they use. Because unlike the C stdlib, sometimes thinks get changed/moved/deleted and if I want to keep improving the language while still maintaining backwards-conpatibility, it's important to have the ability to drastically change the standard library.
If I was to implement this, there are three main syntax possibilities: (fake folder, but retains file like importing)
pragma default_std '2.3'
import "std/basics.adept"
func main {
print("Hello World")
}
or (cleanest and easiest to type, least intuitive)
pragma default_std '2.3'
import basics
func main {
print("Hello World")
}
or no special syntax (most flexible, hard to type)
#set __std__ '2.3'
#import __std__ + '/basics.adept'
func main {
print("Hello World")
}
(default_std
could be named something else, I haven't considered different names for it yet)
pragma compiler_version
/pragma compiler_supports
could also be used to infer the default stdlib version
If I do add this, I'm currently leaning towards the middle one. I would probably also add a warning if pragma default_std
is used but not specified, in order to encourage specifying which version is expected.
Although adding just -std=2.3
looks good in the short term, it makes it harder to compile old programs written with it. Unless you use a build script like Makefile, which I want to avoid.
What do you think?
@IsaacShelton
If you are focusing on the code and not on how to build it, then it is better to use default_std
, but in my opinion -std
would be a good option to start with (plus it will not need to be used when specifying the default (current 2.3) version, only for rollback (2.1, 2.2))
rollback to stdlib v2.2:
adept main.adept -o a.out -std="2.2"
use default stdlib:
adept main.adept -o a.out
@t0md3an Yeah I think that would well. The idea behind default_std
is that you can still run just adept
to compile it, but if you want to force a different stdlib version, you can use -std="2.1" or whatever.
adept
which will use the specified default_std
(or the latest version if not specified)
vs.
adept -std=2.1
which ignore any specified default_std
, and use a specific version
I'm liking this more now, I'm gonna implement it and see how it feels to use
@t0md3an I've implemented it, and I really like it so far.
import basics
func main {
print("Hello World!")
}
compiled with adept -std=2.3
.
Or there is also the corresponding pragma directive:
pragma default_stdlib '2.3'
import basics
func main {
print("Hello World!")
}
compiled with adept
You can pull it down and try it out if you like. Just be careful to not overwrite your Makefile.
@IsaacShelton
Thanks for adding the -std
and default_stdlib
, it makes it easier to choose the std version.
Also are you planning to add sysroot for import? I mean something like /usr/include
in C.
@t0md3an
A sysroot for import already exists, it's just the folder named 'import' wherever the adept executable is located. Everything in that folder you can import from anywhere just like /usr/include
. Depending on where you put the adept executable, the folder should be at /Library/Adept/2.x.0/import
or ~/Library/Adept/2.x.0/import
. If you have the latest build from today, you should be able to see your import folder location and your stdlib folder location by doing adept --version
.
Mine looks something like this:
Platform: MacOS
Adept Build: Adept 2.4 - Build Aug 30 2020 11:55:26 CDT
Adept Version: 2.4
Pkg Manager: disabled
Import Folder: "/Users/isaac/Projects/Adept/bin/import/"
Stblib Folder: "/Users/isaac/Projects/Adept/bin/import/2.4"
@IsaacShelton
Importing from import/2.4
- import module.adept
?
Importing from import/
- import "module.adept"
?
And how to import local file?
@t0md3an To import a local file you can do
import "my_file.adept"
The way that Adept finds which file you're referring to, is by first looking in the same directory as the source file, and then looking in import/
if it couldn't find it locally.
When using the name of a standard library component
import String
Adept will only look for it at {stdlib}/{name}.adept (so import/2.4/String.adept
in this example)
@IsaacShelton
As I understood:
*ubyte
- '
**ubyte
- "
Does it work like that?
I thought they were the same.
Is it possible to allow "
in cstdio printf()
?
'
, but not with "
'
- *ubyte
which is like char*
or char[]
in C
"
- String
which is kinda like std::string_view
in C++. Expect it also has fancy stuff going on behind the scenes.
When using string values ('
or "
) for things like pragma directives, meta directives, imports, and such, 'string'
and "string"
are treated the same.
import basics
import "my_file.adept" // is the same as:
import 'my_file.adept'
but inside the actual code, they are different.
func main {
name *ubyte = 'This is a null-terminated string'
// is different from
name String = "This is not a null-terminated string"
}
"
in printf with what's in the language right now. If more support for varargs is added sometime, then an implementation of printf that takes a String
could be made in the future.Something like:
func printf(format String, ...) int {
// TODO: Write printf that uses the String type
}
As weird as it feels to use '
for C-strings, you get used to it pretty fast.
printf('Hello World!\n')
And "
has its own way of doing printf like stuff, by using %
.
import basics
func main {
value int = 10
print("Value is %" % value)
firstname String = "John"
lastname String = "Smith"
fullname String = "% %" % firstname % lastname
}
If you hate '
, then I would recommend just learning normal "
strings.
@IsaacShelton
import lib/lib
If lib/lib
placed in 2.x
returns an error:
Expected type!
Works only if:
import '2.4/lib/lib'
@t0md3an
Nice catch!
This is fixed now.
import lib1/lib2
import lib1/lib2/lib3
If you want autocompletion in AdeptIDE to still work properly, you'll need to rebuild it after doing make deepclean
.
@IsaacShelton
1. Does Adept support Linux?
2. Can you give me example of using vectors in Adept?
3. How to use pointers in Adept?
Yes, but the precompiled libraries such as GLFW/OpenGL/OpenAL/ALUT/stb_image/etc. that come with AdeptImport
don't work out of the box.
By vectors, do you mean something like std::vector
or something like Vector3f
I'm going to assume what you already know what pointers are, if you don't already know what pointers are I would recommend first watching [https://www.youtube.com/watch?v=iChalAKXffs]()
Pointers in Adept are mostly identical to those in C/C++.
Pointer types are written with an *
in front of the type that the pointer points to.
x *int // Adept
as opposed to in C, where *
is put afterwards
int* x; // In C/C++
The "address of" &
and "dereference/value of" *
operators are the same as in C/C++
import 'sys/cstdio.adept'
import 'sys/cstdlib.adept'
func main {
x int = 5
y *int = &x
*y = 10
printf('x = %d\n', x)
}
The one difference though is that when adding/subtracting from pointers in Adept, they do not take into account the size of the type they are pointing at:
// Adept
x *int = null
printf('%p\n', x + cast ptr 1)
// This will have printed '0x00000001'
// C/C++
int *x = NULL;
printf("%p\n", x + 1)
// This will have printed '0x00000004' on most systems
Also like in C/C++, pointers to blocks of memory can serve as arrays and can have the []
operator used on them:
some_integers = malloc(10 * sizeof int)
defer free(some_integers)
// Set each element to be the same as its index in the array
repeat 10, some_integers[idx] = idx
// Print the 4th element, which should be 3
printf('%d\n', some_integers[3])
@IsaacShelton Thanks. I mean something like std::vector
in Adept.
@t0md3an Adept has List
which serves the same purpose as an std::vector
in C++.
import basics
func main {
// Declare a List that holds 'int's
my_integers <int> List
// Adds numbers 0 through 9 inclusive to the list
repeat 10, my_integers.add(idx)
// Print each integer in the list
each int in my_integers, printf('%d\n', it)
}
Similar to the String
type in Adept, List
s are destroyed when they go out of scope.
import basics
func main {
// Obtain ownership of a list that will be given to us by 'getMyListOfIntegers()'
my_integers <int> List = getMyListOfIntegers()
// Print each int the the list
each int in my_integers, printf('%d\n', it)
// (my_integers has ownership, so the list is destroyed)
}
func getMyListOfIntegers() <int> List {
list <int> List
list.add(1234)
list.add(314159)
// We want to return 'list', but list will be destroyed by the time
// we pick back up at the 'my_integers <int> List = ...' line in main,
// because it is going to run out of scope
// To fix this, we will transfer the ownership of 'list' to a temporary list which will
// give ownership to 'my_integers' when the function is called. We do this
// using commit()
// NOTE: If this isn't done, the program will crash
return list.commit()
}
In order to get certain elements of a list, you can use the get(usize)
and getPointer(usize)
methods:
func main {
// Get list of integers
my_list <int> List = getMyListOfIntegers()
// Print the first element
printf('%d\n', my_list.get(0))
// Get a pointer to the second element and change it
my_pointer *int = my_list.getPointer(1)
*my_pointer = 123456789
// Print the second element, which will be 123456789 since we changed it through our pointer
printf('%d\n', my_list.get(1))
}
By default, .get
and .getPointer
do bounds checking, if this is not desired, you can disable it by putting
#set list_bounds_checks false
and at the beginning of your file
@IsaacShelton I understood.
Why Adept uses import
instead of #include
, (as I know Adept has preprocessor, but does not has #include
)?
@t0md3an
Adept uses import
instead of #include
because it eliminates the need for headers guards.
In C you have to do something like this:
#ifndef _MYFILE_H
#define _MYFILE_H
// ... definitions ...
#endif
In order to prevent the file from being "included" more than once.
Adept only lets you "include" a file once when you use import
or #import
, so you can skip the header guard
// ... definitions ...
So if you import a file more than one time, you only end up with one copy of the definitions
@IsaacShelton
Maybe I found a bug.
Code:
#import basics
func main()
{
print("This is my first program.")
}
Result:
file.adept:1:9: Warning: Usage of undefined transcendant variable 'basics'
(you can disable this warning with '--unsafe-meta' or 'pragma unsafe_meta')
file.adept:1:1: The file 'undef' doesn't exist!
1|#import basics
^^^^^^^
@t0md3an
This is because #import
treats basics
as a preprocessor variable.
The warning
file.adept:1:9: Warning: Usage of undefined transcendant variable 'basics'
is complaining that the preprocessor variable 'basics' is undefined. And when #import
then uses the contents of the preprocessor variable 'basics', it says:
file.adept:1:1: The file 'undef' doesn't exist!
Because the preprocessor variable that it got the value from, is undefined.
When using #import
, it's usually to do something like:
#default my_version_number 3
#default my_library_location "~/my_library/src/"
#import my_library_location + "my_library" + my_version_number + ".adept"
which in this example, boils down to
import "~/my_library/src/my_library3.adept"
So yeah in this case, it's not a bug, it's just that #import
works different from import
and doesn't support importing like that
@t0md3an The keywords[]
array has to be sorted alphabetically for it to work. And the TOKEN_*
values from BEGINNING_OF_KEYWORD_TOKENS
to MAX_LEX_TOKEN
in token.h
need to be sorted to match the alphabetical order of those in keywords[]
Also if you care about debugging tokenlists, you have to reorder global_token_name_table[]
so that the token ids match the name of the tokens
In token.h
you need to replace
#define BEGINNING_OF_KEYWORD_TOKENS TOKEN_POD
#define TOKEN_POD 0x00000050
#define TOKEN_ALIAS 0x00000051
#define TOKEN_AND 0x00000052
#define TOKEN_AS 0x00000053
#define TOKEN_AT 0x00000054
#define TOKEN_BREAK 0x00000055
#define TOKEN_CASE 0x00000056
#define TOKEN_CAST 0x00000057
#define TOKEN_CONTINUE 0x00000058
#define TOKEN_DEF 0x00000059
#define TOKEN_DEFAULT 0x0000005A
#define TOKEN_DEFER 0x0000005B
#define TOKEN_DELETE 0x0000005C
#define TOKEN_EACH 0x0000005D
#define TOKEN_ELSE 0x0000005E
#define TOKEN_ENUM 0x0000005F
#define TOKEN_EXTERNAL 0x00000060
#define TOKEN_FALLTHROUGH 0x00000061
#define TOKEN_FALSE 0x00000062
#define TOKEN_FOR 0x00000063
#define TOKEN_FOREIGN 0x00000064
#define TOKEN_FUNC 0x00000065
#define TOKEN_FUNCPTR 0x00000066
with
#define BEGINNING_OF_KEYWORD_TOKENS TOKEN_POD
#define TOKEN_POD 0x00000050
#define TOKEN_FUNC 0x00000051
#define TOKEN_ALIAS 0x00000052
#define TOKEN_AND 0x00000053
#define TOKEN_AS 0x00000054
#define TOKEN_AT 0x00000055
#define TOKEN_BREAK 0x00000056
#define TOKEN_CASE 0x00000057
#define TOKEN_CAST 0x00000058
#define TOKEN_CONTINUE 0x00000059
#define TOKEN_DEF 0x0000005A
#define TOKEN_DEFAULT 0x0000005B
#define TOKEN_DEFER 0x0000005C
#define TOKEN_DELETE 0x0000005D
#define TOKEN_EACH 0x0000005E
#define TOKEN_ELSE 0x0000005F
#define TOKEN_ENUM 0x00000060
#define TOKEN_EXTERNAL 0x00000061
#define TOKEN_FALLTHROUGH 0x00000062
#define TOKEN_FALSE 0x00000063
#define TOKEN_FOR 0x00000064
#define TOKEN_FOREIGN 0x00000065
#define TOKEN_FUNCPTR 0x00000066
Because BEGINNING_OF_KEYWORD_TOKENS + (index in keywords array) = TOKEN_FOR_THAT_KEYWORD
This is done this way, so that when you convert from an index in the keywords array to a token ID, it is of time O(1)
instead of O(log(n))
or O(n)
.
The // 0x00000000
isn't necessary, it just helps to show me which token ID goes with which string.
@IsaacShelton Thanks, now it works
@IsaacShelton
Is there any difference between #print_error
and #place_error
? Which one is similar to C/C++ #error
?
Why I can't sqrt integer sqrt(4)
?
file.adept:7:11: Undeclared function sqrt(int)!
7| print(sqrt(4))
^^^^
Potential Candidates:
sqrt(double) double
print()
integer? It is very uncomfortable.file.adept:7:5: Undeclared function print(int)!
7| print(0)
^^^^^
Potential Candidates:
print(String) void
Please add more flexibility (I mean printing all types, adding strings to printf... etc) this will make programming in Adept more comfortable.
I can make a site for your language and promote it on reddit or Twitter. I mean that I want to help.
#print_error
also prints a newline, where as #place_error
does notThe equivalent of #error "Something went wrong"
is:
#print_error "Something went wrong!"
#halt
Because it's just not a part of the standard library even though it probably should be. If you want to be able to sqrt any number, you can just define another function like this:
func sqrt(number $T~__number__) $T = sqrt(number as double) as $T
Also make sure that you import either math
or "sys/cmath.adept"
I'll take this into account and add this line into the standard library sometime soon
Same reason as above, just not a part of the library yet even though it probably should be. In the mean time, you can add this which will allow you to print any number like that:
// NOTE: Requires 'parse', which is already included by 'basics'
func print(number $T~number) { print(toString(number)) }
4. Yes I like all those ideas, and you are very welcome to help promote the language if you want
@IsaacShelton about 2,3 I have basics
and cmath
imported
@t0md3an I was looking around in basics
and it turns out everything including parse
, math
, and 'sys/cmath.adept'
is already imported from it directly or indirectly, so it actually doesn't matter if you imported them. It doesn't hurt though.
@IsaacShelton
Have you any plans about improving sys/
libraries (libc)? I mean add support for String
or something.
@t0md3an I haven't thought about it, but it wouldn't be that hard to do. Maybe I'll add libraries like '2.4/cstdio.adept' which import 'sys/cstdio.adept' and add extra wrapper functions.
@IsaacShelton
How to read user input?
C++:
#include <iostream>
int main(void)
{
int num;
cin >> num;
}
@t0md3an You can read user input with the scan* functions from terminal
. They definitions are:
func scan String { ... }
func scan(prompt String) String { ... }
func scanInt int { ... }
func scanInt(prompt String) int { ... }
func scanIntInto(out result *int) successful { ... }
func scanIntInto(prompt String, out result *int) successful { ... }
func scanDouble double { ... }
func scanDouble(prompt String) double { ... }
func scanDoubleInto(out result *double) successful { ... }
func scanDoubleInto(prompt String, out result *double) successful { ... }
An example using scan
:
import basics
func main {
name String = scan("What is your name? ")
printf("Welcome %S!\n", name)
age int = scanInt("How old are you? ")
printf("Ok you are %d years old\n", age)
answer double = scanDouble("What is 1 / 3? ")
printf("You answered: %f\n", answer)
}
@IsaacShelton
Is there any input
for all data types like cin
(cin
can scan all data types)?
@t0md3an Never thought to add one, but I think it's a good idea I'll add it
@IsaacShelton
Also can you give me full list of %<data type>
for printf
please.
@t0md3an There is now a version of scan
which works how you described:
You can use it like this
scan("Enter a value: ", def my_variable int)
Example using it:
import basics
func main {
scan("What is your name? ", def name String)
printf("Welcome %S!\n", name)
scan("How old are you? ", def age int)
printf("Ok you are %d years old\n", age)
scan("What is 1 / 3? ", def answer double)
printf("You answered: %f\n", answer)
}
@t0md3an Not all the types are directly supported yet, but as long as you don't disable runtime type information, you can use the following:
%d - works for bool, successful, byte, ubyte, short, ushort, int, uint, long, ulong, usize
%f - works for float, double
%s - works for *ubyte
%S - works for String
%p - works for ptr, *MyType
@IsaacShelton How to print **ubyte
in printf
?
@t0md3an If you want to print the pointer itself:
pointer **ubyte
printf("%p\n", pointer)
If you want to print the contents as an array:
each element *ubyte in [pointer, number_of_elements] {
printf('%s\n', element)
}
If you want to print the contents as an array and don't need printf:
print(array(pointer, number_of_elements))
Or if you want to print contents as an array as an argument to printf
printf("Array contents: %S\n", toString(array(pointer, number_of_elements)))
For example:
import basics
func main(argc int, argv **ubyte) {
printf("Got arguments: %S\n", toString(array(argv, argc)))
}
Or you can just use toString(*$T, usize)
with the latest version of AdeptImport
:
import basics
func main(argc int, argv **ubyte) {
printf("Got arguments: %S\n", toString(argv, argc))
}
B@IsaacShelton What is this messages after executing binary file of Adept WARNING: <something>
(#6, #8)? Is this is a part of Adept or this is LLVM system? Is this is a debugger?
@t0md3an What does the warning say?
@IsaacShelton I am just asking about the system of errors and warning in binary executable file, how this works and what program does this.
@t0md3an Most of the warnings that start with WARNING:
are from Adept or files written in Adept. It's usually not related to LLVM unless it mentions it.
@IsaacShelton
When you'll release documentation for Adept? (I really want to learn Adept.)
Is there any way to read command-line arguments like in C?