From b2f3e0e304425e77a2b8ef0ee3643a879e0359a0 Mon Sep 17 00:00:00 2001 From: nobody5050 Date: Sat, 28 Jan 2023 17:04:45 -0600 Subject: [PATCH 01/33] Initial draft of 1.0.0 --- readme.md | 178 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 100 insertions(+), 78 deletions(-) diff --git a/readme.md b/readme.md index e78b389..8a18bfb 100644 --- a/readme.md +++ b/readme.md @@ -1,18 +1,17 @@ # The Sack programming language -> Spec v0.0.5 SemVer +> Spec v1.0.0 SemVer Sack is a dynamically typed scripting language for beginner programmers that focusses on readability and ease of use over speed. ## Goals -- Sack is Simple. A program is executed from top to bottom with no async requests. -- Sack is small. The specification is minimal, and simple to implement in a language of choice. -- Sack is Extensible. While the specification is not indended to be open ended, It allows for changes where necessary. -- Sack is Easy. Keywords should be recognizable to both programmers and non programmers alike. +- Sack is Simple. A program is executed from top to bottom. This means asynchronous code is impossible. +- Sack is small. The specification is minimal, and easy to learn as well as implement. +- Sack is Extensible. While the specification is not indended to be open ended, It should allow for new features where required. +- Sack is Easy. Language reserved words should have their basis in common english, and code written in it should prioritize readability. ## Basic notes -- Sack does not have inbuilt networking or the ability to import such functionality from other files. - Sack's default extension is `.sk` or `.sack`. All others are considered invalid by this spec version. - Variable types can be one of the following: - String @@ -20,58 +19,14 @@ Sack is a dynamically typed scripting language for beginner programmers that foc - Decimal (32 bit float) - Bool (`true` or `false`) - None (`none`) -- Sack enforces a style guide for improve readability, It is left up to the implementation whether this is a default warn or error. + - Identifier +- Sack enforces a style guide to improve readability, it is left up to the implementation whether this is a default warn or error. For simplicity, the style guide will not be described in this spec, however the example code will follow it. -## Basic keywords - -### Functions - -Functions are declared using the `func` keyword, followed by a declaration of local variables, - -As an example, here's a function that adds two numbers together: - -``` -func add_numbers ( a, b ) { - - let c = a + b; - return c; - -} -``` - -Functions can not be assigned to variables. The following is **invalid** syntax: -``` -let add_numbers = function( a, b ) { - - let c = a + b; - return c; - -} -``` -So is this: -``` -func add_numbers ( a, b ) { - - let c = a + b; - return c; - -} -let add_numbers_copy = add_numbers; -``` -Functions can return a singular variable using the `return` keyword. Do note that any code after a return is unreachable and will be ignored by the implementation. As such, the following will result in unreachable code: -``` -func add_numbers ( a, b ) { - - let c = a + b; - return c; - print( "hi" ); - -} -``` +## Basic Syntax ### Variables -Variables are dynamically determined by the implementation. Rules for this are as follows: +Variables are case sensitive and dynamically determined by the implementation. Rules for this are as follows: - Strings are variables surrounded by single or double quotes. Because of this, the following are valid strings. ``` @@ -80,7 +35,14 @@ let b = 'world'; let c = "100"; ``` -- Numbers and Decimals are any variable with a digit 0-9 in them. +Strings may not have mismatching quotes. +As such, the following is **invalid** syntax: +``` +let a = "hi'; +let b = 'hello"; +``` + +- Numbers are any variable consisting of digits 0-9. The following are valid Numbers: ``` let a = 1; @@ -88,6 +50,7 @@ let b = 0; let c = 100; ``` +- Likewise, Decimals are 32 bit floating point numbers. The following are valid Decimals: ``` let a = 1.0; @@ -103,14 +66,13 @@ let b = -0.5; let c = -0; ``` - -Trailing or leading decimals on numbers are invalid and should be detected as such by the compiler. Because of this, the following are **invalid** +Trailing or leading decimals on numbers are invalid and should be detected as such by the compiler. Because of this, the following is **invalid**: ``` let a = .1; let b = 5.; ``` -Putting the minus sign in the back is invalid (unless used for subtraction). Because of this and the previous rule, the following are **invalid** +Trailing negative signs are also invalid (unless used for subtraction). Because of this and the previous rule, the following is **invalid**: ``` let a = 1-; let b = -.5; @@ -123,7 +85,7 @@ The following are valid booleans: let a = true; let b = false; ``` -Because booleans (along with all other variables) are case sensitive, the following are **invalid** +Because booleans (along with all other variables) are case sensitive, the following is **invalid**: ``` let a = True; let b = fAlSe; @@ -138,7 +100,8 @@ func a () { } ``` -You can also return none from a failed conditional, like so: + +You can also return none from a failed conditional, however it is reccomended that failed conditionals return false. Here is an example of a conditional that returns `none`: ``` if a > b { @@ -147,6 +110,53 @@ if a > b { } ``` +### Functions + +Functions are declared using either the `func` or `functi` keywords followed by the name of the function. + +As an example, here's a function that adds two numbers together: + +``` +func add_numbers ( a, b ) { + + let c = a + b; + return c; + +} +``` + +Functions can not be retroactively assigned to variables. As such, the following is **invalid** syntax: +``` +let add_numbers = function( a, b ) { + + let c = a + b; + return c; + +} +``` + +So is this: +``` +func add_numbers ( a, b ) { + + let c = a + b; + return c; + +} +let add_numbers_copy = add_numbers; +``` + +Functions can return a singular variable using the `return` keyword. Do note that any code after a return is unreachable and will be ignored by the implementation. As such, the following will result in unreachable code: +``` +func add_numbers ( a, b ) { + + let c = a + b; + return c; + print( "hi" ); + +} +``` + ### Logical operators The following are valid logical operators in sack: @@ -233,7 +243,7 @@ if a > 1 { Else if is valid because it's a combination of the else and the if operator, it is not sytatically unique. ### Print -Print is the only language defined i/o function. It takes in a single variable and returns the output to the terminal appending a newline +Print is a language defined output function. It takes in a single variable and returns the output to the terminal appending a newline The following are valid print statements: ``` @@ -242,10 +252,18 @@ print ( 42 ); print ( a ); # Only valid if the variable "a" is defined, else it returns an error and the program stops. ``` +### Input +Similar to print, Input is a language defined input function. It allows for a prompt, and will read text entered into the program. + +The following is a valid input statement: +``` +let a = input ( "Please enter your name: " ) +``` + ### Loops A loop will itterate between a range of numbers starting at the first number and ending at the last for example `range( 1, 5 )` is equal to `[1, 2, 3, 4, 5]` -Example loops: +The following is an example of valid loop syntax: ``` loop ( a in range( 1, 100 ) ) { @@ -253,47 +271,46 @@ loop ( a in range( 1, 100 ) ) { } ``` + ### Type casting Most data types can be converted into any other data type. ``` # Convert to int. -# Can be done with all data types. +# Will round a decimal to the nearest int int(x) # Convert to float. -# Can be done with all data types. +# Will convert a number to a decimal +# For example, float(3) is equal to 3.0 float(x) # Convert to string. # Can be done with all data types. string(x) -# Convert to bool. -# Can be done with all data types. -bool(x) ``` ### Importing functions By using the `import` keyword you can use functions from other sack programs. Example: - -in exampleModule.sk: ``` +# exampleModule.sk func helloworld() { print ( "Helloworld" ); } ``` -and in exampleProgram.sk: ``` +# exampleProgram.sk import ( "exampleModule" ); + +# prints "Helloworld" to the console helloworld(); ``` You can import modules that are in the current directory without specifying the path to the file, else you will need to specify the path. -By sack first tries to import modlues ending with ".sk", if that fails it will look for files ending with ".sack", if that also fails it will not to compile and just produce errors. - +By default sack first tries to import modlues ending with ".sk", if that fails it will look for files ending with ".sack", if that also fails it will cause an error on runtime. ### Quirks @@ -301,7 +318,7 @@ By sack first tries to import modlues ending with ".sk", if that fails it will l - A string added to a number or a number added to a string will cast the number to a string and then append the string. - A string multiplied by a integer will result in more of the string. - Any operation which contains a number and a decimal will result in a decimal. -- Semicolons are required to end a non commented line. Function declarations and conditionals do not need them as the closing `}` specifies the end. +- Semicolons are required to end a non commented line. Function declarations and conditionals do not need them as the closing `}` terminates the line. - Naming - Identifiers may be given any alphanumeric name that does not start with a number. Underscores are allowed anywhere in the variable name @@ -310,21 +327,26 @@ By sack first tries to import modlues ending with ".sk", if that fails it will l FizzBuzz: ``` func checker ( num ) { - if ( num % 15 ) { + let a = num % 15; + let b = num % 5; + let c = num % 3; + if a == 0 { print ( "FizzBuzz" ) } - else if ( num % 3 ) { + else if b == 0 { print ( "Fizz" ); } - else if ( num % 5 ) { + else if c == 0 { print ( "Buzz" ); } else { - print ( num ); + print( num ); } } loop ( num in range( 1, 100 ) ) { - checker ( num ); + + checker( num ); + } ``` From de36946f2889eed720e2246dc7debdaa4cb835d7 Mon Sep 17 00:00:00 2001 From: nobody5050 Date: Sat, 28 Jan 2023 17:25:33 -0600 Subject: [PATCH 02/33] `!` no longer negates numbers --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 8a18bfb..73b4bfe 100644 --- a/readme.md +++ b/readme.md @@ -201,7 +201,7 @@ The following are valid logical operators in sack: || # Xor ^^ -# Not (Converts numbers to a negative form) +# Not (inverts a boolean value) ! ``` By extension `<=` and `>=` are also valid since they are a combination of the geater than and less than operators with the equal operator. From 661d9ee8b7eb823a7fd02a93ec33c66cdf6d20b4 Mon Sep 17 00:00:00 2001 From: nobody5050 Date: Sat, 28 Jan 2023 18:42:42 -0600 Subject: [PATCH 03/33] while loops --- readme.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 73b4bfe..0b076a0 100644 --- a/readme.md +++ b/readme.md @@ -263,7 +263,7 @@ let a = input ( "Please enter your name: " ) ### Loops A loop will itterate between a range of numbers starting at the first number and ending at the last for example `range( 1, 5 )` is equal to `[1, 2, 3, 4, 5]` -The following is an example of valid loop syntax: +The following is an example of valid loop in range syntax: ``` loop ( a in range( 1, 100 ) ) { @@ -272,6 +272,15 @@ loop ( a in range( 1, 100 ) ) { } ``` +While loops are also possible: +``` +let a = 1 +loop ( while a < 100 ) { + print ( a ); + a = a + 1; +} +``` + ### Type casting Most data types can be converted into any other data type. ``` From 9d31618fbc84f65e89054904ab8d1791c6dcc1a1 Mon Sep 17 00:00:00 2001 From: nobody5050 Date: Sun, 29 Jan 2023 18:04:13 -0600 Subject: [PATCH 04/33] Initial draft of lists --- readme.md | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 100 insertions(+), 6 deletions(-) diff --git a/readme.md b/readme.md index 0b076a0..4974bc0 100644 --- a/readme.md +++ b/readme.md @@ -20,6 +20,7 @@ Sack is a dynamically typed scripting language for beginner programmers that foc - Bool (`true` or `false`) - None (`none`) - Identifier + - List (Itterable Scope) - Sack enforces a style guide to improve readability, it is left up to the implementation whether this is a default warn or error. For simplicity, the style guide will not be described in this spec, however the example code will follow it. ## Basic Syntax @@ -110,6 +111,59 @@ if a > b { } ``` +### Lists +Lists are self contained scope blocks which are itterable. See the scope section for rules on scope. + +A list is defined using square brackets like so: +``` +let x = [ 1, 2, 3 ]; +``` + +Lists can also contain named data, for instance: +``` +let a = 2; +let x = [ 1, a, 3]; +``` + +Finally you can assign names to data outright: +``` +let x = [ 1, a: 2, 3 ]; +``` + +Accessing data in lists can be accomplished in a few ways. You can itterate over a list: +``` +let x = [ 1, 2, 3 ]; + +# Lists start at index 0, so this will print numbers 1 to 3 +loop ( num in range ( 0, 2 ) ) { + print ( x [ num ] ); +} +``` + +Data can also be accessed outright using it's name or a variable's content: +``` +let a = 2; +let x = [ 1, a: 2, 3 ]; + +# returns 3 +print ( x[a] ); + +# returns 2 +print ( x['a'] ); +``` + +If you try to access data that is beyond the length of a list, it is a runtime error as opposed to returning `none` + +Note that this makes the following code **Invalid**: +``` +let a = 10; +let x = [ 1, a: 2, 3 ]; + +if ( x[a] == none ) { + # because the 10th index of the list does not exist this will error on runtime +} +``` + ### Functions Functions are declared using either the `func` or `functi` keywords followed by the name of the function. @@ -281,22 +335,38 @@ loop ( while a < 100 ) { } ``` -### Type casting +### Language Defined Functions +Sack defines a few functions for convenience. These are typically related to typecasting, and are reserved keywords. + + Most data types can be converted into any other data type. ``` # Convert to int. # Will round a decimal to the nearest int -int(x) +int ( x ); # Convert to float. # Will convert a number to a decimal # For example, float(3) is equal to 3.0 -float(x) +float ( x ); # Convert to string. # Can be done with all data types. -string(x) +string ( x ); + +``` +You can also quickly get the type of a variable using the `type()` function: +``` +x = 3 + +# prints `Number: 3` +print ( type ( x ) ); + +string ( x ); + +# prints `String: 3` +print ( type ( x ) ); ``` ### Importing functions @@ -321,6 +391,30 @@ helloworld(); You can import modules that are in the current directory without specifying the path to the file, else you will need to specify the path. By default sack first tries to import modlues ending with ".sk", if that fails it will look for files ending with ".sack", if that also fails it will cause an error on runtime. +### Scope +The following rules govern scope in sack. + +1. You can only access from the global scope or the current scope. +2. The current scope has access to the scope above it, which has access to the scope above it, etc... +3. Calling a function makes a new scope with nothing but the global scope above it. +4. Functions can only access arguments or global vars that are defined before them. + +Following these rules, the following should print four: +``` +let e = 3; +functi print_e() { print(e); } +e = 4; +print_e(); +``` + +Likewise, the following should print `[e: 3, x: 5]` +``` +let e = 3; +let list = [e, x = 5]; +e = 4; +print(list); +``` + ### Quirks - A string added to a string will append the string. @@ -340,7 +434,7 @@ func checker ( num ) { let b = num % 5; let c = num % 3; if a == 0 { - print ( "FizzBuzz" ) + print ( "FizzBuzz" ); } else if b == 0 { print ( "Fizz" ); @@ -355,7 +449,7 @@ func checker ( num ) { loop ( num in range( 1, 100 ) ) { - checker( num ); + checker ( num ); } ``` From 482ff1bd7ae2b422d9de0fca42eda68267233568 Mon Sep 17 00:00:00 2001 From: nobody5050 Date: Sun, 29 Jan 2023 18:08:35 -0600 Subject: [PATCH 05/33] change grammar of string concatenation --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 4974bc0..5847f52 100644 --- a/readme.md +++ b/readme.md @@ -417,7 +417,7 @@ print(list); ### Quirks -- A string added to a string will append the string. +- A string added to another string will concatenate the strings. - A string added to a number or a number added to a string will cast the number to a string and then append the string. - A string multiplied by a integer will result in more of the string. - Any operation which contains a number and a decimal will result in a decimal. From c7e946fafec68894fc01b8e46590f3f0a3302c8b Mon Sep 17 00:00:00 2001 From: nobody5050 Date: Sun, 29 Jan 2023 18:57:50 -0600 Subject: [PATCH 06/33] quick changes and clean up --- readme.md | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/readme.md b/readme.md index 5847f52..135f140 100644 --- a/readme.md +++ b/readme.md @@ -342,11 +342,11 @@ Sack defines a few functions for convenience. These are typically related to typ Most data types can be converted into any other data type. ``` # Convert to int. -# Will round a decimal to the nearest int +# Will round a Decimal to the nearest integer int ( x ); # Convert to float. -# Will convert a number to a decimal +# Will convert a Number to a Decimal # For example, float(3) is equal to 3.0 float ( x ); @@ -360,15 +360,23 @@ You can also quickly get the type of a variable using the `type()` function: ``` x = 3 -# prints `Number: 3` +# prints `Number` print ( type ( x ) ); string ( x ); -# prints `String: 3` +# prints `String` print ( type ( x ) ); ``` +You can also get the length of a list with `len()`: +``` +let x = [ 1, 2, 3 ]; + +# returns `2` +len( x ); +``` + ### Importing functions By using the `import` keyword you can use functions from other sack programs. @@ -402,7 +410,9 @@ The following rules govern scope in sack. Following these rules, the following should print four: ``` let e = 3; -functi print_e() { print(e); } +functi print_e() { + print ( e ); +} e = 4; print_e(); ``` @@ -410,9 +420,9 @@ print_e(); Likewise, the following should print `[e: 3, x: 5]` ``` let e = 3; -let list = [e, x = 5]; +let list = [ e, x = 5 ]; e = 4; -print(list); +print ( list ); ``` ### Quirks From df195356730dfec82a7d84121239001dd6819a4b Mon Sep 17 00:00:00 2001 From: nobody5050 Date: Sat, 15 Apr 2023 23:28:04 -0500 Subject: [PATCH 07/33] Basic file io --- readme.md | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 135f140..7292b5e 100644 --- a/readme.md +++ b/readme.md @@ -21,9 +21,10 @@ Sack is a dynamically typed scripting language for beginner programmers that foc - None (`none`) - Identifier - List (Itterable Scope) + - file (File object) - Sack enforces a style guide to improve readability, it is left up to the implementation whether this is a default warn or error. For simplicity, the style guide will not be described in this spec, however the example code will follow it. -## Basic Syntax +## Syntax ### Variables @@ -164,6 +165,47 @@ if ( x[a] == none ) { } ``` +### Files and Binary + +File io is a core feature of Sack, and as such it has it's own syntax. + +Files are opened using the `open` keyword, and closed using the `close` keyword. There are multiple modes for opening files, and they are as follows: +``` +let file1 = open ( "file1.txt", "r" ); +let file2 = open ( "file2.txt", "w" ); +let file3 = open ( "file3.txt", "a" ); +``` + +The first argument is the file path, and the second is the mode. The mode can be one of the following: +- `r` - Read +- `w` - Write +- `a` - Append + +Files can be read using the `read` keyword, and written to using the `write` keyword. + +Here is an example of a file being opened, read, and closed: +``` +let file = open ( "file.txt", "r" ); +let data = read ( file ); +close ( file ); +``` +In this example, the `data` variable will be a string containing the contents of the file. + +Here is an example of a file being opened, written to, and closed: +``` +let file = open ( "file.txt", "w" ); +write ( file, "hello world" ); +close ( file ); +``` + +Flushing a file is done using the `flush` keyword. This is useful for writing to files that are opened in append mode. +``` +let file = open ( "file.txt", "a" ); +write ( file, "hello world" ); +flush ( file ); +close ( file ); +``` + ### Functions Functions are declared using either the `func` or `functi` keywords followed by the name of the function. From 9ee412a53d09e1879d5f0a9ce1319b9db5943ab9 Mon Sep 17 00:00:00 2001 From: nobody5050 Date: Sat, 15 Apr 2023 23:42:31 -0500 Subject: [PATCH 08/33] reading and writing binary, byte type, and printing file example --- readme.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/readme.md b/readme.md index 7292b5e..c68e749 100644 --- a/readme.md +++ b/readme.md @@ -22,6 +22,7 @@ Sack is a dynamically typed scripting language for beginner programmers that foc - Identifier - List (Itterable Scope) - file (File object) + - byte (8 bit integer) - Sack enforces a style guide to improve readability, it is left up to the implementation whether this is a default warn or error. For simplicity, the style guide will not be described in this spec, however the example code will follow it. ## Syntax @@ -112,6 +113,13 @@ if a > b { } ``` +- Byte is an 8 bit integer, and is used for binary data. +The following are valid bytes: +``` +let a = 0b00000000; +let b = 0b11111111; +``` + ### Lists Lists are self contained scope blocks which are itterable. See the scope section for rules on scope. @@ -180,6 +188,8 @@ The first argument is the file path, and the second is the mode. The mode can be - `r` - Read - `w` - Write - `a` - Append +- `rb` - Read Binary +- `wb` - Write Binary Files can be read using the `read` keyword, and written to using the `write` keyword. @@ -206,6 +216,26 @@ flush ( file ); close ( file ); ``` +Files can also be read and written to using binary data. This is done using the `read` and `write` keywords with the file opened in binary mode. Reading and writing binary data is done using the `byte` type. In this case the file object will be a list of bytes. +``` +let file = open ( "file.txt", "r" ); +let data = read ( file ); + +# data is now a list of bytes +print ( data ); + +close ( file ); +``` + +File data can also be printed to the console. The following code will print the contents of a file to the console: +``` +# file.txt contains the text "hello world" +let file = open ( "file.txt", "r" ); +# prints "hello world" +print ( file ); +close ( file ); +``` + ### Functions Functions are declared using either the `func` or `functi` keywords followed by the name of the function. From eab15be8c0301bacd0efd0310256f4a353cbcb1f Mon Sep 17 00:00:00 2001 From: StealthHydra179 <47306082+StealthHydra179@users.noreply.github.com> Date: Sun, 16 Apr 2023 19:18:34 -0400 Subject: [PATCH 09/33] Fix some errors and make 1 thing more clear --- readme.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index e78b389..39f1007 100644 --- a/readme.md +++ b/readme.md @@ -48,7 +48,7 @@ let add_numbers = function( a, b ) { } ``` -So is this: +This is also **invalid** syntax: ``` func add_numbers ( a, b ) { @@ -99,10 +99,14 @@ let d = 0.0; The following are vaild negative Numbers: ``` let a = -1; -let b = -0.5; -let c = -0; +let b = -0; ``` +The following are valid negative Decimals: +``` +let a = -10.43; +let b = -0.5; +``` Trailing or leading decimals on numbers are invalid and should be detected as such by the compiler. Because of this, the following are **invalid** ``` From cb3818f1c70bcb7dd6e7acd55a9755e31cfc75b8 Mon Sep 17 00:00:00 2001 From: StealthHydra179 <47306082+StealthHydra179@users.noreply.github.com> Date: Sun, 16 Apr 2023 19:29:42 -0400 Subject: [PATCH 10/33] Style Guide --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 39f1007..986783e 100644 --- a/readme.md +++ b/readme.md @@ -20,7 +20,7 @@ Sack is a dynamically typed scripting language for beginner programmers that foc - Decimal (32 bit float) - Bool (`true` or `false`) - None (`none`) -- Sack enforces a style guide for improve readability, It is left up to the implementation whether this is a default warn or error. +- Sack enforces a style guide for improve readability. Compilers should by default warn for violations of the style guide. ## Basic keywords From 1b4e994862134fa92d296cb99df0076e810b5117 Mon Sep 17 00:00:00 2001 From: StealthHydra179 <47306082+StealthHydra179@users.noreply.github.com> Date: Sun, 16 Apr 2023 19:36:59 -0400 Subject: [PATCH 11/33] Update Typo --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 6aa9a06..8a6cdd5 100644 --- a/readme.md +++ b/readme.md @@ -62,7 +62,7 @@ let c = 100.25; let d = 0.0; ``` -The following are vaild negative Numbers: +The following are valid negative Numbers: ``` let a = -1; let b = -0; From c6945f80afc2369e1a614fb84ca2d4d6b0c66a72 Mon Sep 17 00:00:00 2001 From: StealthHydra179 <47306082+StealthHydra179@users.noreply.github.com> Date: Sun, 16 Apr 2023 19:45:28 -0400 Subject: [PATCH 12/33] Formatting and grammar issues --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 8a6cdd5..3c4e497 100644 --- a/readme.md +++ b/readme.md @@ -19,11 +19,11 @@ Sack is a dynamically typed scripting language for beginner programmers that foc - Decimal (32 bit float) - Bool (`true` or `false`) - None (`none`) - - Identifier + - Identifier - List (Itterable Scope) - file (File object) - byte (8 bit integer) -- Sack enforces a style guide for improve readability. Compilers should by default warn for violations of the style guide. +- Sack enforces a style guide to improve readability. Compilers should by default warn for violations of the style guide. ## Syntax From 48bb7c1c0f6f74b0f0bbef80f79ef552a5a54bb6 Mon Sep 17 00:00:00 2001 From: Bigjango13 <58828692+Bigjango13@users.noreply.github.com> Date: Tue, 18 Apr 2023 13:49:27 -0700 Subject: [PATCH 13/33] Add style guide and fix typos --- readme.md | 21 ++++----- style-guide.md | 122 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 11 deletions(-) create mode 100644 style-guide.md diff --git a/readme.md b/readme.md index 3c4e497..a304ab7 100644 --- a/readme.md +++ b/readme.md @@ -1,13 +1,13 @@ # The Sack programming language > Spec v1.0.0 SemVer -Sack is a dynamically typed scripting language for beginner programmers that focusses on readability and ease of use over speed. +Sack is a dynamically typed scripting language for beginner programmers that focuses on readability and ease of use over speed. ## Goals - Sack is Simple. A program is executed from top to bottom. This means asynchronous code is impossible. - Sack is small. The specification is minimal, and easy to learn as well as implement. -- Sack is Extensible. While the specification is not indended to be open ended, It should allow for new features where required. +- Sack is Extensible. While the specification is not intended to be open ended, It should allow for new features where required. - Sack is Easy. Language reserved words should have their basis in common english, and code written in it should prioritize readability. ## Basic notes @@ -15,15 +15,14 @@ Sack is a dynamically typed scripting language for beginner programmers that foc - Sack's default extension is `.sk` or `.sack`. All others are considered invalid by this spec version. - Variable types can be one of the following: - String - - Number (int) + - Number (signed 32 bit int) - Decimal (32 bit float) - Bool (`true` or `false`) - None (`none`) - - Identifier - - List (Itterable Scope) - - file (File object) - - byte (8 bit integer) -- Sack enforces a style guide to improve readability. Compilers should by default warn for violations of the style guide. + - List (Iterable Scope) + - File (File object) + - Byte (8 bit unsigned int) +- Sack enforces a style guide to improve readability. Compilers should by default warn for violations of the [style guide](style-guide.md). ## Syntax @@ -93,7 +92,7 @@ The following are valid booleans: let a = true; let b = false; ``` -Because booleans (along with all other variables) are case sensitive, the following is **invalid**: +Because booleans (along with all other identifiers) are case sensitive, the following is **invalid**: ``` let a = True; let b = fAlSe; @@ -109,7 +108,7 @@ func a () { } ``` -You can also return none from a failed conditional, however it is reccomended that failed conditionals return false. Here is an example of a conditional that returns `none`: +You can also return none from a failed conditional, however it is recommended that failed conditionals return false. Here is an example of a conditional that returns `none`: ``` if a > b { @@ -126,7 +125,7 @@ let b = 0b11111111; ``` ### Lists -Lists are self contained scope blocks which are itterable. See the scope section for rules on scope. +Lists are self contained iterable scope blocks. See the scope section for rules on scope. A list is defined using square brackets like so: ``` diff --git a/style-guide.md b/style-guide.md new file mode 100644 index 0000000..c5aad2f --- /dev/null +++ b/style-guide.md @@ -0,0 +1,122 @@ +# Sack Style Guide + +## Naming convention + +Functions and variables should use `snake_case`, unless they are supposed to be used as constants in which case they should use `SCREAMING_SNAKE_CASE`. + +Never use `l` (lower case L), `I` (uppercase i), or `O` (uppercase o), for variable names as they look too much like numbers. + +If you don't want people to use your function/variable unless they are sure they know exactly what they are doing, prefix it with a single underscore. +Variables and functions specific to a certain implementation should have two underscores, the implementations name, an underscore, and then name of the variable/function. + +## Comments + +Comments should be directly before the line they describe, one space should be put after the `#`. + +Comments should start with an uppercase letter but should not end with a period, however an exclamation or question mark is allowed. For example: + +``` +# Calculates foo +func calc_foo () { + # Returns 4 + return 4; +} + +# Stores foo +let foo = calc_foo(); +``` + +## Spacing + +Every binary operator, parenthesis, and brace should have 1 space around it, with the exception of function calls, list indexes, and semicolons. Unary operators should not have space. For example: + +``` +if a[0] == -b { + print( "Hello!" ); +} +``` + +### Indentation + +After every left brace should be an indent of 4 spaces, which should end before the closing right brace. For example: +``` +loop (while foo > 2) { + print( "Foo is greator than 2" ); + foo -= 1; +} +``` + +Trailing expressions follow the same rules but with with `[]` and `()`. For example: +``` +takes_long_args( + this_is_a_really_really_long_argument_name, + these_long_names_should_change +); +``` + +Trailing expressions may be on the same line as other trailing expressions. For example: +``` +let my_list = [ + 1, 2, 3, + 4, 5, 6 + 7, 8, 9, + 0 +]; +``` + +Trailing binary opetators should have the operator on the second line, unless the binop changes the a value. for example: +``` +let sum = + a_really_long_var_name + + some_other_long_var; +``` + +## Brace style + +All left braces should be inline with the rest of the line, all right braces should be on there own line (with the exception of `else` and `else if`), for example: + +``` +func foo ( bar ) { + if bar { + # Some code here... + } else { + # More code here... + } +} +``` + +## Code location + +Imports should always be at the top of files, and sorted alphabetically, with an extra newline after the final import. + +All globals variables should be after imports, with an extra newline after the final variable. + +Following after global variables should be functions. All functions should have a newline after the final `}`. + +All other code should be at the bottom of the file. + +## Returning + +When returning from all branches of an `if-(else-if)*-else`, it is better to remove the else. For example: +``` +# Wrong +func is_even ( a ) { + if ( type(a) != "Number" ) { + return false; + } else { + return a % 2; + } +} + +# Correct +func is_even ( a ) { + if ( type(a) != "Number" ) { + return false; + } + return a % 2; +} +``` + +## Unary vs binary increment/deincrement operators + +Unary increment and deincrement operators (`++` and `--`) should only be used when the value is not needed, they should not be used when `+=` or `-=` would work. From ac999dbfaac9524c9998b3e1e8bc16b5b7568228 Mon Sep 17 00:00:00 2001 From: Bigjango13 Date: Tue, 18 Apr 2023 20:35:57 -0700 Subject: [PATCH 14/33] Operator and greater were misspelled --- style-guide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/style-guide.md b/style-guide.md index c5aad2f..83d78a7 100644 --- a/style-guide.md +++ b/style-guide.md @@ -41,7 +41,7 @@ if a[0] == -b { After every left brace should be an indent of 4 spaces, which should end before the closing right brace. For example: ``` loop (while foo > 2) { - print( "Foo is greator than 2" ); + print( "Foo is greater than 2" ); foo -= 1; } ``` @@ -64,7 +64,7 @@ let my_list = [ ]; ``` -Trailing binary opetators should have the operator on the second line, unless the binop changes the a value. for example: +Trailing binary operators should have the operator on the second line, unless the binop changes the a value. for example: ``` let sum = a_really_long_var_name From dc4e11062a22bde5bd591497d1533dde3dd41136 Mon Sep 17 00:00:00 2001 From: Bigjango13 Date: Sun, 30 Apr 2023 23:37:17 -0700 Subject: [PATCH 15/33] Fix examples --- readme.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/readme.md b/readme.md index a304ab7..d0d58ad 100644 --- a/readme.md +++ b/readme.md @@ -257,7 +257,7 @@ func add_numbers ( a, b ) { Functions can not be retroactively assigned to variables. As such, the following is **invalid** syntax: ``` -let add_numbers = function( a, b ) { +let add_numbers = functi( a, b ) { let c = a + b; return c; @@ -387,7 +387,7 @@ Similar to print, Input is a language defined input function. It allows for a pr The following is a valid input statement: ``` -let a = input ( "Please enter your name: " ) +let a = input ( "Please enter your name: " ); ``` ### Loops @@ -404,10 +404,10 @@ loop ( a in range( 1, 100 ) ) { While loops are also possible: ``` -let a = 1 +let a = 1; loop ( while a < 100 ) { print ( a ); - a = a + 1; + a += 1; } ``` @@ -434,12 +434,12 @@ string ( x ); You can also quickly get the type of a variable using the `type()` function: ``` -x = 3 +let x = 3; # prints `Number` print ( type ( x ) ); -string ( x ); +x = string ( x ); # prints `String` print ( type ( x ) ); @@ -450,7 +450,7 @@ You can also get the length of a list with `len()`: let x = [ 1, 2, 3 ]; # returns `2` -len( x ); +print ( len( x ) ); ``` ### Importing functions @@ -496,7 +496,7 @@ print_e(); Likewise, the following should print `[e: 3, x: 5]` ``` let e = 3; -let list = [ e, x = 5 ]; +let list = [ e, x: 5 ]; e = 4; print ( list ); ``` From ba5628b8ee7a6fc17cc377e253d75914e1b6fd3e Mon Sep 17 00:00:00 2001 From: Bigjango13 Date: Wed, 17 May 2023 21:50:22 -0700 Subject: [PATCH 16/33] Add string indexing --- readme.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index d0d58ad..ee5fadc 100644 --- a/readme.md +++ b/readme.md @@ -44,6 +44,17 @@ let a = "hi'; let b = 'hello"; ``` +Strings can be zero indexed, which returns a single character long string. For example: +``` +let a = "Hello"; +# Prints H +print( a[0] ); +# Prints true +print( a[2] == "l" ); +# Throws an error (47 is more than 4) +print( a[47] ); +``` + - Numbers are any variable consisting of digits 0-9. The following are valid Numbers: ``` @@ -445,12 +456,17 @@ x = string ( x ); print ( type ( x ) ); ``` -You can also get the length of a list with `len()`: +You can also get the length of a list or string with `len()`: ``` let x = [ 1, 2, 3 ]; -# returns `2` +# prints `3` print ( len( x ) ); + +let s = "Test"; + +# prints `4` +print ( len( s ) ); ``` ### Importing functions From 66a51e27da8f346e422985954e6abc5ad0eac10d Mon Sep 17 00:00:00 2001 From: Bigjango13 Date: Thu, 18 May 2023 23:02:44 -0700 Subject: [PATCH 17/33] Revert changes to `len()` --- readme.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index ee5fadc..790a7a0 100644 --- a/readme.md +++ b/readme.md @@ -460,13 +460,16 @@ You can also get the length of a list or string with `len()`: ``` let x = [ 1, 2, 3 ]; -# prints `3` +# prints `2` print ( len( x ) ); let s = "Test"; -# prints `4` +# prints `3` print ( len( s ) ); + +# prints `none` +print ( len( [] ) ); ``` ### Importing functions From 4d77107ed04261b69c6fcdfb37b52d0af8117965 Mon Sep 17 00:00:00 2001 From: nobody5050 Date: Fri, 19 May 2023 23:39:22 -0500 Subject: [PATCH 18/33] fix some glaring issues with file io --- readme.md | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index 790a7a0..12a599c 100644 --- a/readme.md +++ b/readme.md @@ -194,9 +194,7 @@ File io is a core feature of Sack, and as such it has it's own syntax. Files are opened using the `open` keyword, and closed using the `close` keyword. There are multiple modes for opening files, and they are as follows: ``` -let file1 = open ( "file1.txt", "r" ); -let file2 = open ( "file2.txt", "w" ); -let file3 = open ( "file3.txt", "a" ); +let file1 = open ( "filename", "mode" ); ``` The first argument is the file path, and the second is the mode. The mode can be one of the following: @@ -222,6 +220,14 @@ let file = open ( "file.txt", "w" ); write ( file, "hello world" ); close ( file ); ``` +In this case, the file will be overwritten with the text `hello world`. To write to a specific location in a file, you can use the `seek` keyword. This will move the file pointer to a specific location in the file. The following code will write to the 5th byte in the file: +``` +let file = open ( "file.txt", "w" ); +seek ( file, 5 ); +write ( file, "hello world" ); +close ( file ); +``` +In this case the file will be overwritten with the text `hello world` starting at the 5th byte. Flushing a file is done using the `flush` keyword. This is useful for writing to files that are opened in append mode. ``` @@ -247,10 +253,20 @@ File data can also be printed to the console. The following code will print the # file.txt contains the text "hello world" let file = open ( "file.txt", "r" ); # prints "hello world" -print ( file ); +let data = read ( file ); +print ( data ); close ( file ); ``` +Files can also be iterated over using the `loop` keyword. The following code will print the contents of a file to the console: +``` +# file.txt contains the text "hello world" +let file = open ( "file.txt", "r" ); +loop ( byte in file ) { + print ( byte ); +} +``` + ### Functions Functions are declared using either the `func` or `functi` keywords followed by the name of the function. @@ -392,6 +408,13 @@ print ( "hello" ); print ( 42 ); print ( a ); # Only valid if the variable "a" is defined, else it returns an error and the program stops. ``` +Print can accept the following types: +- String +- Number +- Decimal +- Byte +- List +- Bool ### Input Similar to print, Input is a language defined input function. It allows for a prompt, and will read text entered into the program. From 359c9bb8cfcccdb331a892cbf728352764894935 Mon Sep 17 00:00:00 2001 From: nobody5050 Date: Sat, 20 May 2023 00:00:50 -0500 Subject: [PATCH 19/33] Conversions between string and byte, inconsistency fixes --- readme.md | 52 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/readme.md b/readme.md index 12a599c..6935453 100644 --- a/readme.md +++ b/readme.md @@ -220,14 +220,33 @@ let file = open ( "file.txt", "w" ); write ( file, "hello world" ); close ( file ); ``` -In this case, the file will be overwritten with the text `hello world`. To write to a specific location in a file, you can use the `seek` keyword. This will move the file pointer to a specific location in the file. The following code will write to the 5th byte in the file: + +Performing an invalid operation, such as writing to a read only file or vice versa, will result in an error. For example, the following code will error: +``` +let file = open ( "file.txt", "r" ); +write ( file, "hello world" ); +close ( file ); +``` + +To write to a specific location in a file, you can use the `seek` keyword. This will move the file pointer to a specific location in the file. The following code will write to the 5th byte in the file: ``` let file = open ( "file.txt", "w" ); seek ( file, 5 ); write ( file, "hello world" ); close ( file ); ``` -In this case the file will be overwritten with the text `hello world` starting at the 5th byte. +Overseeking will result in an error. For example, the following code will error: +``` +let file = open ( "file.txt", "w" ); + +# file.txt contains the string "hello world" +# The error will occur when attempting to seek, not when writing +seek ( file, 100 ); + +# This will not be reached +write ( file, "hello world" ); +close ( file ); +``` Flushing a file is done using the `flush` keyword. This is useful for writing to files that are opened in append mode. ``` @@ -239,7 +258,7 @@ close ( file ); Files can also be read and written to using binary data. This is done using the `read` and `write` keywords with the file opened in binary mode. Reading and writing binary data is done using the `byte` type. In this case the file object will be a list of bytes. ``` -let file = open ( "file.txt", "r" ); +let file = open ( "file.txt", "rb" ); let data = read ( file ); # data is now a list of bytes @@ -258,14 +277,24 @@ print ( data ); close ( file ); ``` -Files can also be iterated over using the `loop` keyword. The following code will print the contents of a file to the console: +Files can be iterated over using the `loop` keyword. The following code will print the contents of a file to the console: ``` # file.txt contains the text "hello world" -let file = open ( "file.txt", "r" ); -loop ( byte in file ) { +let file = open ( "file.txt", "rb" ); +let data = read ( file ); +loop ( byte in data ) { print ( byte ); } ``` +alternatively: +``` +# file.txt contains the text "hello world" +let file = open ( "file.txt", "r" ); +let data = read ( file ); +loop ( line in data ) { + print ( line ); +} +``` ### Functions @@ -464,9 +493,16 @@ float ( x ); # Can be done with all data types. string ( x ); +# Convert a byte to a string. +string ( x ); + +# Convert a string to a list of bytes. +# if the string is a single character, it will return a single byte. +byte ( x ); ``` +When converting between types, invalid conversions will return `none`. -You can also quickly get the type of a variable using the `type()` function: +You can quickly get the type of a variable using the `type()` function: ``` let x = 3; @@ -479,7 +515,7 @@ x = string ( x ); print ( type ( x ) ); ``` -You can also get the length of a list or string with `len()`: +You can get the length of a list or string with `len()`: ``` let x = [ 1, 2, 3 ]; From 29237f2ce0ef86b8e3758419d7fb72d9ff70081a Mon Sep 17 00:00:00 2001 From: nobody5050 Date: Sat, 20 May 2023 10:32:09 -0500 Subject: [PATCH 20/33] argv additions, unify on functi for example functions --- readme.md | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/readme.md b/readme.md index 6935453..78234cf 100644 --- a/readme.md +++ b/readme.md @@ -112,7 +112,7 @@ let b = fAlSe; - None is the default return value for functions, it represents no value. The following is a function that returns none: ``` -func a () { +functi a () { print( "hi" ); @@ -303,7 +303,7 @@ Functions are declared using either the `func` or `functi` keywords followed by As an example, here's a function that adds two numbers together: ``` -func add_numbers ( a, b ) { +functi add_numbers ( a, b ) { let c = a + b; return c; @@ -323,7 +323,7 @@ let add_numbers = functi( a, b ) { So is this: ``` -func add_numbers ( a, b ) { +functi add_numbers ( a, b ) { let c = a + b; return c; @@ -334,7 +334,7 @@ let add_numbers_copy = add_numbers; Functions can return a singular variable using the `return` keyword. Do note that any code after a return is unreachable and will be ignored by the implementation. As such, the following will result in unreachable code: ``` -func add_numbers ( a, b ) { +functi add_numbers ( a, b ) { let c = a + b; return c; @@ -531,13 +531,26 @@ print ( len( s ) ); print ( len( [] ) ); ``` +You can get the arguments passed to a function with `args()`: +``` +functi test ( a, b, c ) { + print ( args() ); +} +``` +Args will also return a list of arguments passed to the program if called outside of a function. with the first argument being the name/path of the program. +``` +# prints `["example.sk"]` +print ( args() ); +``` +For this reason, `len( args() )` will always be greater than or equal to 1. + ### Importing functions By using the `import` keyword you can use functions from other sack programs. Example: ``` # exampleModule.sk -func helloworld() { +functi helloworld() { print ( "Helloworld" ); } ``` @@ -593,7 +606,7 @@ print ( list ); FizzBuzz: ``` -func checker ( num ) { +functi checker ( num ) { let a = num % 15; let b = num % 5; let c = num % 3; From de85d735f3669a286c1c95520f41c1fddd7d9805 Mon Sep 17 00:00:00 2001 From: nobody5050 Date: Sun, 21 May 2023 19:32:51 -0500 Subject: [PATCH 21/33] definitely didn't forget that functis can have 0 arguments --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 78234cf..3235011 100644 --- a/readme.md +++ b/readme.md @@ -542,7 +542,7 @@ Args will also return a list of arguments passed to the program if called outsid # prints `["example.sk"]` print ( args() ); ``` -For this reason, `len( args() )` will always be greater than or equal to 1. +If the function that is called has no arguments, `args()` will return `none`, however if args is called in the global scope it's length will always be greater than 0. ### Importing functions By using the `import` keyword you can use functions from other sack programs. From 5440c057b90a7af9006e84ea35ac46414047a677 Mon Sep 17 00:00:00 2001 From: nobody5050 Date: Sun, 21 May 2023 20:47:59 -0500 Subject: [PATCH 22/33] list appending, concatenation --- readme.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/readme.md b/readme.md index 3235011..5f5f915 100644 --- a/readme.md +++ b/readme.md @@ -188,6 +188,26 @@ if ( x[a] == none ) { } ``` +Lists can be appended to: +``` +let x = [ 1, 2, 3 ]; +x += 4; + +let y = [] +y += 1; + +# x is now [ 1, 2, 3, 4 ] +# y is now [ 1 ] +``` + +Lists can also be concatenated: +``` +let x = [ 1, 2, 3 ]; +x += [ 4, 5, 6 ]; + +# x is now [ 1, 2, 3, 4, 5, 6 ] +``` + ### Files and Binary File io is a core feature of Sack, and as such it has it's own syntax. From 32d8befd24f3f352f195672e997cc8cd1989ec4b Mon Sep 17 00:00:00 2001 From: nobody5050 Date: Sun, 21 May 2023 21:33:07 -0500 Subject: [PATCH 23/33] appending lists into lists explicitly defined --- readme.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/readme.md b/readme.md index 5f5f915..4438ba6 100644 --- a/readme.md +++ b/readme.md @@ -208,6 +208,14 @@ x += [ 4, 5, 6 ]; # x is now [ 1, 2, 3, 4, 5, 6 ] ``` +To append a list into a list, surround the list with brackets: +``` +let x = [ 1, 2, 3 ]; +x += [[ 4, 5, 6 ]]; + +# x is now [ 1, 2, 3, [ 4, 5, 6 ] ] +``` + ### Files and Binary File io is a core feature of Sack, and as such it has it's own syntax. From 96a257a1ffc2535d3913ef6a0571060e04923552 Mon Sep 17 00:00:00 2001 From: nobody5050 Date: Mon, 22 May 2023 16:36:52 -0500 Subject: [PATCH 24/33] the in keyword --- readme.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/readme.md b/readme.md index 4438ba6..b392432 100644 --- a/readme.md +++ b/readme.md @@ -481,6 +481,40 @@ The following is a valid input statement: let a = input ( "Please enter your name: " ); ``` +### The `in` keyword +The `in` keyword is used to check if a value is in a list or string. It returns a boolean value. + +The following are valid `in` statements: +``` +let a = "hello"; +let b = "h"; +# prints true +print ( b in a ); + +let c = "z"; +# prints false +print ( c in a ); + +let d = [ 1, 2, 3, 4, 5 ]; +let e = 1; +# prints true +print ( e in d ); + +let f = 6; +# prints false +print ( f in d ); +``` + +It follows that conditional statements can also use the `in` keyword: +``` +let a = "hello"; +let b = "h"; +if ( b in a ) { + print ( "hello contains h" ); +} +``` + + ### Loops A loop will itterate between a range of numbers starting at the first number and ending at the last for example `range( 1, 5 )` is equal to `[1, 2, 3, 4, 5]` From ca88e48db48d467b9a7cb71779bdcb1cb8d0b810 Mon Sep 17 00:00:00 2001 From: nobody5050 Date: Mon, 22 May 2023 16:39:06 -0500 Subject: [PATCH 25/33] Invalid type comparisons for the in keyword --- readme.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/readme.md b/readme.md index b392432..3d16bd4 100644 --- a/readme.md +++ b/readme.md @@ -514,6 +514,14 @@ if ( b in a ) { } ``` +Invalid types will result in an error: +``` +let a = "fourty-seven"; +let b = 47; + +# This will result in an error +print ( b in a ); +``` ### Loops A loop will itterate between a range of numbers starting at the first number and ending at the last for example `range( 1, 5 )` is equal to `[1, 2, 3, 4, 5]` From 18f450cdcb106b88f752e6f01605dab5c687c082 Mon Sep 17 00:00:00 2001 From: Bigjango13 Date: Wed, 12 Jul 2023 20:32:21 -0700 Subject: [PATCH 26/33] Update functions; First-class and overloading --- readme.md | 71 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 10 deletions(-) diff --git a/readme.md b/readme.md index 3d16bd4..4049a9c 100644 --- a/readme.md +++ b/readme.md @@ -22,6 +22,7 @@ Sack is a dynamically typed scripting language for beginner programmers that foc - List (Iterable Scope) - File (File object) - Byte (8 bit unsigned int) + - Function - Sack enforces a style guide to improve readability. Compilers should by default warn for violations of the [style guide](style-guide.md). ## Syntax @@ -136,7 +137,7 @@ let b = 0b11111111; ``` ### Lists -Lists are self contained iterable scope blocks. See the scope section for rules on scope. +Lists are self contained iterable scope blocks. A list is defined using square brackets like so: ``` @@ -333,33 +334,75 @@ As an example, here's a function that adds two numbers together: ``` functi add_numbers ( a, b ) { - let c = a + b; - return c; + let ret = a + b; + return ret; } ``` -Functions can not be retroactively assigned to variables. As such, the following is **invalid** syntax: +Functions aren't allowed to be anonymous. As such, the following is **invalid** syntax: ``` -let add_numbers = functi( a, b ) { +functi( a, b ) { - let c = a + b; - return c; + let ret = a + b; + return ret; } ``` -So is this: +Functions can be assigned to variables however: ``` functi add_numbers ( a, b ) { - let c = a + b; - return c; + let ret = a + b; + return ret; } let add_numbers_copy = add_numbers; ``` +Functions can also be overloaded, this is done based on the number of arguments: +``` +functi add_numbers ( a, b ) { + + let ret = a + b; + return ret; + +} + +functi add_numbers ( a, b, c ) { + + let ret = a + b + c; + return ret; + +} +``` + +The overloaded function to use is chosen when it is called, this may seem obvious but consider the following (which is valid): +``` +functi add_numbers ( a, b ) { + + let ret = a + b; + return ret; + +} + +functi add_numbers ( a, b, c ) { + + let ret = a + b + c; + return ret; + +} + +// Is this add_numbers with 2 or 3 args? It's both! +let adder = add_numbers; + +// Calls the 2 argument version +adder ( 47, 17 ); +// Calls the 3 argument version +adder ( 21, 5, 1940 ); +``` + Functions can return a singular variable using the `return` keyword. Do note that any code after a return is unreachable and will be ignored by the implementation. As such, the following will result in unreachable code: ``` functi add_numbers ( a, b ) { @@ -583,6 +626,9 @@ x = string ( x ); # prints `String` print ( type ( x ) ); + +# prints `Functi` +print ( type ( print ) ); ``` You can get the length of a list or string with `len()`: @@ -644,6 +690,11 @@ The following rules govern scope in sack. 3. Calling a function makes a new scope with nothing but the global scope above it. 4. Functions can only access arguments or global vars that are defined before them. +There are also some rules for defining functions and variables: +1. Functions and variables can not be defined with the name of a builtin function. +2. Functions can not be defined if an existing variable has that name, or if a function with the same number of args exists. +3. Variables can not be defined if a function with the same name exists. + Following these rules, the following should print four: ``` let e = 3; From 5493ad21f27ed9157339dc6e14cf92f44ecaa63d Mon Sep 17 00:00:00 2001 From: nobody5050 <32989720+nobody5050@users.noreply.github.com> Date: Mon, 17 Jul 2023 18:16:52 -0500 Subject: [PATCH 27/33] Update readme.md --- readme.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/readme.md b/readme.md index 4049a9c..79d52ac 100644 --- a/readme.md +++ b/readme.md @@ -587,6 +587,26 @@ loop ( while a < 100 ) { } ``` +The keywords `break` and `continue` will exit the loop, or continue to the next itteration respectively. + +``` +# Looping from 1 to 10, but breaking out at 5 +let a = 1; +loop ( while a <= 10 ) { + if a == 5 { + break; + } +} + +# Looping from 1 to 10 but only the odd numbers +a = 1; +loop ( while a <= 10 ) { + if a % 2 { + continue; + } +} +``` + ### Language Defined Functions Sack defines a few functions for convenience. These are typically related to typecasting, and are reserved keywords. From 9e8ddf557f02f942c0d45a3aca9a1f14525f460d Mon Sep 17 00:00:00 2001 From: nobody5050 <32989720+nobody5050@users.noreply.github.com> Date: Mon, 17 Jul 2023 19:17:20 -0500 Subject: [PATCH 28/33] List additions --- readme.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/readme.md b/readme.md index 79d52ac..d426185 100644 --- a/readme.md +++ b/readme.md @@ -147,6 +147,7 @@ let x = [ 1, 2, 3 ]; Lists can also contain named data, for instance: ``` let a = 2; +# [ 1, a: 2, 3 ] let x = [ 1, a, 3]; ``` @@ -155,6 +156,17 @@ Finally you can assign names to data outright: let x = [ 1, a: 2, 3 ]; ``` +You can put a variable's content in a list (without giving it a name in the list) like so: +``` +let a = 1; +# [ 0, 1, 2] +let x = [ 0, :a, 2] + +# Consequently, this is also possible +# [ 3 ] +let y = [ :3 ] +``` + Accessing data in lists can be accomplished in a few ways. You can itterate over a list: ``` let x = [ 1, 2, 3 ]; From 83d981a7205b3586b0f7b83a4e7564210d4038a8 Mon Sep 17 00:00:00 2001 From: Bigjango13 Date: Tue, 18 Jul 2023 14:14:17 -0700 Subject: [PATCH 29/33] Fix typos --- readme.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/readme.md b/readme.md index d426185..2a3e26d 100644 --- a/readme.md +++ b/readme.md @@ -167,7 +167,7 @@ let x = [ 0, :a, 2] let y = [ :3 ] ``` -Accessing data in lists can be accomplished in a few ways. You can itterate over a list: +Accessing data in lists can be accomplished in a few ways. You can iterate over a list: ``` let x = [ 1, 2, 3 ]; @@ -231,7 +231,7 @@ x += [[ 4, 5, 6 ]]; ### Files and Binary -File io is a core feature of Sack, and as such it has it's own syntax. +File IO is a core feature of Sack, and as such it has it's own syntax. Files are opened using the `open` keyword, and closed using the `close` keyword. There are multiple modes for opening files, and they are as follows: ``` @@ -473,7 +473,7 @@ The following are valid logical operators in sack: # Not (inverts a boolean value) ! ``` -By extension `<=` and `>=` are also valid since they are a combination of the geater than and less than operators with the equal operator. +By extension `<=` and `>=` are also valid since they are a combination of the greater than and less than operators with the equal operator. Operators which check for a condition return a boolean value `true` or `false`. ### Comments @@ -571,7 +571,7 @@ if ( b in a ) { Invalid types will result in an error: ``` -let a = "fourty-seven"; +let a = "forty-seven"; let b = 47; # This will result in an error @@ -579,7 +579,7 @@ print ( b in a ); ``` ### Loops -A loop will itterate between a range of numbers starting at the first number and ending at the last for example `range( 1, 5 )` is equal to `[1, 2, 3, 4, 5]` +A loop will iterate between a range of numbers starting at the first number and ending at the last for example `range( 1, 5 )` is equal to `[1, 2, 3, 4, 5]` The following is an example of valid loop in range syntax: ``` @@ -599,7 +599,7 @@ loop ( while a < 100 ) { } ``` -The keywords `break` and `continue` will exit the loop, or continue to the next itteration respectively. +The keywords `break` and `continue` will exit the loop, or continue to the next iteration respectively. ``` # Looping from 1 to 10, but breaking out at 5 @@ -712,7 +712,7 @@ helloworld(); ``` You can import modules that are in the current directory without specifying the path to the file, else you will need to specify the path. -By default sack first tries to import modlues ending with ".sk", if that fails it will look for files ending with ".sack", if that also fails it will cause an error on runtime. +By default sack first tries to import modules ending with ".sk", if that fails it will look for files ending with ".sack", if that also fails it will cause an error on runtime. ### Scope The following rules govern scope in sack. From f29522b33286e6d5896774e38924ee7b927673f3 Mon Sep 17 00:00:00 2001 From: nobody5050 <32989720+nobody5050@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:44:09 -0500 Subject: [PATCH 30/33] Changes to the way import works "1.0.0 is ready to merge, I won't make any last minute decisions" --- readme.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 2a3e26d..2fe0ee7 100644 --- a/readme.md +++ b/readme.md @@ -705,7 +705,7 @@ functi helloworld() { ``` # exampleProgram.sk -import ( "exampleModule" ); +import "exampleModule"; # prints "Helloworld" to the console helloworld(); @@ -714,6 +714,28 @@ helloworld(); You can import modules that are in the current directory without specifying the path to the file, else you will need to specify the path. By default sack first tries to import modules ending with ".sk", if that fails it will look for files ending with ".sack", if that also fails it will cause an error on runtime. +Because there are multiple valid extensions for a Sack file, you can also specify the extension directly. Be careful though, as a mismatch will error: +``` +import "exampleModule.sk"; + +# The following will error, as the file extension is incorrect +import "exampleModule.sack"; +``` + +You may also import a package, by simply importing a folder: +``` +# examplePackage/main.sk +functi helloworld() { + print ( "Helloworld" ); +} + +# exampleProgram.sk +import "examplePackage"; + +# prints "Helloworld" to the console +helloworld(); +``` + ### Scope The following rules govern scope in sack. From 828d37d55230d33363576f8a71a1d24cdd027755 Mon Sep 17 00:00:00 2001 From: Bigjango13 <58828692+Bigjango13@users.noreply.github.com> Date: Sat, 14 Oct 2023 01:22:10 -0400 Subject: [PATCH 31/33] Jango/minor improvements (#16) * Fix typos * Chained let declarations * List multiplication * Infinite loop * rand function * Know to known * None for invalid indexes --- readme.md | 73 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/readme.md b/readme.md index 2fe0ee7..ad54a0e 100644 --- a/readme.md +++ b/readme.md @@ -38,6 +38,11 @@ let b = 'world'; let c = "100"; ``` +Chaining declarations is also valid: +``` +let a = "hello", b = 'world', c = "100"; +``` + Strings may not have mismatching quotes. As such, the following is **invalid** syntax: ``` @@ -162,7 +167,7 @@ let a = 1; # [ 0, 1, 2] let x = [ 0, :a, 2] -# Consequently, this is also possible +# This is also possible, although not very useful # [ 3 ] let y = [ :3 ] ``` @@ -189,18 +194,37 @@ print ( x[a] ); print ( x['a'] ); ``` -If you try to access data that is beyond the length of a list, it is a runtime error as opposed to returning `none` +If you try to access a key that isn't in the list, it returns `none` -Note that this makes the following code **Invalid**: +For example: ``` -let a = 10; -let x = [ 1, a: 2, 3 ]; +let x = [ 1, 2, 3 ]; -if ( x[a] == none ) { - # because the 10th index of the list does not exist this will error on runtime +if ( x[10] == none ) { + // 10 is way outside the list! So this executes + print("Lists much be shorter than 10!"); } ``` +By extention, setting a key to `none` deletes it: +``` +let x = [ 1, 2, 3 ]; + +# 2 +print ( x[1] ); + +x[1] = none: + +# 3 +print ( x[1] ); +``` + +However, this does mean that having `none` as keys is ignored: +``` +# Prints 4, but because print returns none, x is set to [4] +let x = [ 4, print(4) ]; +``` + Lists can be appended to: ``` let x = [ 1, 2, 3 ]; @@ -229,6 +253,14 @@ x += [[ 4, 5, 6 ]]; # x is now [ 1, 2, 3, [ 4, 5, 6 ] ] ``` +Lists can be multiplied: +``` +let x = [ 1 ] * 3; +x += [ 2, 3 ] * 2; + +# x is now [ 1, 1, 1, 2, 3, 2, 3 ] +``` + ### Files and Binary File IO is a core feature of Sack, and as such it has it's own syntax. @@ -406,12 +438,12 @@ functi add_numbers ( a, b, c ) { } -// Is this add_numbers with 2 or 3 args? It's both! +# Is this add_numbers with 2 or 3 args? It's both! let adder = add_numbers; -// Calls the 2 argument version +# Calls the 2 argument version adder ( 47, 17 ); -// Calls the 3 argument version +# Calls the 3 argument version adder ( 21, 5, 1940 ); ``` @@ -509,7 +541,7 @@ if a > 1 { } ``` -Else if is valid because it's a combination of the else and the if operator, it is not sytatically unique. +Else if is valid because it's a combination of the else and the if operator, it is not syntactically unique. ### Print Print is a language defined output function. It takes in a single variable and returns the output to the terminal appending a newline @@ -599,6 +631,13 @@ loop ( while a < 100 ) { } ``` +There is a quicker way to write a infinite loop: +``` +loop { + print("This will print forever!"); +} +``` + The keywords `break` and `continue` will exit the loop, or continue to the next iteration respectively. ``` @@ -654,6 +693,11 @@ let x = 3; # prints `Number` print ( type ( x ) ); +x = 5.25; + +# prints `Decimal` +print ( type ( x ) ); + x = string ( x ); # prints `String` @@ -691,6 +735,13 @@ Args will also return a list of arguments passed to the program if called outsid print ( args() ); ``` If the function that is called has no arguments, `args()` will return `none`, however if args is called in the global scope it's length will always be greater than 0. +The `rand()` function can be used to get a random number between 0 and 1 (0 included), for example: +``` +let e = rand(); +# Nobody knows exactly what e is a now +# But it's known that type(e) == "Decimal" +# And that 0 <= e && e < 1 +`` ### Importing functions By using the `import` keyword you can use functions from other sack programs. From 2d5fcebc2529f96f87ac9c8e4a7318d8dc736f03 Mon Sep 17 00:00:00 2001 From: Bigjango13 <58828692+Bigjango13@users.noreply.github.com> Date: Wed, 18 Oct 2023 22:21:25 -0400 Subject: [PATCH 32/33] Better define list operations (#17) * Fix typos * Chained let declarations * List multiplication * Infinite loop * rand function * Know to known * None for invalid indexes * Clear up list with named keys being multiplied * Make rand take min and max params * Fix comments * Formatting fix * Adding new (named or numeric) keys Apparently this doesn't already exist --------- Co-authored-by: nobody5050 <32989720+nobody5050@users.noreply.github.com> --- readme.md | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index ad54a0e..0467242 100644 --- a/readme.md +++ b/readme.md @@ -237,6 +237,29 @@ y += 1; # y is now [ 1 ] ``` +Lists can have new keys added: +``` +let x = []; +x["a"] = 1; +# 1 +print ( x["a"] ); + +# This doesn't just work for named keys, the first numeric index beyond the +# length of the list can be set, and has the same effect as appending does +x[1] = 47; +# [ a: 1, 47 ] +print ( x ): + +# Trying to do this with none will have no effect +x[2] = none; +x["foo"] = none; +# [ a: 1, 47 ] +print ( x ): + +# Going more than one index beyond will raise an error +x[3] = 5.25; +``` + Lists can also be concatenated: ``` let x = [ 1, 2, 3 ]; @@ -261,6 +284,17 @@ x += [ 2, 3 ] * 2; # x is now [ 1, 1, 1, 2, 3, 2, 3 ] ``` +Lists cannot be multiplied if they *have* named keys, but may still be multiplied if they *had* named keys: +``` +let x = [1, 2, a: 6 ]; +# Invalid, x has named keys +x * 2; +# Remove the named key +x['a'] = none; +# Valid, x no longer has named keys +x * 2; +``` + ### Files and Binary File IO is a core feature of Sack, and as such it has it's own syntax. @@ -735,13 +769,13 @@ Args will also return a list of arguments passed to the program if called outsid print ( args() ); ``` If the function that is called has no arguments, `args()` will return `none`, however if args is called in the global scope it's length will always be greater than 0. -The `rand()` function can be used to get a random number between 0 and 1 (0 included), for example: +The `rand(min, max)` function can be used to get a random number between `min` and `max` (inclusive), for example: ``` -let e = rand(); +let e = rand(5, 47); # Nobody knows exactly what e is a now -# But it's known that type(e) == "Decimal" -# And that 0 <= e && e < 1 -`` +# But it's known that type(e) == "Number" +# And that 5 <= e && e <= 47 +``` ### Importing functions By using the `import` keyword you can use functions from other sack programs. From 1706e8411617245a81a97ad3dfbadf3ad0afce11 Mon Sep 17 00:00:00 2001 From: Nobody5050 Date: Thu, 8 May 2025 00:56:37 -0500 Subject: [PATCH 33/33] General language and clarity improvements --- readme.md | 313 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 205 insertions(+), 108 deletions(-) diff --git a/readme.md b/readme.md index 0467242..feb34df 100644 --- a/readme.md +++ b/readme.md @@ -1,8 +1,30 @@ # The Sack programming language + > Spec v1.0.0 SemVer Sack is a dynamically typed scripting language for beginner programmers that focuses on readability and ease of use over speed. +## Table of Contents + +- [Goals](#goals) +- [Basic Notes](#basic-notes) +- [Syntax](#syntax) + - [Variables](#variables) + - [Lists](#lists) + - [Files and Binary](#files-and-binary) + - [Functions](#functions) + - [Logical Operators](#logical-operators) + - [Comments](#comments) + - [Conditionals (if/else)](#if-else-else-if) + - [Print and Input](#print) + - [The in Keyword](#the-in-keyword) + - [Loops and Control Flow](#loops) + - [Built-in Functions](#language-defined-functions) + - [Importing](#importing-functions) + - [Scope](#scope) +- [Language Quirks](#quirks) +- [Sample Programs](#sample-programs) + ## Goals - Sack is Simple. A program is executed from top to bottom. This means asynchronous code is impossible. @@ -14,24 +36,25 @@ Sack is a dynamically typed scripting language for beginner programmers that foc - Sack's default extension is `.sk` or `.sack`. All others are considered invalid by this spec version. - Variable types can be one of the following: - - String - - Number (signed 32 bit int) - - Decimal (32 bit float) - - Bool (`true` or `false`) - - None (`none`) - - List (Iterable Scope) - - File (File object) - - Byte (8 bit unsigned int) - - Function +- String +- Number (signed 32 bit int) +- Decimal (32 bit float) +- Bool (`true` or `false`) +- None (`none`) +- List (Iterable Scope) +- File (File object) +- Byte (8 bit unsigned int) +- Function - Sack enforces a style guide to improve readability. Compilers should by default warn for violations of the [style guide](style-guide.md). ## Syntax ### Variables -Variables are case sensitive and dynamically determined by the implementation. Rules for this are as follows: +Variables are case sensitive and dynamically typed. Rules for this are as follows: + +**Strings** are variables surrounded by single or double quotes. Because of this, the following are valid strings. -- Strings are variables surrounded by single or double quotes. Because of this, the following are valid strings. ``` let a = "hello"; let b = 'world'; @@ -39,18 +62,21 @@ let c = "100"; ``` Chaining declarations is also valid: + ``` let a = "hello", b = 'world', c = "100"; ``` -Strings may not have mismatching quotes. +**Strings** may not have mismatching quotes. As such, the following is **invalid** syntax: + ``` let a = "hi'; let b = 'hello"; ``` -Strings can be zero indexed, which returns a single character long string. For example: +**Strings** can be zero indexed, which returns a single character long string. For example: + ``` let a = "Hello"; # Prints H @@ -61,16 +87,18 @@ print( a[2] == "l" ); print( a[47] ); ``` -- Numbers are any variable consisting of digits 0-9. +**Numbers** are any variable consisting of digits 0-9. The following are valid Numbers: + ``` let a = 1; let b = 0; let c = 100; ``` -- Likewise, Decimals are 32 bit floating point numbers. +Likewise, **Decimals** are 32 bit floating point numbers. The following are valid Decimals: + ``` let a = 1.0; let b = 0.5; @@ -78,111 +106,115 @@ let c = 100.25; let d = 0.0; ``` -The following are valid negative Numbers: +The following are valid **negative Numbers**: + ``` let a = -1; let b = -0; ``` -The following are valid negative Decimals: +The following are valid **negative Decimals**: + ``` let a = -10.43; let b = -0.5; ``` -Trailing or leading decimals on numbers are invalid and should be detected as such by the compiler. Because of this, the following is **invalid**: +Decimal point notation requires digits on both sides of the point. Leading or trailing decimal points without digits are invalid syntax for **Decimal** type values. Because of this, the following is **invalid**: + ``` let a = .1; let b = 5.; ``` Trailing negative signs are also invalid (unless used for subtraction). Because of this and the previous rule, the following is **invalid**: + ``` let a = 1-; let b = -.5; let c = 1.-; ``` -- Boolean values are returnable from functions and conditionals, they represent `true` or `false` values +**Boolean** values are returnable from functions and conditionals, they represent `true` or `false` values The following are valid booleans: + ``` let a = true; let b = false; ``` + Because booleans (along with all other identifiers) are case sensitive, the following is **invalid**: + ``` let a = True; let b = fAlSe; ``` -- None is the default return value for functions, it represents no value. -The following is a function that returns none: +**None** is the default return value for functions, it represents no value. +The following is a function that returns None: + ``` functi a () { - - print( "hi" ); - + + print( "hi" ); + } ``` You can also return none from a failed conditional, however it is recommended that failed conditionals return false. Here is an example of a conditional that returns `none`: + ``` if a > b { - return none; + return none; } ``` -- Byte is an 8 bit integer, and is used for binary data. +**Byte** is an 8 bit integer, and is used for binary data. The following are valid bytes: + ``` let a = 0b00000000; let b = 0b11111111; ``` ### Lists -Lists are self contained iterable scope blocks. + +**Lists** are self-contained iterable scope blocks that can hold both indexed and named elements. A list is defined using square brackets like so: + ``` let x = [ 1, 2, 3 ]; ``` -Lists can also contain named data, for instance: -``` -let a = 2; -# [ 1, a: 2, 3 ] -let x = [ 1, a, 3]; -``` +Lists can contain named elements, which act like variables within the list's own scope: -Finally you can assign names to data outright: -``` -let x = [ 1, a: 2, 3 ]; ``` +# Using an existing variable +let a = 2; +let x = [ 1, a, 3 ]; -You can put a variable's content in a list (without giving it a name in the list) like so: +# Assigning outright +let y = [ 1, a: 2, 3 ]; ``` -let a = 1; -# [ 0, 1, 2] -let x = [ 0, :a, 2] -# This is also possible, although not very useful -# [ 3 ] -let y = [ :3 ] -``` +These named elements are not string key-value pairs (unlike dictionaries in some other languages). Instead, they are identifiers within the list's scope block, similar to variables in a function scope. Accessing data in lists can be accomplished in a few ways. You can iterate over a list: + ``` let x = [ 1, 2, 3 ]; # Lists start at index 0, so this will print numbers 1 to 3 loop ( num in range ( 0, 2 ) ) { - print ( x [ num ] ); + print ( x [ num ] ); } ``` Data can also be accessed outright using it's name or a variable's content: + ``` let a = 2; let x = [ 1, a: 2, 3 ]; @@ -197,35 +229,39 @@ print ( x['a'] ); If you try to access a key that isn't in the list, it returns `none` For example: + ``` let x = [ 1, 2, 3 ]; if ( x[10] == none ) { - // 10 is way outside the list! So this executes - print("Lists much be shorter than 10!"); + # 10 is way outside the list! So this executes + print ( "Lists much be shorter than 10!" ); } ``` By extention, setting a key to `none` deletes it: + ``` let x = [ 1, 2, 3 ]; # 2 -print ( x[1] ); +print ( x [ 1 ] ); -x[1] = none: +x [ 1 ] = none; # 3 -print ( x[1] ); +print ( x [ 1 ] ); ``` However, this does mean that having `none` as keys is ignored: + ``` # Prints 4, but because print returns none, x is set to [4] -let x = [ 4, print(4) ]; +let x = [ 4, print ( 4 ) ]; ``` Lists can be appended to: + ``` let x = [ 1, 2, 3 ]; x += 4; @@ -238,6 +274,7 @@ y += 1; ``` Lists can have new keys added: + ``` let x = []; x["a"] = 1; @@ -261,6 +298,7 @@ x[3] = 5.25; ``` Lists can also be concatenated: + ``` let x = [ 1, 2, 3 ]; x += [ 4, 5, 6 ]; @@ -269,6 +307,7 @@ x += [ 4, 5, 6 ]; ``` To append a list into a list, surround the list with brackets: + ``` let x = [ 1, 2, 3 ]; x += [[ 4, 5, 6 ]]; @@ -277,6 +316,7 @@ x += [[ 4, 5, 6 ]]; ``` Lists can be multiplied: + ``` let x = [ 1 ] * 3; x += [ 2, 3 ] * 2; @@ -285,6 +325,7 @@ x += [ 2, 3 ] * 2; ``` Lists cannot be multiplied if they *have* named keys, but may still be multiplied if they *had* named keys: + ``` let x = [1, 2, a: 6 ]; # Invalid, x has named keys @@ -300,11 +341,13 @@ x * 2; File IO is a core feature of Sack, and as such it has it's own syntax. Files are opened using the `open` keyword, and closed using the `close` keyword. There are multiple modes for opening files, and they are as follows: + ``` let file1 = open ( "filename", "mode" ); ``` The first argument is the file path, and the second is the mode. The mode can be one of the following: + - `r` - Read - `w` - Write - `a` - Append @@ -314,14 +357,17 @@ The first argument is the file path, and the second is the mode. The mode can be Files can be read using the `read` keyword, and written to using the `write` keyword. Here is an example of a file being opened, read, and closed: + ``` let file = open ( "file.txt", "r" ); let data = read ( file ); close ( file ); ``` + In this example, the `data` variable will be a string containing the contents of the file. Here is an example of a file being opened, written to, and closed: + ``` let file = open ( "file.txt", "w" ); write ( file, "hello world" ); @@ -329,6 +375,7 @@ close ( file ); ``` Performing an invalid operation, such as writing to a read only file or vice versa, will result in an error. For example, the following code will error: + ``` let file = open ( "file.txt", "r" ); write ( file, "hello world" ); @@ -336,13 +383,16 @@ close ( file ); ``` To write to a specific location in a file, you can use the `seek` keyword. This will move the file pointer to a specific location in the file. The following code will write to the 5th byte in the file: + ``` let file = open ( "file.txt", "w" ); seek ( file, 5 ); write ( file, "hello world" ); close ( file ); ``` + Overseeking will result in an error. For example, the following code will error: + ``` let file = open ( "file.txt", "w" ); @@ -356,6 +406,7 @@ close ( file ); ``` Flushing a file is done using the `flush` keyword. This is useful for writing to files that are opened in append mode. + ``` let file = open ( "file.txt", "a" ); write ( file, "hello world" ); @@ -364,6 +415,7 @@ close ( file ); ``` Files can also be read and written to using binary data. This is done using the `read` and `write` keywords with the file opened in binary mode. Reading and writing binary data is done using the `byte` type. In this case the file object will be a list of bytes. + ``` let file = open ( "file.txt", "rb" ); let data = read ( file ); @@ -375,6 +427,7 @@ close ( file ); ``` File data can also be printed to the console. The following code will print the contents of a file to the console: + ``` # file.txt contains the text "hello world" let file = open ( "file.txt", "r" ); @@ -385,21 +438,24 @@ close ( file ); ``` Files can be iterated over using the `loop` keyword. The following code will print the contents of a file to the console: + ``` # file.txt contains the text "hello world" let file = open ( "file.txt", "rb" ); let data = read ( file ); loop ( byte in data ) { - print ( byte ); + print ( byte ); } ``` + alternatively: + ``` # file.txt contains the text "hello world" let file = open ( "file.txt", "r" ); let data = read ( file ); loop ( line in data ) { - print ( line ); + print ( line ); } ``` @@ -412,63 +468,67 @@ As an example, here's a function that adds two numbers together: ``` functi add_numbers ( a, b ) { - let ret = a + b; - return ret; + let ret = a + b; + return ret; } ``` Functions aren't allowed to be anonymous. As such, the following is **invalid** syntax: + ``` functi( a, b ) { - - let ret = a + b; - return ret; - + + let ret = a + b; + return ret; + } ``` Functions can be assigned to variables however: + ``` functi add_numbers ( a, b ) { - - let ret = a + b; - return ret; - + + let ret = a + b; + return ret; + } let add_numbers_copy = add_numbers; ``` Functions can also be overloaded, this is done based on the number of arguments: + ``` functi add_numbers ( a, b ) { - - let ret = a + b; - return ret; - + + let ret = a + b; + return ret; + } functi add_numbers ( a, b, c ) { - - let ret = a + b + c; - return ret; + + let ret = a + b + c; + return ret; } ``` The overloaded function to use is chosen when it is called, this may seem obvious but consider the following (which is valid): + ``` functi add_numbers ( a, b ) { - - let ret = a + b; - return ret; - + + let ret = a + b; + return ret; + } functi add_numbers ( a, b, c ) { - - let ret = a + b + c; - return ret; + + let ret = a + b + c; + return ret; } @@ -482,12 +542,13 @@ adder ( 21, 5, 1940 ); ``` Functions can return a singular variable using the `return` keyword. Do note that any code after a return is unreachable and will be ignored by the implementation. As such, the following will result in unreachable code: + ``` functi add_numbers ( a, b ) { - let c = a + b; - return c; - print( "hi" ); + let c = a + b; + return c; + print( "hi" ); } ``` @@ -495,6 +556,7 @@ functi add_numbers ( a, b ) { ### Logical operators The following are valid logical operators in sack: + ``` # add to identifier += @@ -539,10 +601,12 @@ The following are valid logical operators in sack: # Not (inverts a boolean value) ! ``` + By extension `<=` and `>=` are also valid since they are a combination of the greater than and less than operators with the equal operator. Operators which check for a condition return a boolean value `true` or `false`. ### Comments + Comments are lines not executed by the parser that are used to enhance readability. - All comments exist at the start of a new line and obey indentation (not compiler enforced) @@ -550,15 +614,18 @@ Comments are lines not executed by the parser that are used to enhance readabili - A comment cannot be the last line of a program The following are valid comments: + ``` # hello #hello ``` ### If, else, else if + Conditionals are compiler determined functions which check for a certain function. The following are valid conditionals (assuming `a` is defined): + ``` if a > 1 { @@ -578,15 +645,19 @@ if a > 1 { Else if is valid because it's a combination of the else and the if operator, it is not syntactically unique. ### Print + Print is a language defined output function. It takes in a single variable and returns the output to the terminal appending a newline The following are valid print statements: + ``` print ( "hello" ); print ( 42 ); print ( a ); # Only valid if the variable "a" is defined, else it returns an error and the program stops. ``` + Print can accept the following types: + - String - Number - Decimal @@ -595,17 +666,21 @@ Print can accept the following types: - Bool ### Input + Similar to print, Input is a language defined input function. It allows for a prompt, and will read text entered into the program. The following is a valid input statement: + ``` let a = input ( "Please enter your name: " ); ``` ### The `in` keyword + The `in` keyword is used to check if a value is in a list or string. It returns a boolean value. The following are valid `in` statements: + ``` let a = "hello"; let b = "h"; @@ -627,15 +702,17 @@ print ( f in d ); ``` It follows that conditional statements can also use the `in` keyword: + ``` let a = "hello"; let b = "h"; if ( b in a ) { - print ( "hello contains h" ); + print ( "hello contains h" ); } ``` Invalid types will result in an error: + ``` let a = "forty-seven"; let b = 47; @@ -645,30 +722,34 @@ print ( b in a ); ``` ### Loops + A loop will iterate between a range of numbers starting at the first number and ending at the last for example `range( 1, 5 )` is equal to `[1, 2, 3, 4, 5]` The following is an example of valid loop in range syntax: + ``` loop ( a in range( 1, 100 ) ) { - print ( a ); + print ( a ); } ``` While loops are also possible: + ``` let a = 1; loop ( while a < 100 ) { - print ( a ); - a += 1; + print ( a ); + a += 1; } ``` There is a quicker way to write a infinite loop: + ``` loop { - print("This will print forever!"); + print("This will print forever!"); } ``` @@ -678,25 +759,26 @@ The keywords `break` and `continue` will exit the loop, or continue to the next # Looping from 1 to 10, but breaking out at 5 let a = 1; loop ( while a <= 10 ) { - if a == 5 { - break; - } + if a == 5 { + break; + } } # Looping from 1 to 10 but only the odd numbers a = 1; loop ( while a <= 10 ) { - if a % 2 { - continue; - } + if a % 2 { + continue; + } } ``` ### Language Defined Functions -Sack defines a few functions for convenience. These are typically related to typecasting, and are reserved keywords. +Sack defines a few functions for convenience. These are typically related to typecasting, and are reserved keywords. Most data types can be converted into any other data type. + ``` # Convert to int. # Will round a Decimal to the nearest integer @@ -718,9 +800,11 @@ string ( x ); # if the string is a single character, it will return a single byte. byte ( x ); ``` + When converting between types, invalid conversions will return `none`. You can quickly get the type of a variable using the `type()` function: + ``` let x = 3; @@ -742,6 +826,7 @@ print ( type ( print ) ); ``` You can get the length of a list or string with `len()`: + ``` let x = [ 1, 2, 3 ]; @@ -758,18 +843,23 @@ print ( len( [] ) ); ``` You can get the arguments passed to a function with `args()`: + ``` functi test ( a, b, c ) { - print ( args() ); + print ( args() ); } ``` + Args will also return a list of arguments passed to the program if called outside of a function. with the first argument being the name/path of the program. + ``` # prints `["example.sk"]` print ( args() ); ``` + If the function that is called has no arguments, `args()` will return `none`, however if args is called in the global scope it's length will always be greater than 0. The `rand(min, max)` function can be used to get a random number between `min` and `max` (inclusive), for example: + ``` let e = rand(5, 47); # Nobody knows exactly what e is a now @@ -778,19 +868,21 @@ let e = rand(5, 47); ``` ### Importing functions -By using the `import` keyword you can use functions from other sack programs. + +By using the `import` standard function you can access other files and their functions. Example: + ``` # exampleModule.sk functi helloworld() { - print ( "Helloworld" ); + print ( "Helloworld" ); } ``` ``` # exampleProgram.sk -import "exampleModule"; +import ( "exampleModule" ); # prints "Helloworld" to the console helloworld(); @@ -800,28 +892,31 @@ You can import modules that are in the current directory without specifying the By default sack first tries to import modules ending with ".sk", if that fails it will look for files ending with ".sack", if that also fails it will cause an error on runtime. Because there are multiple valid extensions for a Sack file, you can also specify the extension directly. Be careful though, as a mismatch will error: + ``` -import "exampleModule.sk"; +import ( "exampleModule.sk" ); # The following will error, as the file extension is incorrect -import "exampleModule.sack"; +import ( "exampleModule.sack" ); ``` You may also import a package, by simply importing a folder: + ``` # examplePackage/main.sk functi helloworld() { - print ( "Helloworld" ); + print ( "Helloworld" ); } # exampleProgram.sk -import "examplePackage"; +import ( "examplePackage" ); # prints "Helloworld" to the console helloworld(); ``` ### Scope + The following rules govern scope in sack. 1. You can only access from the global scope or the current scope. @@ -830,21 +925,24 @@ The following rules govern scope in sack. 4. Functions can only access arguments or global vars that are defined before them. There are also some rules for defining functions and variables: + 1. Functions and variables can not be defined with the name of a builtin function. 2. Functions can not be defined if an existing variable has that name, or if a function with the same number of args exists. 3. Variables can not be defined if a function with the same name exists. Following these rules, the following should print four: + ``` let e = 3; functi print_e() { - print ( e ); + print ( e ); } e = 4; print_e(); ``` Likewise, the following should print `[e: 3, x: 5]` + ``` let e = 3; let list = [ e, x: 5 ]; @@ -860,18 +958,19 @@ print ( list ); - Any operation which contains a number and a decimal will result in a decimal. - Semicolons are required to end a non commented line. Function declarations and conditionals do not need them as the closing `}` terminates the line. - Naming - - Identifiers may be given any alphanumeric name that does not start with a number. Underscores are allowed anywhere in the variable name +- Identifiers may be given any alphanumeric name that does not start with a number. Underscores are allowed anywhere in the variable name ## Sample program(s) FizzBuzz: + ``` functi checker ( num ) { let a = num % 15; - let b = num % 5; - let c = num % 3; + let b = num % 3; + let c = num % 5; if a == 0 { - print ( "FizzBuzz" ); + print ( "FizzBuzz" ); } else if b == 0 { print ( "Fizz" ); @@ -885,8 +984,6 @@ functi checker ( num ) { } loop ( num in range( 1, 100 ) ) { - - checker ( num ); - + checker ( num ); } ```