From d3124b38bceb90d9c81280d66ff6258d5e28e7a3 Mon Sep 17 00:00:00 2001 From: mertcandav Date: Sat, 5 Aug 2023 21:55:16 +0300 Subject: [PATCH] minor&major improvements --- .vitepress/config.ts | 59 ++- src/common-concepts/control-flow.md | 370 ------------------ .../control-flow/conditional.md | 51 +++ src/common-concepts/control-flow/index.md | 2 + .../control-flow/iterations.md | 138 +++++++ src/common-concepts/control-flow/labels.md | 53 +++ .../control-flow/match-statement.md | 120 ++++++ src/compiler/compiling/index.md | 3 + .../compiling/using-backend-compiler.md | 8 + .../using-julec.md} | 23 +- .../install-from-source/build-scripts.md | 43 ++ .../install-from-source/compile-from-ir.md | 28 ++ .../install-from-source/index.md | 3 + .../manual-compilation.md} | 58 +-- src/{basics => introduction}/blocks.md | 0 src/{basics => introduction}/comments.md | 0 src/{basics => introduction}/data-types.md | 0 src/{basics => introduction}/index.md | 2 +- src/{basics => introduction}/operators.md | 0 .../reserved-functions.md | 0 src/{basics => introduction}/syntax.md | 0 src/types/{string.md => strings.md} | 0 src/unsafe-jule/immutability.md | 16 + src/unsafe-jule/index.md | 122 ------ src/unsafe-jule/raw-pointers.md | 68 ++++ src/unsafe-jule/references.md | 27 ++ src/unsafe-jule/unsafe-defines.md | 14 + 27 files changed, 631 insertions(+), 577 deletions(-) delete mode 100644 src/common-concepts/control-flow.md create mode 100644 src/common-concepts/control-flow/conditional.md create mode 100644 src/common-concepts/control-flow/index.md create mode 100644 src/common-concepts/control-flow/iterations.md create mode 100644 src/common-concepts/control-flow/labels.md create mode 100644 src/common-concepts/control-flow/match-statement.md create mode 100644 src/compiler/compiling/index.md create mode 100644 src/compiler/compiling/using-backend-compiler.md rename src/compiler/{compiling.md => compiling/using-julec.md} (67%) create mode 100644 src/getting-started/install-from-source/build-scripts.md create mode 100644 src/getting-started/install-from-source/compile-from-ir.md create mode 100644 src/getting-started/install-from-source/index.md rename src/getting-started/{install-from-source.md => install-from-source/manual-compilation.md} (55%) rename src/{basics => introduction}/blocks.md (100%) rename src/{basics => introduction}/comments.md (100%) rename src/{basics => introduction}/data-types.md (100%) rename src/{basics => introduction}/index.md (87%) rename src/{basics => introduction}/operators.md (100%) rename src/{basics => introduction}/reserved-functions.md (100%) rename src/{basics => introduction}/syntax.md (100%) rename src/types/{string.md => strings.md} (100%) create mode 100644 src/unsafe-jule/immutability.md create mode 100644 src/unsafe-jule/raw-pointers.md create mode 100644 src/unsafe-jule/references.md create mode 100644 src/unsafe-jule/unsafe-defines.md diff --git a/.vitepress/config.ts b/.vitepress/config.ts index b6874e9..8abd13e 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -36,7 +36,15 @@ export default defineConfig({ link: '/getting-started/', items: [ { text: 'Downloads', link: '/getting-started/downloads' }, - { text: 'Install from Source', link: '/getting-started/install-from-source' }, + { + text: 'Install from Source', + link: '/getting-started/install-from-source/', + items: [ + { text: 'Build Scripts', link: '/getting-started/install-from-source/build-scripts' }, + { text: 'Manual Compilation', link: '/getting-started/install-from-source/manual-compilation' }, + { text: 'Compile From IR', link: '/getting-started/install-from-source/compile-from-ir' }, + ], + }, ], }, { @@ -52,7 +60,14 @@ export default defineConfig({ { text: 'Compiler Optimizations', link: '/compiler/compiler-optimizations' }, { text: 'Cross Transpilation', link: '/compiler/cross-transpilation' }, { text: 'Backend', link: '/compiler/backend' }, - { text: 'Compiling', link: '/compiler/compiling' }, + { + text: 'Compiling', + link: '/compiler/compiling/', + items: [ + { text: 'Using JuleC', link: '/compiler/compiling/using-julec' }, + { text: 'Using Backend Compiler', link: '/compiler/compiling/using-backend-compiler' }, + ], + }, ], }, { @@ -66,15 +81,15 @@ export default defineConfig({ ], }, { - text: 'Basics', - link: '/basics/', + text: 'Introduction', + link: '/introduction/', items: [ - { text: 'Comments', link: '/basics/comments' }, - { text: 'Reserved Functions', link: '/basics/reserved-functions' }, - { text: 'Data Types', link: '/basics/data-types' }, - { text: 'Operators', link: '/basics/operators' }, - { text: 'Syntax', link: '/basics/syntax' }, - { text: 'Blocks', link: '/basics/blocks' }, + { text: 'Comments', link: '/introduction/comments' }, + { text: 'Reserved Functions', link: '/introduction/reserved-functions' }, + { text: 'Data Types', link: '/introduction/data-types' }, + { text: 'Operators', link: '/introduction/operators' }, + { text: 'Syntax', link: '/introduction/syntax' }, + { text: 'Blocks', link: '/introduction/blocks' }, ], }, { @@ -86,7 +101,16 @@ export default defineConfig({ { text: 'Arrays', link: '/common-concepts/arrays' }, { text: 'Slices', link: '/common-concepts/slices' }, { text: 'Maps', link: '/common-concepts/maps' }, - { text: 'Control Flow', link: '/common-concepts/control-flow' }, + { + text: 'Control Flow', + link: '/common-concepts/control-flow/', + items: [ + { text: 'Iterations', link: '/common-concepts/control-flow/iterations' }, + { text: 'Conditional', link: '/common-concepts/control-flow/conditional' }, + { text: 'Match Statement', link: '/common-concepts/control-flow/match-statement' }, + { text: 'Labels', link: '/common-concepts/control-flow/labels' }, + ], + }, { text: 'Enums', link: '/common-concepts/enums' }, { text: 'Structures', link: '/common-concepts/structures' }, ], @@ -108,7 +132,16 @@ export default defineConfig({ { text: 'Memory Management', link: '/memory/memory-management' }, ], }, - { text: 'Unsafe Jule', link: '/unsafe-jule/index.md', items: [] }, + { + text: 'Unsafe Jule', + link: '/unsafe-jule/', + items: [ + { text: 'Immutability', link: '/unsafe-jule/immutability' }, + { text: 'Raw Pointers', link: '/unsafe-jule/raw-pointers' }, + { text: 'Unsafe Defines', link: '/unsafe-jule/unsafe-defines' }, + { text: 'References', link: '/unsafe-jule/references' }, + ], + }, { text: 'Error Handling', link: '/error-handling/', @@ -126,7 +159,7 @@ export default defineConfig({ { text: 'Aliasing', link: '/types/aliasing' }, { text: 'Casting', link: '/types/casting' }, { text: 'Generics', link: '/types/generics' }, - { text: 'String', link: '/types/string' }, + { text: 'Strings', link: '/types/strings' }, { text: 'Arrays', link: '/types/arrays' }, { text: 'Slices', link: '/types/slices' }, { text: 'Maps', link: '/types/maps' }, diff --git a/src/common-concepts/control-flow.md b/src/common-concepts/control-flow.md deleted file mode 100644 index 77f1cb7..0000000 --- a/src/common-concepts/control-flow.md +++ /dev/null @@ -1,370 +0,0 @@ -# Control Flow -We may want to guide the progress of the program with various conditions or repeat certain commands. That's what control flowers are for. Many programming languages have their control flows. - -## Iterations -Iterations allow you to repeat the algorithm according to certain conditions. The `for` keyword use for iterations in Jule. - -### Infinity Iterations -Infinite iterations keep repeating endlessly until the loop is somehow broken. - -For example: -``` -fn main() { - for { - outln("Hello, iterations") - } -} -``` -The above example prints `Hello, iterations` repeatedly. - -### While Iterations -The while iterations are iterations that repeat as long as a certain condition is met. It is not much different from defining an infinite iteration. - -For example: -``` -fn main() { - let mut counter = 0 - for counter <= 5 { - outln(counter) - counter += 10 - } -} -``` -The While loops use boolean expressions. As seen in the example above, the expression is written between the keyword and the block. This expression is evaluated before each loop, and if it returns true, the loop is iterated. This example just prints `0`. - -### While-Next Iterations -If you've ever used a programming language, you're probably familiar with for loops. Jule doesn't have the classic for loops. The main reason for this is that it does not look stylish and is not readable. The first of the three iteration statements almost always serves to define a scope-specific variable. The main reason for use is a conditional iteration and a post-iteration step. For this reason, Jule has a different iteration that is more readable and is thought to serve the purpose better: while-next - -While-next is almost the same as a classic while iteration. In this, the only addition is to be able to write an expression that will happen after the iteration step. While-next's statement is separated by the statement terminator. First comes the condition expression, then statement. - -For example: -``` -fn main() { - let mut i = 1 - for i <= 5; i++ { - outln(i) - } -} -``` -The while-next iteration above executes first if the condition is met. After execution, the statement is executed. Then the scope executes if the condition is met, so on. - -### Foreach Iterations -Foreach or for-each can be summarized as an iteration standard for collections. It repeats itself by looping through the elements of the collection. - -Each identifier used for foreach is used to create a new variable. So if there is a definition that already has this identifier, it will be shaded. - -For example: -``` -fn main() { - let s = "Hello" - for i in s { - outln(i) - } -} - -// OUTPUT -// 0 -// 1 -// 2 -// 3 -// 4 -``` -Seen as the example at above, this is a foreach iteration.\ -Iterations can have two variables: Current index and current element. - -This example, just shows index. Let's see foreach iteration with content. - -For example: -``` -fn main() { - let s = "Hello" - for _, b in s { - outln(b) - } -} - -// OUTPUT -// 72 -// 101 -// 108 -// 108 -// 111 -``` -As you can see, it is possible to use the ignore operator for unused fields. -::: tip -Jule assign variables data types by automatically by collection. Similar to type inferred variables. If the index variable is be numeric, Jule's auto data type is `int` type. -::: - ---- - -Foreach iterations have immutable variables by default. But you may want to get them as mutable. For this, enclose the identifiers in parentheses and qualify the variable you want to be mutable as mutable. - -For example: -``` -fn main() { - let s = "Hello" - for (_, mut b) in s { - outln(b) - } -} -``` - -### Iteration Controlling -We may want to check for iterations, this is normal and common. There are two ways to do this in Jule; The `continue` and `break` keywords. - -If you want break the iteration, use the `break` keyword. - -For example: -``` -fn main() { - for { - outln("Hello, World") - break - } -} -``` -The example at above, normally prints `Hello, World` again and again. But just prints one time, because `break` keyword is breaks iteration. - ---- - -If you want continue to next iteration, use the `continue` keyword. - -For example: -``` -fn main() { - for { - continue - outln("Hello, World") - } -} -``` -The example at above, normally prints `Hello, World` again and again. But prints nothing, because `continue` keyword is continue to next iteration. So print operation is the unreachable code. - -## If Expressions -If expressions allow you to manipulate the algorithm according to certain conditions. The `if` and `else` keywords use for if expressions in Jule. - -### `if` Expression -If the provided condition is `true` the block is executed, otherwise it is not executed. It is also the beginning of a new chain of conditions. - -For example: -``` -fn main() { - let x: Error - if x == nil { - outln("error is not initialized") - } -} -``` - -### `else if` Expression -If the preceding `if` and `else if` expressions have not been fulfilled, it is a condition presented as an alternative to them. - -For example: -``` -fn main() { - let x = 100 - if x > 1000 { - outln("greater than thousand") - } else if x < 100 { - outln("less than hundred") - } else if x == 100 { - outln("equals to hundred") - } -} - -// OUTPUT: equals to hundred -``` - -### `else` blocks -It is the block that will be executed unconditionally if the previous `if` and `else if` expressions are not fulfilled. - -For example: -``` -use std::errors - -fn main() { - let x: Error = std::errors::new("my error message") - if x == nil { - outln("error is not initialized") - } else { - outln("error is initialized") - } -} -``` - -## Match Expressions -If you need to make a selection and run an algorithm based on that selection, `match` is a good choice. The operator `|` is used for each case. For a block to be executed if not exist any match, don't give any expression to one case. - -**Syntax** -``` -match EXPRESSION { -| CASE_EXPRESSION1: // Body -| CASE_EXPRESSION2: // Body -| CASE_EXPRESSION3: // Body -|: // Body -} -``` -**EXPRESSION**: Expression to match. -**CASE_EXPRESSION1**: Expression for case. -**CASE_EXPRESSION2**: Expression for another case. -**CASE_EXPRESSION3**: Expression for another case. -**No expression case**: Default block. - -For example: -``` -match my_integer { -| MY_INTEGER_MIN: outln("Minimum") -| MY_INTEGER_MAX: outln("Maximum") -|: outln("Between or not") -} -``` - -### Breaking Execution -As with iterations, you can break the execution of the block. The keyword `break` is sufficient for this. - -For example: -``` -match X { -| Y: - if Y == A { - break - } - // ... -| Z: - // ... -} -``` - -### Condition Chain -If a match expression is not given, match acts like an if-else chain. This might be a more readable option on long condition chains. - -For example: -``` -match { -| x > 10 || x < 90: - // Body -| my_bool: - // Body -| y == 100: - // Body -|: - // Body -} -``` - -### Multiple Case Expression -You can have a single algorithm for multiple cases. For this, you can give more than one expression for a case. The only addition in syntax is vline operator (`|`) between expressions. - -For example: -``` -match X { -| Y | Z | V: - // Body -| A | B: - // Body -| C: - // Body -|: - // Body -} -``` - -### The `fall` Keyword -The fall keyword can only useable into case scopes and end of the scopes. It continues to next scope. - -For example: -``` -match { -| false: - outln("Case1") -| true: - outln("Case2") - fall -| false: - outln("Case3") - fall -|: - outln("Default") -} -``` - -Output: -``` -Case2 -Case3 -Default -``` - -### Type Matching -The `any` data type may contain any data and you may want to execute different algorithms based on this data, in which case type matching is useful. You can also determine types of trait's data. Type matching is easy. Just use the keyword `type` and then use the data type in case to match. - -For example: -``` -fn main() { - let x: any = 10 - match type x { - | int: - outln("integer") - | f32 | f64: - outln("floating-point") - |: - outln("other") - } -} -``` - -## Goto Statements -The goto statements allow you to jump to any part of the algorithm. - -### Labels -Goto statements need labels to jump somewhere in the algorithm. To declare a label, simply put the name of the tag followed by a colon. - -For example: `repeat:` - -### Going to Labels -The `goto` keyword is used for a goto statement. Jumping to a label is as simple as defining a label. First comes the keyword, then the label you want to jump to. - -For example: `goto repeat` -::: tip -- You can jump to any label without breaking the rules. -- Labels are only valid for the function block you are in. -::: -::: warning -- If your jumps over any declaration you will get a compiler error. -- Each label declared and not used causes a compiler error. -::: - -### Labels for `break` and `continue` Keywords -When using nested iterations or match expressions, the keywords `break` and `continue` are targeted to the last valid block. This makes it harder to target outer loops or the match expression. - -For example: -``` -fn main() { - for { - match { - | true: - break - } - } -} -``` -An infinite iteration appears in this example. The `break` keyword inside the match expression breaks the match expression. This way there is no way to break the infinite loop. Maybe alternative solutions like using a goto label outside of the iteration could be adopted but this is confusion. - -Again, label can be used to clear up this confusion. This is a more maintainable and clearer solution. Labels defined before an iteration and match expression can be used for targeting. - -For example: -``` -fn main() { -loop: - for { - match { - | true: - break loop - } - } -} -``` -The above example will break the iteration outside. Because the "loop" label used by the `break` keyword indicates that iteration. -::: tip -These labels are not special for that, so `goto` keyword can use these labels. -::: \ No newline at end of file diff --git a/src/common-concepts/control-flow/conditional.md b/src/common-concepts/control-flow/conditional.md new file mode 100644 index 0000000..c6460b6 --- /dev/null +++ b/src/common-concepts/control-flow/conditional.md @@ -0,0 +1,51 @@ +# Conditional +If expressions allow you to manipulate the algorithm according to certain conditions. The `if` and `else` keywords use for if expressions in Jule. + +## `if` Statement +If the provided condition is `true` the block is executed, otherwise it is not executed. It is also the beginning of a new chain of conditions. + +For example: +``` +fn main() { + let x: Error + if x == nil { + outln("error is not initialized") + } +} +``` + +## `else if` Statement +If the preceding `if` and `else if` expressions have not been fulfilled, it is a condition presented as an alternative to them. + +For example: +``` +fn main() { + let x = 100 + if x > 1000 { + outln("greater than thousand") + } else if x < 100 { + outln("less than hundred") + } else if x == 100 { + outln("equals to hundred") + } +} + +// OUTPUT: equals to hundred +``` + +## `else` Statement +It is the block that will be executed unconditionally if the previous `if` and `else if` expressions are not fulfilled. + +For example: +``` +use std::errors + +fn main() { + let x: Error = std::errors::new("my error message") + if x == nil { + outln("error is not initialized") + } else { + outln("error is initialized") + } +} +``` diff --git a/src/common-concepts/control-flow/index.md b/src/common-concepts/control-flow/index.md new file mode 100644 index 0000000..8ab036d --- /dev/null +++ b/src/common-concepts/control-flow/index.md @@ -0,0 +1,2 @@ +# Control Flow +We may want to guide the progress of the program with various conditions or repeat certain commands. That's what control flowers are for. Many programming languages have their control flows. diff --git a/src/common-concepts/control-flow/iterations.md b/src/common-concepts/control-flow/iterations.md new file mode 100644 index 0000000..59febc2 --- /dev/null +++ b/src/common-concepts/control-flow/iterations.md @@ -0,0 +1,138 @@ +# Iterations +Iterations allow you to repeat the algorithm according to certain conditions. The `for` keyword use for iterations in Jule. + +## Infinity Iterations +Infinite iterations keep repeating endlessly until the loop is somehow broken. + +For example: +``` +fn main() { + for { + outln("Hello, iterations") + } +} +``` +The above example prints `Hello, iterations` repeatedly. + +## While Iterations +The while iterations are iterations that repeat as long as a certain condition is met. It is not much different from defining an infinite iteration. + +For example: +``` +fn main() { + let mut counter = 0 + for counter <= 5 { + outln(counter) + counter += 10 + } +} +``` +The While loops use boolean expressions. As seen in the example above, the expression is written between the keyword and the block. This expression is evaluated before each loop, and if it returns true, the loop is iterated. This example just prints `0`. + +## While-Next Iterations +If you've ever used a programming language, you're probably familiar with for loops. Jule doesn't have the classic for loops. The main reason for this is that it does not look stylish and is not readable. The first of the three iteration statements almost always serves to define a scope-specific variable. The main reason for use is a conditional iteration and a post-iteration step. For this reason, Jule has a different iteration that is more readable and is thought to serve the purpose better: while-next + +While-next is almost the same as a classic while iteration. In this, the only addition is to be able to write an expression that will happen after the iteration step. While-next's statement is separated by the statement terminator. First comes the condition expression, then statement. + +For example: +``` +fn main() { + let mut i = 1 + for i <= 5; i++ { + outln(i) + } +} +``` +The while-next iteration above executes first if the condition is met. After execution, the statement is executed. Then the scope executes if the condition is met, so on. + +## Foreach Iterations +Foreach or for-each can be summarized as an iteration standard for collections. It repeats itself by looping through the elements of the collection. + +Each identifier used for foreach is used to create a new variable. So if there is a definition that already has this identifier, it will be shaded. + +For example: +``` +fn main() { + let s = "Hello" + for i in s { + outln(i) + } +} + +// OUTPUT +// 0 +// 1 +// 2 +// 3 +// 4 +``` +Seen as the example at above, this is a foreach iteration.\ +Iterations can have two variables: Current index and current element. + +This example, just shows index. Let's see foreach iteration with content. + +For example: +``` +fn main() { + let s = "Hello" + for _, b in s { + outln(b) + } +} + +// OUTPUT +// 72 +// 101 +// 108 +// 108 +// 111 +``` +As you can see, it is possible to use the ignore operator for unused fields. +::: tip +Jule assign variables data types by automatically by collection. Similar to type inferred variables. If the index variable is be numeric, Jule's auto data type is `int` type. +::: + +--- + +Foreach iterations have immutable variables by default. But you may want to get them as mutable. For this, enclose the identifiers in parentheses and qualify the variable you want to be mutable as mutable. + +For example: +``` +fn main() { + let s = "Hello" + for (_, mut b) in s { + outln(b) + } +} +``` + +## Iteration Controlling +We may want to check for iterations, this is normal and common. There are two ways to do this in Jule; The `continue` and `break` keywords. + +If you want break the iteration, use the `break` keyword. + +For example: +``` +fn main() { + for { + outln("Hello, World") + break + } +} +``` +The example at above, normally prints `Hello, World` again and again. But just prints one time, because `break` keyword is breaks iteration. + +--- + +If you want continue to next iteration, use the `continue` keyword. + +For example: +``` +fn main() { + for { + continue + outln("Hello, World") + } +} +``` +The example at above, normally prints `Hello, World` again and again. But prints nothing, because `continue` keyword is continue to next iteration. So print operation is the unreachable code. \ No newline at end of file diff --git a/src/common-concepts/control-flow/labels.md b/src/common-concepts/control-flow/labels.md new file mode 100644 index 0000000..2d29fb9 --- /dev/null +++ b/src/common-concepts/control-flow/labels.md @@ -0,0 +1,53 @@ +# Labels +Goto statements need labels to jump somewhere in the algorithm. To declare a label, simply put the name of the tag followed by a colon. + +For example: `repeat:` + +## Going to Labels +The goto statements allow you to jump to any part of the algorithm. +The `goto` keyword is used for a goto statement. Jumping to a label is as simple as defining a label. First comes the keyword, then the label you want to jump to. + +For example: `goto repeat` +::: tip +- You can jump to any label without breaking the rules. +- Labels are only valid for the function block you are in. +::: +::: warning +- If your jumps over any declaration you will get a compiler error. +- Each label declared and not used causes a compiler error. +::: + +## Labels for `break` and `continue` Keywords +When using nested iterations or match expressions, the keywords `break` and `continue` are targeted to the last valid block. This makes it harder to target outer loops or the match expression. + +For example: +``` +fn main() { + for { + match { + | true: + break + } + } +} +``` +An infinite iteration appears in this example. The `break` keyword inside the match expression breaks the match expression. This way there is no way to break the infinite loop. Maybe alternative solutions like using a goto label outside of the iteration could be adopted but this is confusion. + +Again, label can be used to clear up this confusion. This is a more maintainable and clearer solution. Labels defined before an iteration and match expression can be used for targeting. + +For example: +``` +fn main() { +loop: + for { + match { + | true: + break loop + } + } +} +``` +The above example will break the iteration outside. Because the "loop" label used by the `break` keyword indicates that iteration. +::: tip +These labels are not special for that, so `goto` keyword can use these labels. +::: \ No newline at end of file diff --git a/src/common-concepts/control-flow/match-statement.md b/src/common-concepts/control-flow/match-statement.md new file mode 100644 index 0000000..ebad6a1 --- /dev/null +++ b/src/common-concepts/control-flow/match-statement.md @@ -0,0 +1,120 @@ +# Match Statements +If you need to make a selection and run an algorithm based on that selection, `match` is a good choice. The operator `|` is used for each case. For a block to be executed if not exist any match, don't give any expression to one case. + +**Syntax** +``` +match EXPRESSION { +| CASE_EXPRESSION1: // Body +| CASE_EXPRESSION2: // Body +| CASE_EXPRESSION3: // Body +|: // Body +} +``` +**EXPRESSION**: Expression to match. +**CASE_EXPRESSION1**: Expression for case. +**CASE_EXPRESSION2**: Expression for another case. +**CASE_EXPRESSION3**: Expression for another case. +**No expression case**: Default block. + +For example: +``` +match my_integer { +| MY_INTEGER_MIN: outln("Minimum") +| MY_INTEGER_MAX: outln("Maximum") +|: outln("Between or not") +} +``` + +### Breaking Execution +As with iterations, you can break the execution of the block. The keyword `break` is sufficient for this. + +For example: +``` +match X { +| Y: + if Y == A { + break + } + // ... +| Z: + // ... +} +``` + +### Condition Chain +If a match expression is not given, match acts like an if-else chain. This might be a more readable option on long condition chains. + +For example: +``` +match { +| x > 10 || x < 90: + // Body +| my_bool: + // Body +| y == 100: + // Body +|: + // Body +} +``` + +### Multiple Cases +You can have a single algorithm for multiple cases. For this, you can give more than one expression for a case. The only addition in syntax is vline operator (`|`) between expressions. + +For example: +``` +match X { +| Y | Z | V: + // Body +| A | B: + // Body +| C: + // Body +|: + // Body +} +``` + +### The `fall` Keyword +The fall keyword can only useable into case scopes and end of the scopes. It continues to next scope. + +For example: +``` +match { +| false: + outln("Case1") +| true: + outln("Case2") + fall +| false: + outln("Case3") + fall +|: + outln("Default") +} +``` + +Output: +``` +Case2 +Case3 +Default +``` + +### Type Matching +The `any` data type may contain any data and you may want to execute different algorithms based on this data, in which case type matching is useful. You can also determine types of trait's data. Type matching is easy. Just use the keyword `type` and then use the data type in case to match. + +For example: +``` +fn main() { + let x: any = 10 + match type x { + | int: + outln("integer") + | f32 | f64: + outln("floating-point") + |: + outln("other") + } +} +``` \ No newline at end of file diff --git a/src/compiler/compiling/index.md b/src/compiler/compiling/index.md new file mode 100644 index 0000000..33453db --- /dev/null +++ b/src/compiler/compiling/index.md @@ -0,0 +1,3 @@ +# Compiling + +This section covers how you can compile your Jule packages and source codes using your compiler. diff --git a/src/compiler/compiling/using-backend-compiler.md b/src/compiler/compiling/using-backend-compiler.md new file mode 100644 index 0000000..7d02d71 --- /dev/null +++ b/src/compiler/compiling/using-backend-compiler.md @@ -0,0 +1,8 @@ +# Using Backend Compiler +JuleC has multiple officially supported C++ compilers. For this reason, it does not contain a specific C++ compiler in itself to keep your download sizes small and leave the choice to you. If you're using Linux or a similar operating system, you can usually already have an officially supported C++ compiler. Once you've decided on the C++ compiler you want to use, JuleC can take care of the rest for you. Before that, you need to give JuleC a few simple instructions. + +JuleC will automatically choose the recommended C++ compiler when compiling your code. If the recommended compiler is your preferred compiler, you don't need to take an action. But if not, you need to set your compiler using the related compiler option(s). + +If you need a special configuration for your build, it is recommended to create a script file for it or write compile command in a document such as a readme files. This makes it clearer and easier how to compile the project, as well as a faster and more comfortable development experience. + +Please read [backend](/compiler/backend) documentations for more information about supported C++ compilers and standards. \ No newline at end of file diff --git a/src/compiler/compiling.md b/src/compiler/compiling/using-julec.md similarity index 67% rename from src/compiler/compiling.md rename to src/compiler/compiling/using-julec.md index 2fab4c9..74160df 100644 --- a/src/compiler/compiling.md +++ b/src/compiler/compiling/using-julec.md @@ -1,6 +1,4 @@ -# Compiling - -## Using JuleC (Reference Compiler) +# Using JuleC (Reference Compiler) Let's start with a simple hello world program and learn compiling from this program. We have a `main.jule` file: @@ -21,13 +19,13 @@ In compile mode, JuleC will show you the build command itself on the command lin As result we have a executable machine code result of our program. -### Backend Compiler Optimizations +## Backend Compiler Optimizations By default, JuleC sets your backend compiler's optimizations to the lowest possible level and compiles your code as such. These optimizations are independent of the compiler optimizations that JuleC has, they are the backend-compiler optimizations you use for IR compilation. The created executable file is available for debugging. The lowest optimization level usually allows the backend-compiler to exhibit the fastest compilation performance. But without the generated executable optimizations it can be significantly underperforming. If you want to compile your code with backend-compiler optimizations, there are several ways you can do it. -#### Set Backend Compiler Optimizations via `pass` Directive +### Set Backend Compiler Optimizations via `pass` Directive You can add the `pass` directive to a suitable place in the main package of your program and adjust the optimizations of your backend-compiler. This method works most of the time. @@ -36,11 +34,11 @@ For example to enable Clang's `O3` optimizations: //jule:pass -O3 ``` -#### Transpile and Compile Manually +### Transpile and Compile Manually You can transpile your code and compile your IR code with your desired optimization setting by customizing the suggested compile command by compiler or with a completely custom compile command. -### Transpilation +## Transpilation Preferably, instead of compiling your code directly, you may want to transpile your code for various reasons to obtain the IR code. This scenario is mostly used when you want to distribute IR code, modify it, debug compiler's code generation, compile with different compilers or compilation commands. @@ -51,16 +49,7 @@ For example: julec --transpile . ``` -### Why JuleC Needs Directory Path Instead File Path? +## Why JuleC Needs Directory Path Instead File Path? In Jule, each program is also a package. Jule source codes in the directory are accepted within the package. Accordingly, every directory is actually a potential Jule package. The directory of the program you will compile should be the main package of your program. This also eliminates the need to link individual source codes to the compiler and significantly avoids the occurrence of long compiler commands. It makes it easy to understand which of the source codes are in the main program, the answer is simple: all the Jule source codes in the directory. Because of this approach, each Jule program is kept in a separate directory as a package, causing optimistic pressure on the project organization. - -## Using Backend Compiler -JuleC has multiple officially supported C++ compilers. For this reason, it does not contain a specific C++ compiler in itself to keep your download sizes small and leave the choice to you. If you're using Linux or a similar operating system, you can usually already have an officially supported C++ compiler. Once you've decided on the C++ compiler you want to use, JuleC can take care of the rest for you. Before that, you need to give JuleC a few simple instructions. - -JuleC will automatically choose the recommended C++ compiler when compiling your code. If the recommended compiler is your preferred compiler, you don't need to take an action. But if not, you need to set your compiler using the related compiler option(s). - -If you need a special configuration for your build, it is recommended to create a script file for it or write compile command in a document such as a readme files. This makes it clearer and easier how to compile the project, as well as a faster and more comfortable development experience. - -Please read [backend](/compiler/backend) documentations for more information about supported C++ compilers and standards. diff --git a/src/getting-started/install-from-source/build-scripts.md b/src/getting-started/install-from-source/build-scripts.md new file mode 100644 index 0000000..75b48dc --- /dev/null +++ b/src/getting-started/install-from-source/build-scripts.md @@ -0,0 +1,43 @@ +# Build Scripts + +The JuleC project has scripts for building/compiling the compiler. You can use them. If you execute a script, the result will be either a compilation error or a completion message. + +::: warning +You should have JuleC for this option. +These examples assume you are in the source code `(src/julec)` directory of the JuleC. +::: + +::: warning +The build scripts designed for developers. \ +Compiles JuleC with zero optimization. +::: + +### Windows +::: tip +Ideal scripts for Windows: usally batchfiles (.bat). +::: + +Using example for PowerShell: +``` +$ build.bat +``` + +### macOS (Darwin) +::: tip +Ideal scripts for macOS: usally shellscripts (.sh). +::: + +Using example for ZSH: +``` +$ zsh build.sh +``` + +### Linux +::: tip +Ideal scripts for Linux: usally shellscripts (.sh). +::: + +Using example for Bash: +``` +$ sh build.sh +``` diff --git a/src/getting-started/install-from-source/compile-from-ir.md b/src/getting-started/install-from-source/compile-from-ir.md new file mode 100644 index 0000000..3f213ec --- /dev/null +++ b/src/getting-started/install-from-source/compile-from-ir.md @@ -0,0 +1,28 @@ +# Compile From IR + +The IR codes are kept in this [respository](https://github.com/julelang/julec-ir), you need to get them here. + +This repository contains the platform-specific C++ IR code of JuleC, the reference compiler of the Jule programming language, that was created by leveraging JuleC's cross-transpile feature. When you are unable to compile the JuleC's new major branch codes with an existing JuleC build, when you want to get a JuleC build from IR code, when you want to examine the IRs created by JuleC, or for a different purpose, you can consider using C++ IR codes. A common usage scenario of this repository is to get a JuleC build that can compile up-to-date code and access up-to-date major code. + +This repository does not commit to update the content with every commit of JuleC. The main point to note is that the IR code provided is up-to-date enough to get you a JuleC build that can compile the master branch. That's why it usually gets updates after major compiler code changes. But it can also receive non-essential updates. + +If this code does not allow you to get the latest JuleC build, you can get a latest JuleC build from the master branch with the build you get with the IR code. + +## How to Compile an IR + +You need the Jule source codes to compile the IR code. IR codes have dependencies like some header files in API and standard library. Therefore, you must have obtained the Jule source code to compile the IR code. In most cases, it shouldn't be a problem for you to compile the most recent C++ headers in the main branch. However, for the most guaranteed experience, it is recommended to obtain the source tree of the commit where these IR codes were generated. The commit IR uses is mentioned above. + +The include directories of the IR codes are set to be in the root directory of the Jule source code of the IR code. So put your IR code inside the root directory of Jule source tree you got and build in that directory. If you want a different directory, you need to change the include directories of IR. + +Of course you need a C++ compiler to compile the IR code. At this point it is recommended to use one of the officially supported compilers. You can check [relavant manual page](/compiler/backend) for more details. + +We recommend compiling JuleC's IR codes with Clang and C++17. The recommended build command below is built accordingly. You can change the optimization level as you wish. -O0 is recommended for debugging. If you're using it to get the most up-to-date compiler in the main branch, you can still use -O0 to get the fastest build time, but it may also result in a JuleC build with a higher transpilation time. But in the general scenario you wait less time than you would expect for the -O3 optimization level. + +Recommended compile command: +```sh +clang++ -O3 -Wno-everything --std=c++17 ir.cpp +``` + +::: tip +Note that if you want to use the build you received as JuleC after the compilation phase, you have to place it in the bin directory in the root directory. Simply review any JuleC release to understand the relevant positioning. You can easily see the bin directory in the root directory. +::: diff --git a/src/getting-started/install-from-source/index.md b/src/getting-started/install-from-source/index.md new file mode 100644 index 0000000..744c6e6 --- /dev/null +++ b/src/getting-started/install-from-source/index.md @@ -0,0 +1,3 @@ +# Install from Source + +This method refers to compiling JuleC from or from source code. This is a good way if you don't want to use release or want to get the latest development version. diff --git a/src/getting-started/install-from-source.md b/src/getting-started/install-from-source/manual-compilation.md similarity index 55% rename from src/getting-started/install-from-source.md rename to src/getting-started/install-from-source/manual-compilation.md index 55d9671..0006825 100644 --- a/src/getting-started/install-from-source.md +++ b/src/getting-started/install-from-source/manual-compilation.md @@ -1,63 +1,13 @@ -# Install from Source - -::: warning -You should have JuleC for this option. -::: - -Ok! We will install JuleC from source code. Actually, we will just compile the project. Getting the latest version is a good way to start. We assume that you already have the Jule source code. - -## Build Scripts -The JuleC project has scripts for building/compiling the compiler. You can use them. If you execute a script, the result will be either a compilation error or a completion message. - -::: warning -These examples assume you are in the source code `(src/julec)` directory of the JuleC. -::: - -::: warning -The build scripts designed for developers. \ -Compiles JuleC with zero optimization. -::: - -### Windows -::: tip -Ideal scripts for Windows: usally batchfiles (.bat). -::: - -Using example for PowerShell: -``` -$ build.bat -``` - -### macOS (Darwin) -::: tip -Ideal scripts for macOS: usally shellscripts (.sh). -::: - -Using example for ZSH: -``` -$ zsh build.sh -``` - -### Linux -::: tip -Ideal scripts for Linux: usally shellscripts (.sh). -::: - -Using example for Bash: -``` -$ sh build.sh -``` - -## Manual Compilation +# Manual Compilation JuleC is written in Jule! ::: warning -These example assume you are in the source code `(src/julec)` directory of the JuleC. -This example also accepts you already have JuleC in global path. +These example assume you are in the root directory of the JuleC. +This example also accepts you already have JuleC in global path. ::: ``` -julec -o ../../bin/julec . +julec -o ./bin/julec ./src/julec ``` ## Compile Tips diff --git a/src/basics/blocks.md b/src/introduction/blocks.md similarity index 100% rename from src/basics/blocks.md rename to src/introduction/blocks.md diff --git a/src/basics/comments.md b/src/introduction/comments.md similarity index 100% rename from src/basics/comments.md rename to src/introduction/comments.md diff --git a/src/basics/data-types.md b/src/introduction/data-types.md similarity index 100% rename from src/basics/data-types.md rename to src/introduction/data-types.md diff --git a/src/basics/index.md b/src/introduction/index.md similarity index 87% rename from src/basics/index.md rename to src/introduction/index.md index 4f1e2ba..59191f0 100644 --- a/src/basics/index.md +++ b/src/introduction/index.md @@ -1,3 +1,3 @@ -# Basics +# Introduction The basics of the Jule programming language.\ Comments, operators and more. Knowledge for next steps. \ No newline at end of file diff --git a/src/basics/operators.md b/src/introduction/operators.md similarity index 100% rename from src/basics/operators.md rename to src/introduction/operators.md diff --git a/src/basics/reserved-functions.md b/src/introduction/reserved-functions.md similarity index 100% rename from src/basics/reserved-functions.md rename to src/introduction/reserved-functions.md diff --git a/src/basics/syntax.md b/src/introduction/syntax.md similarity index 100% rename from src/basics/syntax.md rename to src/introduction/syntax.md diff --git a/src/types/string.md b/src/types/strings.md similarity index 100% rename from src/types/string.md rename to src/types/strings.md diff --git a/src/unsafe-jule/immutability.md b/src/unsafe-jule/immutability.md new file mode 100644 index 0000000..eb414a2 --- /dev/null +++ b/src/unsafe-jule/immutability.md @@ -0,0 +1,16 @@ +# Immutability + +## Break Immutability +If a variable is immutable and has mutable data type, Safe Jule does not allow assigning it to a mutable variable. Memory safety on this is mentioned in the [immutability](/memory/immutability) documentations. There is a way to break it. It naturally means breaking safety as well, but you are conscious of it. Unsafe Jule does not encourage you to be safe about it. + +The knowledge that the pointers are obtained as mutable when they are received is important in this regard. So if you take the pointer of an immutable variable and use it mutable, you can change the data of the immutable variable. Since you can only assign to pointers with Unsafe Jule, you will be aware that this is already an action that can create unsafety. + +For example: +``` +fn main() { + let x = 10 + let mut xp = &x + unsafe { *xp += 20 } + outln(x) // 30 +} +``` \ No newline at end of file diff --git a/src/unsafe-jule/index.md b/src/unsafe-jule/index.md index ff93bcf..fbbbb86 100644 --- a/src/unsafe-jule/index.md +++ b/src/unsafe-jule/index.md @@ -20,125 +20,3 @@ Benefits of Unsafe Jule: - Access reference from parent scope Note that this does not lead to a completely unsafe use of Jule. Other than the listed unsafe behaviors, Safe Jule will continue to show itself. This means you get a level of safety even with unsafe blocks. - -Let's take a look at the unsafe behaviors listed above: - -## Break Immutability -If a variable is immutable and has mutable data type, Safe Jule does not allow assigning it to a mutable variable. Memory safety on this is mentioned in the [immutability](/memory/immutability) documentations. There is a way to break it. It naturally means breaking safety as well, but you are conscious of it. Unsafe Jule does not encourage you to be safe about it. - -The knowledge that the pointers are obtained as mutable when they are received is important in this regard. So if you take the pointer of an immutable variable and use it mutable, you can change the data of the immutable variable. Since you can only assign to pointers with Unsafe Jule, you will be aware that this is already an action that can create unsafety. - -For example: -``` -fn main() { - let x = 10 - let mut xp = &x - unsafe { *xp += 20 } - outln(x) // 30 -} -``` - -## Derefence a Raw Pointer -Unsafe Jule allows deference raw pointers. - -For example: -``` -fn main() { - let x = 200 - let ptr = &x - unsafe { outln(*ptr) } -} -``` -Note that no safety is provided in this regard. Pointers can benefit you, but you have to provide safety yourself. You need to be wary of dangling pointers, buffer overflows, and similar memory issues. - -## Postfixes for Raw Pointers -Unsafe Jule supports postfixes for raw pointers. - -For example: -``` -fn print_slice_components_with_unsafe(slc: []int) { - unsafe { - let mut ptr = &slc[0] - let mut end = &slc[slc.len-1] - end++ - for ptr < end { - outln(*ptr) - ptr++ - } - } -} -``` - -## Indexing with Raw Pointers -This is especially true if you have a pointer to an array (for example, an array pointer allocated with `std::mem::c` package), which allows you to use the pointer just like an array. Maybe you would prefer to use a wrapper to help with the length for the offset, as they are just raw pointers and don't have a way to get the lengths right away like in Jule's slice or array structures. - -The fact that this operation is covered by Unsafe Jule is not only because it has widespread pointer unsafety, but also because there is a possibility of overflow and this is not checked. For example, slice and array are safe and controlled in this regard. - -When you have an array of pointers, it can be interpreted semantically like this: It is a pointer to the component type, as the pointer usually points to one of the memory areas. So think of it like a pointer to the field of an element of an array. Indexing is sensitive to the data type according to the offset, skipping that much space in the memory, finding the position of the offset and selecting that area. - -To better understand data type sampling, array pointers can be interpreted as: -- `*int` = `[]int ` -- `*str` = `[]str` - -For example: -``` -ptr[9] -``` -Suppose the variable `ptr` is `*int`. Let this pointer be an array pointer. The above expression takes the data at index 9 of this array. - -## Cast Raw Pointers -You can cast a pointer to an integer with valid integer types or cast a raw pointer from an integer. However, you can also cast a pointer to a pointer of different type. - -For example: -``` -let ptr: int = 0 -let unsafe_ptr = unsafe { (*str)(ptr) } -``` - -## Call Unsafe Functions or Methods -You can call unsafe functions with Unsafe Jule. Functions or methods that qualify as unsafe can only be called with Unsafe Jule. Functions that qualify as unsafe can only be called with an Unsafe Jule and have an Unsafe Jule throughout their entire body. - -For example: -``` -unsafe fn my_unsafe_fn() { /* ... */ } -``` -::: tip -Before qualifying a function or method as unsafe, make sure that the function is not safe all time. Even if it performs unsafe operations, it is better for the function to act as a safe wrapper than to qualify as unsafe if safety is guaranteed. - -If safety depends on parameters and other external factors then it is better to qualify as unsafe. -::: - -## Concurrent Calls with Reference Parameters - -References cannot be used with concurrent calls because of potantial reference dangling. Because there is no clear guarantee that the concurrent call is being followed and that the program is safely waiting for the concurrent call to terminate at a non-dangling point. Therefore, your compiler cannot be sure that what you are doing is safe. - -If you are confident and aware that this concurrent call you make will be safe, you should take responsibility by showing that you know that the call you are making is unsafe. So your compiler will respect you and allow you to concurrent call a function with reference parameters. - -For example: -``` -unsafe { co my_function(my_var) } -``` - -## Pass Pointer to Reference - -If you want to send your pointers to a reference parameter, you can do so with a simple pointer dereferencing. You are aware of the insecurity as this is already an action you would take using Unsafe Jule. - -For example: -``` -my_function(unsafe { *my_pointer }) -``` - -## Access Reference from Parent Scope - -Anonymous functions copy the definitions of the scope in which they are defined for safety reasons, they do not refer to them. But a copied reference is still a reference and is in danger of dangling. Therefore, anonymous functions do not use references from parent scopes. But Unsafe Jule lets you do just that. Access the relevant reference only with Unsafe Jule. - -For example: -``` -fn main() { - let x = 10 - let &y = x - fn() { - unsafe { outln(y) } - }() -} -``` diff --git a/src/unsafe-jule/raw-pointers.md b/src/unsafe-jule/raw-pointers.md new file mode 100644 index 0000000..e5dd6ca --- /dev/null +++ b/src/unsafe-jule/raw-pointers.md @@ -0,0 +1,68 @@ +# Raw Pointers + +## Derefence a Raw Pointer +Unsafe Jule allows deference raw pointers. + +For example: +``` +fn main() { + let x = 200 + let ptr = &x + unsafe { outln(*ptr) } +} +``` +Note that no safety is provided in this regard. Pointers can benefit you, but you have to provide safety yourself. You need to be wary of dangling pointers, buffer overflows, and similar memory issues. + + +## Postfixes for Raw Pointers +Unsafe Jule supports postfixes for raw pointers. + +For example: +``` +fn print_slice_components_with_unsafe(slc: []int) { + unsafe { + let mut ptr = &slc[0] + let mut end = &slc[slc.len-1] + end++ + for ptr < end { + outln(*ptr) + ptr++ + } + } +} +``` + +## Indexing with Raw Pointers +This is especially true if you have a pointer to an array (for example, an array pointer allocated with `std::mem::c` package), which allows you to use the pointer just like an array. Maybe you would prefer to use a wrapper to help with the length for the offset, as they are just raw pointers and don't have a way to get the lengths right away like in Jule's slice or array structures. + +The fact that this operation is covered by Unsafe Jule is not only because it has widespread pointer unsafety, but also because there is a possibility of overflow and this is not checked. For example, slice and array are safe and controlled in this regard. + +When you have an array of pointers, it can be interpreted semantically like this: It is a pointer to the component type, as the pointer usually points to one of the memory areas. So think of it like a pointer to the field of an element of an array. Indexing is sensitive to the data type according to the offset, skipping that much space in the memory, finding the position of the offset and selecting that area. + +To better understand data type sampling, array pointers can be interpreted as: +- `*int` = `[]int ` +- `*str` = `[]str` + +For example: +``` +ptr[9] +``` +Suppose the variable `ptr` is `*int`. Let this pointer be an array pointer. The above expression takes the data at index 9 of this array. + +## Cast Raw Pointers +You can cast a pointer to an integer with valid integer types or cast a raw pointer from an integer. However, you can also cast a pointer to a pointer of different type. + +For example: +``` +let ptr: int = 0 +let unsafe_ptr = unsafe { (*str)(ptr) } +``` + +## Pass Pointer to Reference + +If you want to send your pointers to a reference parameter, you can do so with a simple pointer dereferencing. You are aware of the insecurity as this is already an action you would take using Unsafe Jule. + +For example: +``` +my_function(unsafe { *my_pointer }) +``` diff --git a/src/unsafe-jule/references.md b/src/unsafe-jule/references.md new file mode 100644 index 0000000..fa2cae6 --- /dev/null +++ b/src/unsafe-jule/references.md @@ -0,0 +1,27 @@ +# References + +## Concurrent Calls with Reference Parameters + +References cannot be used with concurrent calls because of potantial reference dangling. Because there is no clear guarantee that the concurrent call is being followed and that the program is safely waiting for the concurrent call to terminate at a non-dangling point. Therefore, your compiler cannot be sure that what you are doing is safe. + +If you are confident and aware that this concurrent call you make will be safe, you should take responsibility by showing that you know that the call you are making is unsafe. So your compiler will respect you and allow you to concurrent call a function with reference parameters. + +For example: +``` +unsafe { co my_function(my_var) } +``` + +## Access Reference from Parent Scope + +Anonymous functions copy the definitions of the scope in which they are defined for safety reasons, they do not refer to them. But a copied reference is still a reference and is in danger of dangling. Therefore, anonymous functions do not use references from parent scopes. But Unsafe Jule lets you do just that. Access the relevant reference only with Unsafe Jule. + +For example: +``` +fn main() { + let x = 10 + let &y = x + fn() { + unsafe { outln(y) } + }() +} +``` \ No newline at end of file diff --git a/src/unsafe-jule/unsafe-defines.md b/src/unsafe-jule/unsafe-defines.md new file mode 100644 index 0000000..a84b0a2 --- /dev/null +++ b/src/unsafe-jule/unsafe-defines.md @@ -0,0 +1,14 @@ +# Unsafe Defines + +## Call Unsafe Functions or Methods +You can call unsafe functions with Unsafe Jule. Functions or methods that qualify as unsafe can only be called with Unsafe Jule. Functions that qualify as unsafe can only be called with an Unsafe Jule and have an Unsafe Jule throughout their entire body. + +For example: +``` +unsafe fn my_unsafe_fn() { /* ... */ } +``` +::: tip +Before qualifying a function or method as unsafe, make sure that the function is not safe all time. Even if it performs unsafe operations, it is better for the function to act as a safe wrapper than to qualify as unsafe if safety is guaranteed. + +If safety depends on parameters and other external factors then it is better to qualify as unsafe. +:::