diff options
| -rw-r--r-- | spec/1.md | 660 | ||||
| -rw-r--r-- | spec/2.md | 101 | ||||
| -rw-r--r-- | spec/3.md | 48 | ||||
| -rw-r--r-- | spec/4.md | 48 | ||||
| -rw-r--r-- | spec/5.md | 101 | ||||
| -rw-r--r-- | spec/Appendices.md | 39 | ||||
| -rw-r--r-- | spec/README.md | 51 | ||||
| -rw-r--r-- | tnslc/README.md | 4 | ||||
| -rwxr-xr-x | tnslc/build.sh | 13 | ||||
| -rw-r--r-- | tnslc/compile/codegen.tnsl | 122 | ||||
| -rw-r--r-- | tnslc/compile/compbuf.tnsl | 45 | ||||
| -rw-r--r-- | tnslc/compile/compile.tnsl | 11 | ||||
| -rw-r--r-- | tnslc/compile/function.tnsl | 1918 | ||||
| -rw-r--r-- | tnslc/compile/generate.tnsl | 6 | ||||
| -rw-r--r-- | tnslc/compile/module.tnsl | 504 | ||||
| -rw-r--r-- | tnslc/compile/scope.tnsl | 752 | ||||
| -rw-r--r-- | tnslc/compile/struct.tnsl | 282 | ||||
| -rw-r--r-- | tnslc/compile/tests_var.tnsl | 248 | ||||
| -rw-r--r-- | tnslc/compile/type.tnsl | 360 | ||||
| -rw-r--r-- | tnslc/compile/var.tnsl | 1822 | ||||
| -rw-r--r-- | tnslc/parse/ast.tnsl | 36 | ||||
| -rw-r--r-- | tnslc/test.tnsl | 17 | ||||
| -rw-r--r-- | tnslc/tnslc.tnsl | 59 | ||||
| -rw-r--r-- | tnslc/utils/algo.tnsl | 32 | ||||
| -rw-r--r-- | tnslc/utils/vector.tnsl | 48 | ||||
| -rw-r--r-- | tnslc/vec_test.tnsl | 47 |
26 files changed, 6534 insertions, 840 deletions
@@ -6,332 +6,467 @@ Normal TNSL project structure has a root source folder with TNSL files contained within the folder and sub-folders. It is normal for the root folder to represent a single library or binary, although there is no strict rule enforcing this. Standard organization is to place sub-modules in sub-folders. The file name for the module's entry point should match the folder name. -The file representing the compile target is known as the root file, which generally resides in the root source folder. This file will contain a main method, and/or the pre-processor statement `rootfile` to denote the root of a library. If both are present, the compiler will generate an executable by default. +The file representing the compile target is known as the root file, which generally resides in the root source folder. If the program is built as an executible, it requires a function with the name `main` as the entrypoint to the program. ### TNSL Files TNSL files end with the `.tnsl` extension and may contain the following: -- Comments -- Pre-processor statements - Modules - Constant and variable definitions -- Named function blocks +- Enum declarations - Struct definitions +- Named function blocks - Method and interface blocks +- Import statements +- Asm statements -The following may only occur within named function or method blocks: +There are other language constructs which may only be used within functions: - Re-assignment of variables - Control flow blocks -- Function calls +- Value statements - Anonymous blocks (Scope blocks) +- Stream semantics -## Section 2 - Blocks - -### TNSL Block Notation - -Blocks in tnsl consist of a slash `/` and a character denoting the type of block. The reverse of these symbols end the block. The three types of blocks are comment, pre-processor, and code. Code blocks can be further broken down into modules, functions, control flow, methods, and interfaces. +Comments may appear anywhere in the file - Examples of standard block opening and closing characters: +### Comments - /# - open comment - #/ - close comment +Comments begin with `#` and end with a new line. Comment blocks start with `/#` and end with `#/`. - /: - open pre-processor - :/ - close pre-processor - - /; - open code - ;/ - close code - -In addition to the standard opening and closing characters, there exist "swivel" character sets to quickly close and open a block type +## Section 2 - Blocks - ;; - close code, then open code - #; - close comment, open code - ;# - close code, open comment - :: - close pre-processor, open preprocessor - #: - close comment, open pre-processor - :# - close pre-processor, open comment +Blocks in TNSL open with `/;` and close with `;/`. Keywords directly after the opening (and on the same line) affect the type of block created. +A quicker syntax for closing and re-opening a new block is to use `;;` which is equivalent to `;//;`; this can be helpful with series of `else if` blocks and `case` blocks. ### Modules Modules are akin to namespaces in C++ -They hold a group of related modules, functions, structs, and variables. These named definitions may be used by other projects if the `export` keyword is used in conjunction with the `module` keywor; otherwise, the names are not exported into the file's symbol table. +They hold a group of related sub-modules, functions, structs, and variables. +These named definitions may be used by other projects if the `export` keyword is used before the `module` keyword. +Otherwise, the names are not exported into the program/library's symbol table. ### Module definition example: *File a.tnsl (project a)* - - /; export module pubmod - /; module hidden - # Can access all from pubmod, and pubmod.hidden - ;/ - # Can access all from pubmod, and pubmod.hidden - ;/ +``` +/; export module pubmod + /; module hidden + # Can access all from pubmod, and pubmod.hidden + ;/ # Can access all from pubmod, and pubmod.hidden +;/ +# Can access all from pubmod, and pubmod.hidden +``` *File aa.tnsl (project a)* - - /; my_function_a - # Can access all from pubmod, and pubmod.hidden - ;/ +``` +/; my_function_a # Can access all from pubmod, and pubmod.hidden +;/ +# Can access all from pubmod, and pubmod.hidden +``` *File b.tnsl (project b)* - - /; my_function - # Can access all from pubmod, but not pubmod.hidden - ;/ - # Can access all from pubmod, but not pubmod.hidden +``` +/; my_function + # Can import all from pubmod, but not pubmod.hidden +;/ +# Can import all from pubmod, but not pubmod.hidden +``` ### Functions -TNSL functions are code blocks whose definition contains none of the following: control flow keywords, the module keyword, the method keyword. TNSL functions are called methods if they are contained within a method block. TNSL methods may only be called with relation to the user defined type they are linked to. If a TNSL function has no user defined name, it is anonymous. Anonymous functions can be stored as void type variables or called immediately. If an anonymous function is not stored, it is interpreted as inline and called immediately (this is known as a scope block). +Functions are blocks followed by a user defined name (not a keyword). +Functions may have inputs and/or outputs. +Inputs are enclosed by `()` and outputs are enclosed by `[]` -Function blocks may have inputs and/or outputs. Inputs are enclosed by `()` and outputs are enclosed by `[]` - -Input lists must begin with a type and conclude with at least one named parameter. If no type is given after a comma, it is assumed that the previous type carries over. +Input lists may begin with a type or be empty. +If they begin with a type they must conclude with at least one named parameter. +Parameters are separated by commas and use the previous type unless a new one is specified. Output lists consist of a comma seperated list of types. -Either of these may be omitted for no input and/or output. +Functions *may* be overloaded (that is, two functions may share names but have differing input type lists). +Overloaded functions *must not* share the same inputs and differing outputs, but *may* have both differing inputs and differing outputs. +Symbols can be defined in a separate build file or auto-generated by the compiler. +There is a standard for how the compiler will auto-generate names; this can be found in another chapter. -TNSL functions may have their call stack modified by the `raw` and/or `inline` keywords. If the `inline` keyword is placed before the function declaration, the function will still be exported (if it is being exported), but any time it is used in the project's code, it will be optimized as if in-line. +### Function definition example: -The use of the `raw` keyword has several effects: the function will have no generated assembly preamble, the function will allow `raw return` statements, the function will not be optimized, and the function will allow `asm` statements. Any function may be labeled `raw`, even `main` and anonymous functions. +*simple function with no inputs or outputs named "my_function"* +``` +/; my_function + tnsl.print("Hello from my_function!") +;/ +``` -Functions *may* be overloaded (that is, two functions may share names but have differing input type lists). Overloaded functions *may not* share the same inputs and differing outputs, but *may* have both differing inputs and differing outputs. +*functions with inputs and/or outputs* +``` +/; my_second_function (int input1, bool input2) [bool, int] + return input2, input1 +;/ -Examples: +/; sum_lists ({}int a, b) [int] + int sum = 0 + + /; loop (int i = 0; i < len a) [++i] + sum += a{i} + ;/ - # simple function with no inputs or outputs named "my_function" - /; my_function - <statements> - ;/ + /; loop (int i = 0; i < len b) [++i] + sum += b{i} + ;/ +;/ - # function with inputs and outputs - /; my_second_function ( <type> input1, <type (optional)> input2 ) [ <type 1>, <type 2>, ... , <type n> ] - <statements> - ;/ +int global_lol = 0 - # funtion with a scope block - /; my_third_function - <statements> - # a scope block - /; - <statements> - ;/ - ;/ +/; next [int] + return ++global_lol +;/ + +/; set_global (int i) + global_lol = i +;/ +``` ### Control Flow Blocks -Control flow blocks are code blocks whose definitions contain the keywords if, else, loop, match, case, or default. +Control flow blocks begin with the keywords `if`, `else`, `loop`, `match`, `case`, or `default`. -For if, else, loop, and match any inputs and/or outputs are a semicolon-separated list of statements. For case or default, only inputs are accepted in the form of a single value. Any variables defined in these inputs or outputs are scoped to the block only. Control flow blocks may not actually output any values; instead, any statements in the output are evaluated when the block ends, weather it loops or not. +Control flow blocks have a series of lists, generally these can be thought of as 'beginnings' encased in "()" and 'endings' encased in "[]". +What 'beginning' and 'ending' mean varries by the type of block and is explained below. -Examples: +### `if` Blocks - # simple if block - /; if ( <conditional> ) - <statements> - ;/ +`if` blocks generally work as you would expect in other procedural languages. +They can be followed by any number of `else if` blocks as well as a final `else` block. - # if block with else and else if - /; if ( <statements (optional)> ; ... ; <conditional> ) - <statements> - ;; else if ( <statements (optional)> ; ... ; <conditional> ) - <statements> - ;; else - <statements> - ;/ +The 'beginning' "()" of an `if` or `else if` block is a series of statements separated by `;`. +These are executed in order. The last of these must evaluate to a boolean (type `bool` with value `true` or `false`). +The code within the if block is executed if the boolean evaluates to `true` and does not execute if the boolean evaluates to `false`. - # loop block - /; loop ( <statements (optional)> ; ... ; <conditional (optional)> ) - [ <statements (optional)> ; ... ; <conditional (optional)> ] +If any block in the series has a condition which evaluates to true then it is executed and the others are skipped. +If none evaluate to true then the `else` block will execute if present. - <statements> - ;/ +The 'ending' "[]" of an `if` block is currently reserved and has undefined behavior. - # match block - /; match ( <statement (optional)> ; ... ; <input value> ) +*Examples:* +``` +/; if (true) + tnsl.print("this always prints") +;/ - /; case <match value> - <statements> - ;; case <match value> - <statements> - # Continue here would fall through to default - ;; default - <statements> - ;/ - ;/ +/; if (false) + tnsl.print("this never prints") +;; else if (true) + tnsl.print("this one will now print") +;; else + tnsl.print("this never prints either") +;/ + +/; if (0 !== 0) + tnsl.print("you have to use boolean values") +;; else if (1 < 0) + tnsl.print("standard equality operators work, see appendix for a list.") +;; else if (int i = 0; i < 2 && 5 > i) + tnsl.print("Statements!") +;/ -### Loops +/; if (false) + tnsl.print("this never prints") +;; else if (false) + tnsl.print("this never prints either") +;; else + tnsl.print("this one will now print") +;/ +``` + +### `loop` Blocks The `loop` block can be configured (based on weather or not each boolean statement is omitted) to act as any type of loop. -The *first* conditional is the **initial run condition**. It decides weather the loop is entered at all. If omitted, defaults to `true`, creating a `do ... while` type block. +The 'beginning' "()" of a `loop` is similar to an `if` in that it is a series of statements; however in the case of a `loop` the conditional is optional and defaults to `true` if omitted. +If the conditional evaluates to `true` then the inner code is evaluated. -The *second* conditional is the **subsequent run condition**. It decides weather the loop continues to loop or not. If omitted, it *mirrors* the **initial run condition** (this is equivalent to a `for` or `while` block). +The 'ending' "[]" of a `loop` is similar to the 'beginning' of the loop as it a series of statements with an optional conditional at the end. +If the conditional is omitted here it defaults to the same as the conditional from the 'beginning'. +Each of these statements are evaluated at the end of the loop and if the conditional evaluates to `true` then the loop repeats its execution from just after the 'beginning'. -Examples: - - # Same as a do ... while block - /; loop [ <conditional> ] - <statements> - ;/ +*Examples* +``` +# Same as a do ... while block +/; loop [ <conditional> ] + <statements> +;/ - # Same as a while loop - /; loop ( <conditional> ) - <statements> - ;/ +# Same as a while loop +/; loop ( <conditional> ) + <statements> +;/ - # Infinite loop - /; loop - <statements> - ;/ +# Infinite loop +/; loop + <statements> +;/ - # Adding statements to mimic a for loop - /; loop (int i = 0; i < 10) [i++] - <statements> - ;/ +# Adding statements to mimic a for loop +# Since i++ is not a bool it does not count +# as the conditional +/; loop (int i = 0; i < 10) [i++] + <statements> +;/ +``` -## Section 3 - Statements +### `match` Blocks -### TNSL Statement Notation +`TODO` -There are three types of tnsl statements: code, pre-processor, and comment. Code statements begin with `;` and end at the next statement. Pre-processor statements begin with `:` and end at the next statement. Comment statements (line comments) begin with `#` and end at the next new line. After a line comment ends, the previous statement resumes. +#### `case` Block -### Variable Declaration +`TODO` -Declaring a variable is done by referencing a type and then giving a list of names for the new variables. Optionally, a list of values may be given to initialize the new variables. +#### `default` Block -Variables may be augmented by the following keywords: `const`, `volatile`, and/or `static`. +`TODO` -Declaring a variable as `const` means that it is a constant and must be immediately initialized. A constant may not be re-assigned a value. +## Section 3 - Types -Declaring a variable as `volatile` means that the compiler will not attempt to optimize operations performed on it. +An exhaustive list of built-in and special types can be found in Appendix B. -Declaring a variable `static` means that the value will be kept between function calls. Static variables may create race conditions when paired with threads. +### Standard Types in `tnsl` -Examples: +The standard set of types will be familiar to programmers with experience in procedural languages. +Some common types are: - # list with initialization - ;int a, b = 0, 1 - - # single without initialization - ;int c - - # list with partial initialization - ;int d, e = 0 # d is defined, but e is not +- Signed integer variants (positive or negative): `int`, `int8`, `int16`, `int32`, `int64` +- Unsigned integer variants (positive only): `uint`, `uint8`, `uint16`, `uint32`, `uint64` +- Floating point variants: `float`, `float32`, `float64` +- Boolean (`true` or `false`): `bool` +- -## Section 4 - Types +TNSL restricts valid platforms to those with byte addressable memory and whose processors support at least 16-bit integers. -An exhaustive list of built-in types can be found in Appendix B. +TNSL basic types with unspecified length (`int`,`uint`, and `float`) default to the largest supported in standard registers (non-SIMD or vector operations). -### The `void` Type +I.E. for x86_32 `int` defaults to `int32`, on x86_64 `int` defaults to `int64` -The `void` type represents one of two things: an unknown data type, or a function. When the void type is paired with input and output parameters `void( <types> )[ <types> ]` it represents a function. When it is not paired with these things it represents an unknown data type. +### `libtnsl` Types -Pure `void` types can not be created. Function types are *always* references to the function, more like pointers than data holders. Otherwise, void types must be created as pure pointers. In this case, they are in a sense "un-typed" pointers which do not know the length of the object they are pointing at, only the address. +The following are well supported but rely on libtnsl: -Examples: +- The meta-type: `type` +- The vector (SIMD) type: `vect` - # simple function - /; func_1 - ;/ +They are discussed in more detail in the advanced features section of the specification. - # void example func - /; func_2 +### Pointers - # create a void type and assign it func_1's value - ;void()[] func_ref = func_1 - - # call func_1 using func_ref - ;func_ref() - ;/ +Pointer types are prefixed with the `~` (pointer to) operator. +This operator serves as both part of the type, and as a way to get a pointer from a variable. + +The de-reference operator `` ` `` is used as a postfix to pointer variables when getting or setting the underlying value. + +*Examples* +``` +# define int i as 0 +int i = 0 + +# pointer to int i +~int p = ~i + +# set the value of i using p (i is set to 1) +p` = 1 + +/; if (i == 1) + tnsl.print("That's pointers!") +;/ +``` + +### References + +Reference types are typically for use in function parameters but can be defined anywhere. Their type signature ends in the de-reference operator `` ` ``. + +A few quirks of reference types: + +- Reference types are similar to pointers and must be initialized with a pointer to be useful. +- When accessed or set in a normal statement they automatically de-reference the pointer they hold. +- When set in definition or function call they expect a pointer to the underlying variable they will access. +- The pointer which the reference variable uses can be set by prefixing with `~` + +*Examples* +``` +# this will be our underlying integer +int a = 0 + +# basic definition and immediate assignment of a reference type +# (immediate assignment is special as it allows setting the +# pointer of the reference without use of the ~ operator) +int` r = ~a + +# sets or gets in normal statements will use the underlying 'a' + +# a becomes 1 +r = 1 + +# b is defined and set to 1 +int b = r + +# an example of declaration without immediate assignment +int` s + +# setting what 's' points to requires use of the ~ operator +~s = ~a -More examples of pointer voids are available in the pointers section of this document. +# a becomes 2 +s++ -### Arrays +# a function with a reference parameter +/; add_one (int` i) + i++ +;/ -In memory, arrays store their length as a uint, then immediately follow with the contents of the array. This way, all arrays can be checked for length with the `len` operator. +# you must explicitly call using a pointer to the variable being referenced +add_one(~a) -Arrays are created by prefixing a type with `{ <# of elements> }` or simply `{}` so long as the array is immediately initialized. One can similarly access an element of an array by suffixing the variable name with `{ <value of element to return> }`. +/; if (a == 3) + tnsl.print("a is now three") +;/ +``` + +### Fixed-length Arrays + +Arrays are a repeated sequence of the same type of data in memory. + +Arrays store their length as a `uint` and immediately follow with the contents of the array. All arrays can be checked for length with the `len` operator. + +Arrays are created by prefixing a type with `{ <# of elements> }`. +One can similarly access an element of an array by suffixing the variable name with `{ <offset of element to return> }`. When initializing or assigning a new value to an entire array, use `{}` to encase a list of values. The length of the array can be gotten by `len <variable name>` -Examples: +*Examples* +``` +# create an array of five integers +{5}int i - # create an array of five integers - ;{5}int i +# assign values to the array +i{0} = 0 +i{1} = 2 +i{2} = 0 +i{3} = 2 +i{4} = 1 - # assign values to the array - ;i{0} = 0 - ;i{1} = 2 - ;i{2} = 0 - ;i{3} = 2 - ;i{4} = 1 +# store the length of the array (5) +uint array_length = len i - # store the length of the array - ;uint array_length = len i +# create an initialized array with length five +{5}int j = {1, 2, 3, 4, 5} - # create an initialized array with length five - ;{}int j = {1, 2, 3, 4, 5} +# loop through the array and add them. +/; loop (int k = 0; k < array_length) [k++] + i{k} += j{k} +;/ +``` - # loop through the array and add them. - /; loop (int k = 0; k < array_length) [k++] +### Unknown-length Arrays - ;i{k} += j{k} - ;/ +When creating an array where the length is not known at compile time (or accepting an array with unknown length as a parameter) use the `{}` prefix. +***When would I use this?*** +- When defining an array within a function body or module the compiler will optimize however it thinks is best and is functionally equivalent to a fixed length array. +- The difference matters more when **defining functions** or **defining structs** since in this case `{}` ***always*** denotes a ***pointer*** to an array. + - This can be useful when you want to accept arbitrarily long lists or have a recursive struct which has an array of itself as a member. -### Pointers +*Examples* +``` +# when defining an array +{}int i = {1, 2, 3, 4} +{}int j = {5, 6, 7, 8, 9, 10} -Pointer types are created using the `~` (pointer to) operator. This operator serves as both part of the type, and as a way to get a pointer from a type. To de-reference a pointer into it's base type, use the `` ` `` (de-reference) operator. +# when defining a recursive struct +struct Node { + int i, + # using a fixed-length here would result in a + # compile time error because the size could + # not be computed + {}Node sub +} -Passing by reference can be done by prefixing the type with the de-reference operator. This type is only available in function parameter declaration. To call a function with this type, a pointer to the desired variable must be given. +# when defining a function +/; sum({}int arr) [int] + int out = 0 + /; loop (int i = 0; i < len arr) [++i] + out += arr{i} + ;/ +;/ -Examples: +# sum can take any array of integers +int a = sum(i), b = sum(j) +``` - # define int - ;int i +### NOTE: Evaluation Order - # pointer to i - ;~int p = ~i +Order of evaluation of type prefixes and postfixes is first all prefixes in right to left order then all postfixes in left to right order. +This can be overridden using parenthesis. - # set i using p - ;`p = 1 +*Convoluted Examples* +``` +# a reference to a pointer which points to an int +~int` +(~int)` - # a function taking a pass by reference - /; add_two (`int i) - i += 2 - ;/ +# a reference to a reference to an array (unknown length) which holds pointers to floats +{}~float`` +({}(~(float))`` - # calling add_two in two different ways - ;add_two(p) - ;add_two(~i) +``` - # i is now 5 +### The `void` Type + +The `void` type can represent two different things: unknown memory or a function. +When `void` is prefixed with `~` it represents a pointer to arbitrary memory (byte aligned). +When the void type is paired with input and output parameters `void( <type list> )[ <type list> ]` it represents a function. This is considered part of the type and is not a postifx. + +*Examples* +``` +# simple function +/; func_1 + tnsl.print("hello!") +;/ + +# void example func +/; func_2 + + # create a void type and assign it func_1's value + void()[] func_ref = func_1 + + # call func_1 using func_ref + func_ref() +;/ +``` ### Casting Types -Casting between types uses the standard input and output enclosing `()` and `[]` in conjunction. Place a value in the input enclosing characters and a type to output in the output enclosing characters to cast one type to another (`( <value> )[ <type> ]`). +Cast by enclosing a value in `()` and following with the type to cast to enclosed in `[]`. -Examples: +*Examples* +``` +# define an int and a float +int i = 10 +float f = 11.5 - # define an int and a float - ;int i = 10 - ;float f = 11.5 +# define a void pointer and set it to reference i +~void v = ~i - # define a void pointer and set it to reference i - ;~void v = ~i - - # define an int pointer and cast the void pointer to initialize it - ;~int p = (v)[~int] +# define an int pointer and cast the void pointer to initialize it +~int p = (v)[~int] - # cast the float to an int and set the value of i - ;`p = (f)[int] +# cast the float to an int and set the value of i +p` = (f)[int] +``` ### Defining Types @@ -363,7 +498,7 @@ Examples: ;/ ;/ -### Interfaces +### Interface Types Interfaces are defined using the `interface` keyword. Interfaces have methods but no struct or members to accompany them. Instances of interfaces may not be created. Methods defined by interfaces must be overridden unless marked in the interface. Such marked methods may call on other methods, but may not use any members as interfaces have none. @@ -394,7 +529,7 @@ Example: ;/ ;/ -### Enums +### Enum Types Enums are defined using the `enum` keyword. An enum represents a set of possible states, and requires a single output type which can be compared. @@ -421,6 +556,10 @@ Examples: ROCK } +## Section 4 - Statements + +`TODO` + ## Section 5 - Operators An exhaustive list of operators can be found in Appendix A @@ -429,114 +568,101 @@ An exhaustive list of operators can be found in Appendix A Operator precedence is as follows (from greatest to least): - Pointer operators (p0): - - ~ - address of - - ` - de-reference - - - Access operator (p1): +``` +Pointer operators (p0): - . - get/access +~ - address of +` - de-reference - Increment/de-increment (p2): - ++ - increment +Access operator (p1): - -- - de-increment +. - get/access - Multiplication/division (p3): +Increment/de-increment (p2): - * - multiply +++ - increment - / - divide +-- - de-increment - Addition and subtraction (p4): +Multiplication/division (p3): - + - addition +* - multiply - - - subtraction +/ - divide - Modulus (p5): +Addition and subtraction (p4): - % - modulus ++ - addition +- - subtraction - Bitwise operators (p6): - & - and +Modulus (p5): - | - nor +% - modulus - ^ - xor - << - shift left - - >> - shift right +Bitwise operators (p6): - !& - nand +& - and - !| - nor +| - nor - !^ - xand +^ - xor - ! - not (bitwise or boolean) +<< - shift left +>> - shift right - Boolean operators (p7): +!& - nand - && - boolean and +!| - nor - || - boolean or +!^ - xand - == - boolean eq +! - not (bitwise or boolean) - > - greater than - < - less than - - !&& - boolean nand +Boolean operators (p7): - !|| - boolean nor +&& - boolean and - !== - boolean neq +|| - boolean or - !> - boolean not greater than +== - boolean eq - !< - boolean not less than +> - greater than - >== - boolean greater than or equal to +< - less than - <== - boolean less than or equal to +!&& - boolean nand -## Section 6 - `raw` and `asm` +!|| - boolean nor -### The `raw` Keyword +!== - boolean neq -The `raw` keyword can be used in four different scenarios, and each has a different meaning. +!> - boolean not greater than -1. The `raw` keyword can be used in function definitions. These effects were discussed in section 2.2. +!< - boolean not less than -2. The `raw` keyword may be used in conjunction with the `return` keyword, but only inside of raw functions. This causes an instant return regardless of stack or register state. It is recommended to clean up function and provide return types before using this. +>== - boolean greater than or equal to -3. The `raw` keyword may be used with the `struct` keyword to create a raw struct. Raw structs can not contain user defined types or generics. Raw types encode no type information and may not be extended. Raw structs, unlike static or dynamic structs, are only as wide their members. - - Static and dynamic structs contain a small amount of information pertaining to their actual type and generics so may be larger than only their members. - - In addition, since static and dynamic structs may be extended, they may not be the initially defined type and may be larger, further complicating matters. +<== - boolean less than or equal to +``` -4. The `raw` keyword may be used with the `enum` keyword to create a raw enum. Raw enums only have at most one state per bit and may bitwise or and bitwise and to generate a full state. Raw enums are much akin to bit-masks. +## Section 6 - `asm` -### The `asm` Keyword +`TODO` -The `asm` keyword may be used in `raw` functions or blocks to produce raw asm code for the assembler. Any valid assembly code may be used, and certain extensions are available such as variable pointer references. +## Section 7 - Crosscalling to C -Syntax: +`TODO` - ;asm "<valid line of assembly code>" ## License @@ -1,101 +0,0 @@ -# Related Features - -## Section 1 - Style Guide - -This style guide is primarially for anyone working on tnsl-lang and to a baseline good practice. However, different programmers and groups will feel differently, the real recommendation is to keep your project consistant. Pick and choose what you need such that it fits your needs and everyone is on board, just keep it consistant. - -This section will probably feel less formal than the others simply due to how subjective things are. - -### Tabs or Spaces - -You will for the most part see tabs being used over spaces in tnsl-lang. This is not to keep file sizes down, but instead so that anyone can tweak how the files look to fit their asthetic best without affecting anyone else. Adjust tab sizing on your machine and it won't change it on mine. - -### Naming Convention - -NOTE: Remember that naming convention is no substitute for readability. If your names don't make sense, neither does your code. x, y, and z may be fine, but that doesn't help anyone else figure out what you're using them for. If a particular implementation *does* use variables that are hard to name, feel free to use letter identifiers, but add some short comments to explain what you're doing and how (see comments for more info). - -- UPPER_SNAKE_CASE for constants and enum values - -- lowerCamelCase for functions, and methods - -- UpperCamelCase or flatcase for enums, types, and interfaces - - it is recommended that interfaces start with the letter i - -- lower_snake_case (hungarian notation acceptable) for type/struct members, variables - -- flatcase for modules - -## Comments - -Comments should strive to tell programmers what a function does or what a struct's member is for rather than how it goes about doing/generating said thing. Particularly obtuse implimentations are free to use comments to explain their code. - -Comment blocks starting with an extra number sign `/##` are documentation comments. If they are at the top of the file, they provide either info about the file or license information. If they appear directly before (or joined to using `#;`) a function or method they explain what the function or method does. - -## Directory Structure - -`src` will be the name of the root source files/folders, `build` will be the name of the outputed files/folders. - -The name of the *main* root file (there may be multiple root files) will be the project folder name followed by .tnsl (example: project tnslc's root file is `src/tnslc.tnsl`). - -Sub-modules shall be contained in their own folder, and the main entry point to the module will be named the same as that folder followed by .tnsl (example: module `tnsl.alg` in libtnsl has the main entry point for the module `/src/alg/alg.tnsl`). - -## Section 2 - Compiler Options - -An exhaustive list of all compiler options can be found in the spec for tnslc, but these are a general sub-set for working on lower-level projects and language bootstrapping. - -### ISA Options - -The tnslc compiler can output for a variety of backends. These can be set with the `--arch <desired isa>` option. - -### Pre-processor - -tnslc can preemptively set pre-processor variables using the `--V "VARIABLE=value, VARIABLE2=value2, ..."` option. - -### Standard Library - -Access to the standard library can be disabled (bare metal mode) by using the `--no-libtnsl` flag. Also use this to build the standard library as it will attempt to link improperly otherwise. - -### T-LETs - -What T-LETs exactly are is discussed later. You can have tnslc produce them by passing the `--otype tlet` flag. - -## Section 3 - The Pre-Processor - -An exhaustive account of the full pre-processor can be found in the tnslc specification. - -### Including Other Files - -Use the `include` directive to include other code. This can be a library using single quotation marks (example: `:include 'tnslc'`), another file using a path with double quotes (example: `:include "c.tnsl"` reads as `:include "./c.tnsl"`), or a module in a subfolder using expanded syntax (`:include "some_module"` reads as `:include "./some_module/some_module.tnsl"`). - -No header guard is needed, tnslc can pick up on if a file has already been included in the project. - -Cyclic dependency for files is fine. Cyclic dependency for structs is not. - -## Section 4 - libtnsl - -An exhaustive list of all features included in the standard libtnsl is provided in the libtnsl spec. This is a short list of minimum functions a libtnsl must provide to make full use of the tnsl programming language. - -### Method and type resolution for non-raw types - -**TBD - this sub-section is under construction** - -The type `tnsl.reflect.type` must exist and must be a raw struct. This is for storing type information. - -The module `tnsl.reflect` must provide the following functions/methods: - -- `type._get_member_ [tnsl.reflect.library]` -- `tnsl.reflect._is (type check, base) [bool]` - -### Vector types - -The type `tnsl.vector.vector` must exist. This is the generic simd/vector type and is referenced by `vect`. - -## Section 5 - TNSL-Lang Export Trees (T-LETs) - -TNSL doesn't use header files, and downloading full source code for every library being referenced would be a pain. This is where T-LETs come in. T-LETs contain a tree of all exported modules, functions, types, methods, and doc comments from a project. They are more compact and are simply named `<project name>.tlet`. - -## License - - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. @@ -1,4 +1,50 @@ -# The TNSL Calling ABI +# Features in Position + +To use higher level features in TNSL, an implimentation of libtnsl must be in place with a minimum set of features. This document outlines what concepts are and are not considered "higher level". This document also covers what the minimum set of features are for a given implimentation of libtnsl. Finally, this document covers how to call between TNSL and other programming languages. + +## Section 1 - Bare Metal + +## Section 2 - libtnsl and Types + +`libtnsl` has the authority to define how types are stored in memory (to some extent) and how method resolution works on static and dynamic structs. `libtnsl` *must* provide certain methods for some language features to be available. These functions, and their uses are now listed. A comprehensive list of language keywords can be found in Appendix B. + +### 2.0 - nullptr + +`tnsl._null` *must* be defined as a universal null pointer value. **No type except pointers may be `null`**, but pointers *must* have a null value to distinguish if they point to a meaningful position in memory. + +### 2.1 - The libtnsl Type Extension + +`tnsl.reflect._type` *must* be defined as a raw struct which contains minimum information for type identification. This information is stored with every static or dynamic struct so that programs can reflect type information. + +`tnsl.reflect._member` *should* be defined as a raw struct which contains minimum information for member variable identification within a type. The information need not be stored with every member variable, but should be stored somewhere such that the `tnsl.reflect._get` method can make use of it. + +`tnsl.reflect._method` *must* be defined as a raw struct which contains minimum information for method identification within a type. The information need not be stored with every member function, but should be stored somewhere such that the `tnsl.reflect._call` method can make use of it. + +`tnsl.reflect._is` *must* be defined as a function taking two parameters of type `tnsl.reflect._type` and returning a single `bool` as output. This function is called when the `is` operator is envoked. + +`tnsl.reflect._get` *should* be defined as a function taking three parameters. The first parameter will be of type `tnsl.reflect._type`, the second parameter will be of type `~void`, and the third parameter will be of type `tnsl.reflect._member`. The function will return a single `~void` as output. + +In the parameters, the first relates to the type of the struct given, or at least how the callee views the struct. The second is a pointer to the struct itself. The third is the requested member to get. The `~void` returned must point to the requested value. + +`tnsl.reflect._call` *must* be defined as a function taking four parameters. The first parameter will be of type `tnsl.reflect._type`, the second parameter will be of type `~void`, the third parameter will be of type `tnsl.reflect._method`, and the fourth parameter will be of type `~void`. The function will return a single `~void` as output. + +In the parameters, the first relates to the type of the struct given, or at least how the callee views the struct. The second is a pointer to the struct itself. The third is the requested method to call. The fourth is a pointer to the parameters for the method. The `~void` returned must point to the return value of the method. + +### 2.2 - Memory allocation and de-allocation + +`tnsl.algo._alloc` *must* be defined as a function taking a single parameter of type `uint` as the number of bytes to allocate and returning a single `~void` as the pointer to the allocated memory. The memory must be allocated from the heap. + +`tnsl.algo._salloc` *should* be defined as a function taking a single parameter of type `uint` as the number of bytes to allocate and returning a single `~void` as the pointer to the allocated memory. The memory must be allocated from the stack. + +`tnsl.algo._dealloc` *must* be defined as a function taking a single parameter of type `~void` as the pointer to a chunk of allocated memory. The function should deallocate the memory. + +### 2.3 - Expanded Types Extension + +`tnsl.box._vect` *must* be a raw struct which vector or simd instructions can be preformed on. + +`tnsl.box._string` *must* be a raw struct which stores a string of text. This text may be ASCII or Unicode, and should be stored as `uint8` values internally. + +## Section 3 - Cross calling C ## License @@ -1,50 +1,4 @@ -# Features in Position - -To use higher level features in TNSL, an implimentation of libtnsl must be in place with a minimum set of features. This document outlines what concepts are and are not considered "higher level". This document also covers what the minimum set of features are for a given implimentation of libtnsl. Finally, this document covers how to call between TNSL and other programming languages. - -## Section 1 - Bare Metal - -## Section 2 - libtnsl and Types - -`libtnsl` has the authority to define how types are stored in memory (to some extent) and how method resolution works on static and dynamic structs. `libtnsl` *must* provide certain methods for some language features to be available. These functions, and their uses are now listed. A comprehensive list of language keywords can be found in Appendix B. - -### 2.0 - nullptr - -`tnsl._null` *must* be defined as a universal null pointer value. **No type except pointers may be `null`**, but pointers *must* have a null value to distinguish if they point to a meaningful position in memory. - -### 2.1 - The libtnsl Type Extension - -`tnsl.reflect._type` *must* be defined as a raw struct which contains minimum information for type identification. This information is stored with every static or dynamic struct so that programs can reflect type information. - -`tnsl.reflect._member` *should* be defined as a raw struct which contains minimum information for member variable identification within a type. The information need not be stored with every member variable, but should be stored somewhere such that the `tnsl.reflect._get` method can make use of it. - -`tnsl.reflect._method` *must* be defined as a raw struct which contains minimum information for method identification within a type. The information need not be stored with every member function, but should be stored somewhere such that the `tnsl.reflect._call` method can make use of it. - -`tnsl.reflect._is` *must* be defined as a function taking two parameters of type `tnsl.reflect._type` and returning a single `bool` as output. This function is called when the `is` operator is envoked. - -`tnsl.reflect._get` *should* be defined as a function taking three parameters. The first parameter will be of type `tnsl.reflect._type`, the second parameter will be of type `~void`, and the third parameter will be of type `tnsl.reflect._member`. The function will return a single `~void` as output. - -In the parameters, the first relates to the type of the struct given, or at least how the callee views the struct. The second is a pointer to the struct itself. The third is the requested member to get. The `~void` returned must point to the requested value. - -`tnsl.reflect._call` *must* be defined as a function taking four parameters. The first parameter will be of type `tnsl.reflect._type`, the second parameter will be of type `~void`, the third parameter will be of type `tnsl.reflect._method`, and the fourth parameter will be of type `~void`. The function will return a single `~void` as output. - -In the parameters, the first relates to the type of the struct given, or at least how the callee views the struct. The second is a pointer to the struct itself. The third is the requested method to call. The fourth is a pointer to the parameters for the method. The `~void` returned must point to the return value of the method. - -### 2.2 - Memory allocation and de-allocation - -`tnsl.algo._alloc` *must* be defined as a function taking a single parameter of type `uint` as the number of bytes to allocate and returning a single `~void` as the pointer to the allocated memory. The memory must be allocated from the heap. - -`tnsl.algo._salloc` *should* be defined as a function taking a single parameter of type `uint` as the number of bytes to allocate and returning a single `~void` as the pointer to the allocated memory. The memory must be allocated from the stack. - -`tnsl.algo._dealloc` *must* be defined as a function taking a single parameter of type `~void` as the pointer to a chunk of allocated memory. The function should deallocate the memory. - -### 2.3 - Expanded Types Extension - -`tnsl.box._vect` *must* be a raw struct which vector or simd instructions can be preformed on. - -`tnsl.box._string` *must* be a raw struct which stores a string of text. This text may be ASCII or Unicode, and should be stored as `uint8` values internally. - -## Section 3 - Cross calling C +# The TNSL Calling ABI ## License diff --git a/spec/5.md b/spec/5.md new file mode 100644 index 0000000..1f5a7ac --- /dev/null +++ b/spec/5.md @@ -0,0 +1,101 @@ +# Related Features + +## Section 1 - Style Guide + +This style guide is primarially for anyone working on tnsl-lang and to a baseline good practice. However, different programmers and groups will feel differently, the real recommendation is to keep your project consistant. Pick and choose what you need such that it fits your needs and everyone is on board, just keep it consistant. + +This section will probably feel less formal than the others simply due to how subjective things are. + +### Tabs or Spaces + +You will for the most part see tabs being used over spaces in tnsl-lang. This is not to keep file sizes down, but instead so that anyone can tweak how the files look to fit their asthetic best without affecting anyone else. Adjust tab sizing on your machine and it won't change it on mine. + +### Naming Convention + +NOTE: Remember that naming convention is no substitute for readability. If your names don't make sense, neither does your code. x, y, and z may be fine, but that doesn't help anyone else figure out what you're using them for. If a particular implementation *does* use variables that are hard to name, feel free to use letter identifiers, but add some short comments to explain what you're doing and how (see comments for more info). + +- UPPER_SNAKE_CASE for constants and enum values + +- lowerCamelCase for functions, and methods + +- UpperCamelCase or flatcase for enums, types, and interfaces + - it is recommended that interfaces start with the letter i + +- lower_snake_case (hungarian notation acceptable) for type/struct members, variables + +- flatcase for modules + +## Comments + +Comments should strive to tell programmers what a function does or what a struct's member is for rather than how it goes about doing/generating said thing. Particularly obtuse implimentations are free to use comments to explain their code. + +Comment blocks starting with an extra number sign `/##` are documentation comments. If they are at the top of the file, they provide either info about the file or license information. If they appear directly before (or joined to using `#;`) a function or method they explain what the function or method does. + +## Directory Structure + +`src` will be the name of the root source files/folders, `build` will be the name of the outputed files/folders. + +The name of the *main* root file (there may be multiple root files) will be the project folder name followed by .tnsl (example: project tnslc's root file is `src/tnslc.tnsl`). + +Sub-modules shall be contained in their own folder, and the main entry point to the module will be named the same as that folder followed by .tnsl (example: module `tnsl.alg` in libtnsl has the main entry point for the module `/src/alg/alg.tnsl`). + +## Section 2 - Compiler Options + +An exhaustive list of all compiler options can be found in the spec for tnslc, but these are a general sub-set for working on lower-level projects and language bootstrapping. + +### ISA Options + +The tnslc compiler can output for a variety of backends. These can be set with the `--arch <desired isa>` option. + +### Pre-processor + +tnslc can preemptively set pre-processor variables using the `--V "VARIABLE=value, VARIABLE2=value2, ..."` option. + +### Standard Library + +Access to the standard library can be disabled (bare metal mode) by using the `--no-libtnsl` flag. Also use this to build the standard library as it will attempt to link improperly otherwise. + +### T-LETs + +What T-LETs exactly are is discussed later. You can have tnslc produce them by passing the `--otype tlet` flag. + +## Section 3 - The Pre-Processor + +An exhaustive account of the full pre-processor can be found in the tnslc specification. + +### Including Other Files + +Use the `include` directive to include other code. This can be a library using single quotation marks (example: `:include 'tnslc'`), another file using a path with double quotes (example: `:include "c.tnsl"` reads as `:include "./c.tnsl"`), or a module in a subfolder using expanded syntax (`:include "some_module"` reads as `:include "./some_module/some_module.tnsl"`). + +No header guard is needed, tnslc can pick up on if a file has already been included in the project. + +Cyclic dependency for files is fine. Cyclic dependency for structs is not. + +## Section 4 - libtnsl + +An exhaustive list of all features included in the standard libtnsl is provided in the libtnsl spec. This is a short list of minimum functions a libtnsl must provide to make full use of the tnsl programming language. + +### Method and type resolution for non-raw types + +**TBD - this sub-section is under construction** + +The type `tnsl.reflect.type` must exist and must be a raw struct. This is for storing type information. + +The module `tnsl.reflect` must provide the following functions/methods: + +- `type._get_member_ [tnsl.reflect.library]` +- `tnsl.reflect._is (type check, base) [bool]` + +### Vector types + +The type `tnsl.vector.vector` must exist. This is the generic simd/vector type and is referenced by `vect`. + +## Section 5 - TNSL-Lang Export Trees (T-LETs) + +TNSL doesn't use header files, and downloading full source code for every library being referenced would be a pain. This is where T-LETs come in. T-LETs contain a tree of all exported modules, functions, types, methods, and doc comments from a project. They are more compact and are simply named `<project name>.tlet`. + +## License + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. diff --git a/spec/Appendices.md b/spec/Appendices.md index 7904af5..924be70 100644 --- a/spec/Appendices.md +++ b/spec/Appendices.md @@ -23,8 +23,6 @@ ; - beginning of statement, end of previous statement - : - beginning of pre-processor statement, end of previous - # - line comment, ends at newline @@ -297,8 +295,41 @@ NOTE: Static structs *can* allow generics so long as they do not store said gen Variable width structs (dynamic structs) can accomodate generics and variable width members. Dynamic structs may extend static structs or other dynamic structs. By extending a dynamic struct, even if one makes no use of the dynamic members, their struct is automatically a dynamic struct. These structs offer the least control over memory, and slightly slower call times, but offer the most flexability to the programmer. +## Appendix D - When can I use...? + +### Core Language + +The core language encompases what you would expect from any C-like language: +- Modules +- Variables +- Enums +- Types +- Functions +- Methods +- Interfaces +- Scalar Operators +- Control Flow +- Anonymous Functions +- Inline Assembly +- Interop with C ABI + +### Advanced Language Features +These may depend on support from `libtnsl`, though they are still considered "standard" and any complete implementation of TNSL must include them. +The table below shows what parts of the standard library must be present for the features to work as designed: + +| Feature | libtnsl - `reflect` | libtnsl - `thread` | libtnsl - `stream` | +| --------------- | ------------------- | ------------------ | ------------------ | +| Runtime type reflection | Yes | - | - | +| Threads | - | Yes | - | +| Mutex | - | Yes | - | +| Coroutines | - | Yes | - | +| Generators | - | Yes | - | +| Loop over Generator | - | Yes | - | +| Stream | - | - | Yes | +| Stream Operators | - | - | Yes | +| Loop over Streams | - | Yes | Yes | -## Appendix D - UN7+1 +## Appendix Z - UN7+1 Unicode Non-standard 7+1 bit (UN7+1) encoding is a non-standard way to represent any unicode character using a series of 8-bit values. The first bit in the 8-bit sequence represents if the next 8-bit sequence is included in the character code, the other seven bits are part of the character code. @@ -317,4 +348,4 @@ Examples: This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/.
\ No newline at end of file + file, You can obtain one at http://mozilla.org/MPL/2.0/. diff --git a/spec/README.md b/spec/README.md index 67f7e39..2a3f652 100644 --- a/spec/README.md +++ b/spec/README.md @@ -1,47 +1,54 @@ -Version 0.0.1 -# The TNSL Language Specification +# The TNSL Specification +Version 0.0.2 ## Forward -I forgot what TNSL stands for. I now worry that it's one of those "ATM Machine" situations. +This is mostly meant to be a collection of valid TNSL syntax that interested parties can use to learn the language by example. Explanations are provided alongside. In-depth explanations may be provided for features which use a bit of "compiler magic". One goal of the language is to keep "compiler magic" to a minimum while still providing quality of life constructs for programmers to use, so these should be few. + +Also, I forgot what TNSL stands for. I now worry that it's one of those "ATM Machine" situations. -CircleShift ## Chapter Index -1. [The Language](./1.md) - +1. [The Core Language](./1.md) - Files - Blocks - - Statements - Types + - Basic Statements - Operators - - `raw` and `asm` + - `asm` + - Cross Calling to C -2. [Related features](./2.md) - - - Style guide - - Compiler Options - - The Pre-Processor - - libtnsl - - TNSL-lang export trees (T-LETs) +2. [Advanced Features](./2.md) + - Relation to `libtnsl` + - Generators and Coroutines + - Streams + - Anonymous Functions + - Interfaces and Runtime Type Reflection -3. [The TNSL Calling ABI](./3.md) +3. [Tweaking Features](./3.md) + - Bare Metal + - `libtnsl` as it relates to Types +4. [The TNSL Calling ABI](./4.md) - Differences from C - - Exporting C-like Functions + - Exporting and importing C-like Functions - Types and Arrays in Memory -4. [Features in Position](./4.md) - - - Bare Metal - - `libtnsl` as it relates to Types - - Cross Calling to C +5. [Related Reading](./5.md) + - Style guide + - Compiler Options + - The Pre-Processor + - libtnsl + - TNSL export trees (TETs) - [Appendix](./Appendices.md) - A: Reserved Characters - B: Reserved Words - - C: Speed vs the Type System + - C: Speed of Advanced Features + - D: When can I use [Feature]? + - Z: UN7+1 (A unicode nonstandard format) ## License diff --git a/tnslc/README.md b/tnslc/README.md index 9a69f6b..88607eb 100644 --- a/tnslc/README.md +++ b/tnslc/README.md @@ -4,12 +4,12 @@ The reference compiler for the TNSL programming language. The compiler is writt ## Usage: -Place the [bootstrap compiler](https://git.cshift.net/CircleShift/ctc) `ctc` in this folder and execute `build.sh` +Place the [bootstrap compiler](https://git.cshift.net/CircleShift/ctc) `ctc` in this folder and execute `build.sh tnslc` The compiler outputs x86 NASM compatible assembly. Examples: - `./ctc dummy.tnsl dummy.asm` - Run the bootstrap compiler on the dummy file, output to dummy.asm -- `./build.sh` - Build the compiler +- `./build.sh tnslc` - Build the compiler ## License diff --git a/tnslc/build.sh b/tnslc/build.sh index 6bee3fc..d66742f 100755 --- a/tnslc/build.sh +++ b/tnslc/build.sh @@ -5,9 +5,20 @@ ARTIFACT_DIR=$BUILD_DIR/artifacts mkdir -p $BUILD_DIR mkdir -p $ARTIFACT_DIR -filename=tnslc.tnsl +filename=$1 filename="${filename%.*}" + ./ctc $filename.tnsl $ARTIFACT_DIR/$filename.asm + +if [ $? -ne 0 ]; then + exit $? +fi + nasm -g -f elf64 -o $ARTIFACT_DIR/$filename.o $ARTIFACT_DIR/$filename.asm + +if [ $? -ne 0 ]; then + exit $? +fi + gcc -ggdb -o $BUILD_DIR/$filename $ARTIFACT_DIR/$filename.o diff --git a/tnslc/compile/codegen.tnsl b/tnslc/compile/codegen.tnsl new file mode 100644 index 0000000..636f16b --- /dev/null +++ b/tnslc/compile/codegen.tnsl @@ -0,0 +1,122 @@ +/; _indent (int idt) + /; loop (int i = 0; i < idt) [i++] + _printf(" \0") + ;/ +;/ + +/; parse_tree(~utils.File fin) + # Parse files into AST + parse.Node ast = parse.generate_ast(fin) + ast.update_children() + + # Print parse tree + parse.print_ast(~ast) + + ast.end() +;/ + +/; mod_tree(~utils.File fin) + # Parse files into AST + parse.Node ast = parse.generate_ast(fin) + ast.update_children() + + # Create output buffer + CompBuf buffer + buffer.init() + + Module mod + mod.init(~ast, ~buffer) + _gen_prims(~mod) + mod.update_children() + mod.collect_methods(~ast) + + # Compile code + mod.compile(~buffer) + + mod.print() + + # Free all structs + mod.end() + buffer.end() + ast.end() +;/ + + +/; generate (~utils.File fin, fout) + # Parse files into AST + parse.Node ast = parse.generate_ast(fin) + ast.update_children() + + # Create output buffer + CompBuf buffer + buffer.init() + + # Transform into a module tree + Module mod + mod.init(~ast, ~buffer) + _gen_prims(~mod) + mod.update_children() + mod.collect_methods(~ast) + + # Compile code + mod.compile(~buffer) + + # Tests + # var_tests(~buffer) + + # Write assembly to output file + fout.create() + buffer.write_to(fout) + fout.close() + + # Free all structs + mod.end() + buffer.end() + ast.end() +;/ + +# Generate a primitive type with the specified size and specified name +/; _gen_prim(~Module m, int size, ~uint8 id) + ~Module mds = m`._create_methods(id) + + Var t + + Struct s + s.size = size + s.methods = NULL + s.members.init(len t) + s.name = utils.strcpy(id) + s._up = NULL + + m`.structs.push(~s) +;/ + +# This function generates the generic language primitives on +# the root module so that resolution will work when creating +# a variable or struct +/; _gen_prims (~Module m) + + # One byte prims + _gen_prim(m, 1, "bool\0") + _gen_prim(m, 1, "uint8\0") + _gen_prim(m, 1, "int8\0") + + # Two byte prims + _gen_prim(m, 2, "uint16\0") + _gen_prim(m, 2, "int16\0") + + # Four byte prims + _gen_prim(m, 4, "uint32\0") + _gen_prim(m, 4, "int32\0") + _gen_prim(m, 4, "float32\0") + + # Eight byte prims + _gen_prim(m, 8, "uint64\0") + _gen_prim(m, 8, "int64\0") + _gen_prim(m, 8, "float64\0") + _gen_prim(m, 8, "uint\0") + _gen_prim(m, 8, "int\0") + _gen_prim(m, 8, "float\0") + _gen_prim(m, 8, "void\0") +;/ + diff --git a/tnslc/compile/compbuf.tnsl b/tnslc/compile/compbuf.tnsl new file mode 100644 index 0000000..d4bc640 --- /dev/null +++ b/tnslc/compile/compbuf.tnsl @@ -0,0 +1,45 @@ + +struct CompBuf { + utils.Vector + sec_head, + sec_data, + sec_code +} + +/; method CompBuf + /; init + self.sec_head.init(1) + self.sec_data.init(1) + self.sec_code.init(1) + ;/ + + /; add_h(~uint8 text) + self.sec_head.push_cstr(text) + ;/ + + /; add_d(~uint8 text) + self.sec_data.push_cstr(text) + ;/ + + /; add_c(~uint8 text) + self.sec_code.push_cstr(text) + ;/ + + /; write_to(~utils.File fout) + fout`.write_cstr("BITS 64\n\0") + fout`.write_cstr(self.sec_head.as_cstr()) + + fout`.write_cstr("\nsection .data\n\n\0") + fout`.write_cstr(self.sec_data.as_cstr()) + + fout`.write_cstr("\nsection .text\n\n\0") + fout`.write_cstr(self.sec_code.as_cstr()) + ;/ + + /; end + self.sec_head.end() + self.sec_data.end() + self.sec_code.end() + ;/ +;/ + diff --git a/tnslc/compile/compile.tnsl b/tnslc/compile/compile.tnsl index 3560f48..709dbce 100644 --- a/tnslc/compile/compile.tnsl +++ b/tnslc/compile/compile.tnsl @@ -1,4 +1,11 @@ /; module compile - :import "type.tnsl" - :import "generate.tnsl" + :import "compbuf.tnsl" + :import "struct.tnsl" + :import "var.tnsl" + :import "function.tnsl" + :import "module.tnsl" + :import "tests_var.tnsl" + :import "codegen.tnsl" + :import "scope.tnsl" ;/ + diff --git a/tnslc/compile/function.tnsl b/tnslc/compile/function.tnsl new file mode 100644 index 0000000..9188ca3 --- /dev/null +++ b/tnslc/compile/function.tnsl @@ -0,0 +1,1918 @@ + +struct Function { + ~uint8 name, + utils.Vector + inputs, + outputs, + ~parse.Node _up, + int call_padding, + bool m, + ~Module mod +} + +~uint8 BOOL_OPS = "&&,||,^^,!&&,!||,!^^\0" +~uint8 CMP_OPS = "==,<,>,!==,!<,!>,<==,>==\0" + +/; method Function + /; init (~parse.Node n) + self.name = utils.strcpy(n`.data) + self._up = n + self.m = false + self.call_padding = 0 + Var v + self.inputs.init(len v) + self.outputs.init(len v) + ;/ + + /; _resolve_dlist (~Module parent, ~parse.Node dl) + ~parse.Node tn = NULL + ~parse.Node n + int reg = 1 + int stack_up = 8 + + /; if (self.m == true) + ~Struct s = parent`.related_type() + Var p + p._init(s) + _delete(p.name) + p.name = utils.strcpy("self\0") + p.ptr_push(0) + p.loc = reg + reg++ + self.inputs.push(~p) + ;/ + + /; if (dl == NULL) + return + ;/ + + /; loop (int i = 0; i < dl`.sub.count) [i++] + n = dl`.sub.get(i) + /; if (n`._type == parse.NTYPE_TYPE) + tn = n + ;; else if (n`._type == parse.NTYPE_ID) + /; if (tn == NULL) + _printf("Identifier declared in parameter list before any type was found!\n\0") + return + ;/ + Var p + p.init(tn, n) + p._resolve_type(parent) + # TODO: This is wrong + /; if (p.regable() == true && reg < 7) + p.loc = reg + reg++ + ;; else + p.loc = 0 - 1 + p.offset = stack_up + stack_up = stack_up + p.actual_size() + ;/ + self.inputs.push(~p) + ;/ + ;/ + ;/ + + /; _resolve_tlist (~Module parent, ~parse.Node tl) + ~parse.Node n + parse.Node out_id + out_id.data = "# OUTPUT #\0" + + int reg = 1 + int stack = 0 + /; loop (int i = 0; i < tl`.sub.count) [i++] + n = tl`.sub.get(i) + /; if (n`._type == parse.NTYPE_TYPE) + # TODO: also wrong + Var r + r.init(n, ~out_id) + r._resolve_type(parent) + r._id = NULL + r._tn = NULL + + /; if (r.regable() == true && reg < 7) + r.loc = reg + /; if (reg > 4) + r.loc = r.loc + 4 + ;/ + reg++ + ;; else + r.loc = 0 - 1 + r.offset = stack + stack = stack + r.actual_size() + ;/ + + self.outputs.push(~r) + ;/ + ;/ + ;/ + + /; _resolve_type (~Module parent) + ~parse.Node _up = self._up + /; if (_up`.sub.count < 1) + /; if (self.m == true) + self._resolve_dlist(parent, NULL) + ;/ + return + ;/ + + ~parse.Node lst = _up`.sub.get(0) + /; if (lst`._type == parse.NTYPE_DLIST) + self._resolve_dlist(parent, lst) + /; if (_up`.sub.count > 1) + lst = _up`.sub.get(1) + ;/ + ;; else if (self.m == true) + self._resolve_dlist(parent, NULL) + ;/ + + /; if (lst`._type == parse.NTYPE_TLIST) + self._resolve_tlist(parent, lst) + ;/ + + int i_size = 0 + int o_size = 0 + ~Var v + + /; loop (int i = 0; i < self.inputs.count) [i++] + v = self.inputs.get(i) + /; if (v`.loc < 0) + int vsz = v`.actual_size() + i_size = i_size + vsz + ;/ + ;/ + + /; loop (int i = 0; i < self.outputs.count) [i++] + v = self.outputs.get(i) + /; if (v`.loc < 0) + int vsz = v`.actual_size() + o_size = o_size + vsz + ;/ + ;/ + + /; if (o_size > i_size) + int padding = o_size - i_size + + /; loop (int i = 0; i < self.inputs.count) [i++] + v = self.inputs.get(i) + int off = v`.offset + v`.offset = off + padding + ;/ + + self.call_padding = padding + ;/ + ;/ + + /; _compute_scope_vars_loop(~Scope s, ~parse.Node upper, int i) + ~parse.Node n + /; loop (i < upper`.sub.count) [i++] + n = upper`.sub.get(i) + /; if (n`._type == parse.NTYPE_DECL) + s`.mk_aware_node(n) + ;; else + s`.precheck_stmt(n) + ;/ + ;/ + ;/ + + /; _compute_scope_vars(~Scope s) + + ~Var in + /; loop (int i = 0; i < self.inputs.count) [i++] + in = self.inputs.get(i) + s`.mk_aware(in) + ;/ + + int i = self._first_stmt_off() + /; if (i < 0) + return + ;/ + + ~parse.Node _up = self._up + self._compute_scope_vars_loop(s, _up, i) + ;/ + + + /; _call_label [~uint8] + Scope tmp + self.mod`._print(0) + tmp.init(self.mod, NULL, self.name) + ~uint8 out = tmp.base_label() + tmp.end() + return out + ;/ + + /; _build_func(~Module parent, ~CompBuf cb) [Scope] + Scope out + out.init(parent, cb, self.name) + out.parent = NULL + + /; if (parent`.e == true) + # Add to the global exports if the parent is exported + ~uint8 bl = out.base_label() + cb`.add_h("global \0") + cb`.add_h(bl) + cb`.add_h("\n\0") + _delete(bl) + ;/ + + # Write label and opening + # Nieve implementation: r10-r15 are callee saved registers + # in the TNSL style ABI + out.place_base_label() + cb`.add_c(" push rbp\n\0") + cb`.add_c(" lea rbp, [rsp + 8]\n\0") + cb`.add_c(" push r10\n\0") + cb`.add_c(" push r11\n\0") + cb`.add_c(" push r12\n\0") + cb`.add_c(" push r13\n\0") + cb`.add_c(" push r14\n\0") + cb`.add_c(" push r15 ; scope init\n\n\0") + + self._compute_scope_vars(~out) + + # Add all params to the scope + ~Var in + /; loop (int i = 0; i < self.inputs.count) [i++] + in = self.inputs.get(i) + /; if (in.is_ref()) + ~int32 ptc = in.top_ptrc() + int32 set = 0 + set = set - 1 + ptc` = set + ;/ + out.mk_set_var(in) + ;/ + + return out + ;/ + + /; _end_func(~Scope scope, ~CompBuf cb) + cb`.add_c("\n\0") + scope`.place_end_label() + cb`.add_c(" lea rsp, [rbp - 56]\n\0") + cb`.add_c(" pop r15\n\0") + cb`.add_c(" pop r14\n\0") + cb`.add_c(" pop r13\n\0") + cb`.add_c(" pop r12\n\0") + cb`.add_c(" pop r11\n\0") + cb`.add_c(" pop r10\n\0") + cb`.add_c(" pop rbp\n\0") + cb`.add_c(" ret ; scope end\n\n\n\0") + + scope`.end() + ;/ + + /; _first_stmt_off [int] + int i = 0 + ~parse.Node _up = self._up + + /; if (_up`.sub.count < 1) + return i - 1 + ;/ + + ~parse.Node n = _up`.sub.get(i) + /; if (n`._type == parse.NTYPE_DLIST) + i++ + /; if (_up`.sub.count > 1) + n = _up`.sub.get(1) + ;/ + ;/ + + /; if (n`._type == parse.NTYPE_TLIST) + i++ + ;/ + + return i + ;/ + + /; _compile (~Module parent, ~CompBuf cb) + # Sanity check + int i = self._first_stmt_off() + + # Create scope + Scope fscope = self._build_func(parent, cb) + + /; if (i !< 0) + ~parse.Node _up = self._up + self._compile_statements(~fscope, _up, i) + ;/ + + # Compile and then end scope + self._end_func(~fscope, cb) + ;/ + + # + # Compiling individual statements + # + + /; _compile_statements (~Scope s, ~parse.Node _up, int off) + ~parse.Node n = NULL + /; loop (off < _up`.sub.count) [off++] + n = _up`.sub.get(off) + /; if (n`._type == parse.NTYPE_FLOW_CONTROL) + self._compile_flow_control(s, n) + ;; else if (n`._type == parse.NTYPE_ASM) + s`.cb`.add_c(" \0") + s`.cb`.add_c(n`.data) + s`.cb`.add_c(" ; User defined asm\n\0") + ;; else if (n`._type == parse.NTYPE_DECL) + self._compile_decl(s, n) + ;; else if (n`._type == parse.NTYPE_VALUE) + + Var v = self._compile_value(s, n) + /; if (s`.is_tmp(~v)) + s`.free_to(~v, true) + ;/ + v.end() + ;; else if (n`._type == parse.NTYPE_IF_BLOCK) + off = self._compile_if(s, _up, off) + ;; else if (n`._type == parse.NTYPE_LOOP_BLOCK) + self._compile_loop(s, n) + ;/ + ;/ + ;/ + + /; _compile_cf_pre(~Scope s, ~parse.Node _up) [bool] + ~parse.Node n = NULL + + bool last_var = false + Var v + + /; loop (int off = 0; off < _up`.sub.count) [off++] + n = _up`.sub.get(off) + /; if (n`._type == parse.NTYPE_FLOW_CONTROL) + self._compile_flow_control(s, n) + ;; else if (n`._type == parse.NTYPE_ASM) + s`.cb`.add_c(" \0") + s`.cb`.add_c(n`.data) + s`.cb`.add_c(" ; User defined asm\n\0") + ;; else if (n`._type == parse.NTYPE_DECL) + self._compile_decl(s, n) + ;; else if (n`._type == parse.NTYPE_VALUE) + + v = self._compile_value(s, n) + + int count = _up`.sub.count + /; if (off + 1 == count) + last_var = true + ;; else + /; if (s`.is_tmp(~v)) + s`.free_to(~v, true) + ;/ + v.end() + ;/ + ;; else if (n`._type == parse.NTYPE_IF_BLOCK) + off = self._compile_if(s, _up, off) + ;; else if (n`._type == parse.NTYPE_LOOP_BLOCK) + self._compile_loop(s, n) + ;/ + ;/ + + /; if (last_var == true) + last_var = false + /; if (v.is_struct() == false) + # Do cond jmp + ~CompBuf buf = s`.cb + ~uint8 lab = s`.end_label() + /; if (v.loc == 0 && v.offset == 0) + # False was passed, always jump to end + buf`.add_c(" jmp \0") + buf`.add_c(lab) + buf`.add_c("\n\0") + ;; else if (v.loc !== 0) + v.test(s`.cb) + buf`.add_c(" je \0") + buf`.add_c(lab) + buf`.add_c("\n\0") + ;/ + _delete(lab) + last_var = true + ;/ + + /; if (s`.is_tmp(~v) == true) + s`.free_to(~v, true) + ;/ + v.end() + ;/ + + return last_var + ;/ + + /; _compile_cf_post(~Scope s, ~parse.Node pre, post, bool has_pre) + ~parse.Node n = NULL + + bool last_var = false + Var v + + /; loop (int i = 0; i < post`.sub.count) [i++] + n = post`.sub.get(i) + /; if (n`._type == parse.NTYPE_FLOW_CONTROL) + self._compile_flow_control(s, n) + ;; else if (n`._type == parse.NTYPE_ASM) + s`.cb`.add_c(" \0") + s`.cb`.add_c(n`.data) + s`.cb`.add_c(" ; User defined asm\n\0") + ;; else if (n`._type == parse.NTYPE_DECL) + self._compile_decl(s, n) + ;; else if (n`._type == parse.NTYPE_VALUE) + + v = self._compile_value(s, n) + + int count = post`.sub.count + /; if (i + 1 == count) + last_var = true + ;; else + /; if (s`.is_tmp(~v)) + s`.free_to(~v, true) + ;/ + v.end() + ;/ + ;; else if (n`._type == parse.NTYPE_IF_BLOCK) + i = self._compile_if(s, post, i) + ;; else if (n`._type == parse.NTYPE_LOOP_BLOCK) + self._compile_loop(s, n) + ;/ + ;/ + + /; if (last_var == true) + last_var = false + /; if (v.is_struct() == false) + # Do cond jmp + ~CompBuf buf = s`.cb + ~uint8 lab = s`.end_label() + /; if (v.loc == 0 && v.offset == 0) + # False was passed, always jump to end + buf`.add_c(" jmp \0") + buf`.add_c(lab) + buf`.add_c("\n\0") + ;; else if (v.loc !== 0) + v.test(s`.cb) + buf`.add_c(" je \0") + buf`.add_c(lab) + buf`.add_c("\n\0") + ;/ + _delete(lab) + last_var = true + ;/ + + /; if (s`.is_tmp(~v) == true) + s`.free_to(~v, true) + ;/ + v.end() + ;/ + + /; if (last_var == false) + /; if (has_pre == true) + # Grab last item from pre block and compile as value. Then do similar to above + # as if it was the last statement + int off = pre`.sub.count - 1 + n = pre`.sub.get(off) + v = self._compile_value(s, n) + + # Do cond jmp + ~CompBuf buf = s`.cb + ~uint8 lab = s`.end_label() + /; if (v.loc == 0 && v.offset == 0) + # False was passed, always jump to end + buf`.add_c(" jmp \0") + buf`.add_c(lab) + buf`.add_c("\n\0") + ;; else if (v.loc !== 0) + v.test(s`.cb) + buf`.add_c(" je \0") + buf`.add_c(lab) + buf`.add_c("\n\0") + ;/ + _delete(lab) + + /; if (s`.is_tmp(~v) == true) + s`.free_to(~v, true) + ;/ + v.end() + ;/ + ;/ + ;/ + + /; _compile_if_if(~Scope wrap, ~parse.Node n) + /; if (n`.sub.count < 1) + # Sanity + return + ;/ + + # Generate and pre-compute scope + Scope s = wrap`.gen_sub("if\0") + ~parse.Node first = n`.sub.get(0) + int off = 0 + + # Compile pre statements if applicable and do conditional jmp + /; if (first`._type == parse.NTYPE_SLIST) + self._compute_scope_vars_loop(~s, first, 0) + self._compute_scope_vars_loop(~s, n, 1) + self._compile_cf_pre(~s, first) + off = off + 1 + ;; else + self._compute_scope_vars_loop(~s, n, 0) + ;/ + + # Actually compile all the statements + self._compile_statements(~s, n, off) + + # If we did execute the if branch then we are jumping to the end of the wrapper + ~CompBuf cb = wrap`.cb + cb`.add_c(" jmp \0") + ~uint8 lab = wrap`.end_label() + cb`.add_c(lab) + cb`.add_c("\n\0") + _delete(lab) + + # Place an anchor for the negative case to latch on to and clean up the scope + s.place_end_label() + s.end() + ;/ + + /; _compile_if_elif(~Scope wrap, ~parse.Node n) [bool] + /; if (n`._type !== parse.NTYPE_ELIF_BLOCK) + return false + ;/ + + self._compile_if_if(wrap, n) + return true + ;/ + + /; _compile_if_else(~Scope wrap, ~parse.Node n) [bool] + /; if (n`._type !== parse.NTYPE_ELSE_BLOCK) + return false + ;/ + + self._compile_if_if(wrap, n) + return true + ;/ + + /; _compile_if (~Scope s, ~parse.Node n, int off) [int] + Scope wrap = s`.gen_sub("wrap\0") + + ~parse.Node block = n`.sub.get(off) + self._compile_if_if(~wrap, block) + + bool run = true + /; loop (run == true) + off = off + 1 + /; if (off !< n`.sub.count) + run = false + ;; else + block = n`.sub.get(off) + /; if (self._compile_if_elif(~wrap, block) == false) + run = false + ;/ + ;/ + ;/ + + /; if (off < n`.sub.count) + /; if (self._compile_if_else(~wrap, block) == true) + off = off + 1 + ;/ + ;/ + + wrap.place_end_label() + wrap.end() + + off = off - 1 + return off + ;/ + + + /; _compile_loop (~Scope s, ~parse.Node n) + # Generate scope + Scope ls = s`.gen_sub("loop\0") + + # Grab pre-statements, post-statements, and offset + ~parse.Node pre = NULL + ~parse.Node post = NULL + int off = 0 + + /; if (n`.sub.count > 0) + ~parse.Node first = n`.sub.get(0) + /; if (first`._type == parse.NTYPE_SLIST) + /; if (utils.strcmp(first`.data, "(\0") == true) + pre = first + off++ + ;; else if (utils.strcmp(first`.data, "[\0") == true) + post = first + off++ + ;/ + ;/ + ;/ + + /; if (n`.sub.count > 1) + ~parse.Node second = n`.sub.get(1) + /; if (second`._type == parse.NTYPE_SLIST) + /; if (utils.strcmp(second`.data, "[\0") == true) + post = second + off++ + ;/ + ;/ + ;/ + + # Scope pre-check + /; if (pre !== NULL) + self._compute_scope_vars_loop(~ls, pre, 0) + ;/ + + /; if (post !== NULL) + self._compute_scope_vars_loop(~ls, post, 0) + ;/ + + self._compute_scope_vars_loop(~ls, n, off) + + # Compile pre statements if applicable and do conditional jmp + bool has_pre = false + /; if (pre !== NULL) + has_pre = self._compile_cf_pre(~ls, pre) + ;/ + + # Place an anchor for repeating the loop to latch on to + ls.place_start_label() + + # Actually compile all the statements + self._compile_statements(~ls, n, off) + + # Place an anchor for continue statements to latch on to + ls.place_rep_label() + + # Compile post-statements if applicable + /; if (post !== NULL) + self._compile_cf_post(~ls, pre, post, has_pre) + ;; else if (has_pre == true) + # In this case no post but we have a valid condition in the pre-statements + # so we should use that as the rep condition + int idx = pre`.sub.count - 1 + ~parse.Node last = pre`.sub.get(idx) + Var v = self._compile_value(~ls, last) + + # Do cond jmp + ~CompBuf buf = ls.cb + ~uint8 lab = ls.end_label() + /; if (v.loc == 0 && v.offset == 0) + # False was passed, always jump to end + buf`.add_c(" jmp \0") + buf`.add_c(lab) + buf`.add_c("\n\0") + ;; else if (v.loc !== 0) + v.test(s`.cb) + buf`.add_c(" je \0") + buf`.add_c(lab) + buf`.add_c("\n\0") + ;/ + _delete(lab) + + /; if (ls.is_tmp(~v) == true) + ls.free_to(~v, true) + ;/ + v.end() + ;/ + + # Default to repeating the loop + ~CompBuf cb = ls.cb + cb`.add_c(" jmp \0") + ~uint8 lab = ls.start_label() + cb`.add_c(lab) + cb`.add_c("\n\0") + _delete(lab) + + # Place an anchor for breaks to latch on to and clean up the scope + ls.place_end_label() + ls.end() + ;/ + + /; _check_return (~parse.Node n) [bool] + int expected = self.outputs.count + int have = 0 + + ~parse.Node val + /; if (n`.sub.count > 0) + val = n`.sub.get(0) + val = val`.sub.get(0) + /; if (expected > 1 && val`._type == parse.NTYPE_VLIST) + have = val`.sub.count + ;; else + have = 1 + ;/ + ;/ + + /; if (have == expected) + return true + ;/ + + _print_num("ERROR: Number of return values (%d) does not match expected (\0", have) + _print_num("%d)\n\0", expected) + _printf("ERROR: To return multiple values enclose them in {}\n\0") + _printf("ERROR: To add more return values, add them (comma separated) between [] at the end of the function definition\n\0") + + return false + ;/ + + /; _compile_return_vals (~Scope s, ~parse.Node n) + _printf("Compiling return vals!\n\0") + /; if (self.outputs.count == 0) + return + ;/ + + n = n`.sub.get(0) + /; if (self.outputs.count > 1) + n = n`.sub.get(0) + ;/ + + s`.cb`.add_c("\n ; STARTING RETURN\n\0") + + ~Var out + Var cmp + /; if (self.outputs.count > 1) + ~parse.Node val_node + Var tmp + utils.Vector tmps + tmps.init(len cmp) + + # Compile all values + /; loop (int i = 0; i < self.outputs.count) [i++] + val_node = n`.sub.get(i) + out = self.outputs.get(i) + + tmp = s`.mk_tmp(out) + cmp = self._compile_value(s, val_node) + /; if (tmp.is_ref() == true) + int32 pp = 0 + pp = pp - 1 + tmp.ptr_pop() + tmp.ptr_push(pp) + ;; else + tmp.set(s`.cb, ~cmp) + ;/ + cmp.end() + tmps.push(~tmp) + ;/ + + # Set all outputs + ~Var tt + /; loop (int i = 0; i < self.outputs.count) [i++] + out = self.outputs.get(i) + tt = tmps.get(i) + out`.set(s`.cb, tt) + tt`.end() + ;/ + + s`.free_tmp(tmps.count, false) + tmps.end() + ;; else + out = self.outputs.get(0) + cmp = self._compile_value(s, n) + out`.set(s`.cb, ~cmp) + cmp.end() + ;/ + ;/ + + /; _return (~Scope s, ~parse.Node n) + /; if (self._check_return(n) !== true) + return + ;/ + + # Compute all return values and set the output variables properly + self._compile_return_vals(s, n) + + # Find root scope of function + /; loop (s`.parent !== NULL) + s = s`.parent + ;/ + + # Generate jump instruction + ~uint8 lab = s`.end_label() + s`.cb`.add_c(" jmp \0") + s`.cb`.add_c(lab) + s`.cb`.add_c("\n\0") + _delete(lab) + ;/ + + # Should handle break, continue, and return + /; _compile_flow_control (~Scope s, ~parse.Node n) + /; if (utils.strcmp(n`.data, "return\0") == true) + # Compute value and return + self._return(s, n) + ;; else if (utils.strcmp(n`.data, "continue\0") == true) + ~Scope lp = s`.closest_loop() + /; if (lp == NULL) + return + ;/ + + ~uint8 lab = lp`.rep_label() + s`.cb`.add_c(" jmp \0") + s`.cb`.add_c(lab) + s`.cb`.add_c("\n\0") + _delete(lab) + ;; else if (utils.strcmp(n`.data, "break\0") == true) + ~Scope br = s`.closest_break() + /; if (br == NULL) + return + ;/ + + ~uint8 lab = br`.rep_label() + s`.cb`.add_c(" jmp \0") + s`.cb`.add_c(lab) + s`.cb`.add_c("\n\0") + _delete(lab) + ;; else + _printf("COMPILER ERROR: The following was detected as flow control, but we do not handle it: '\0") + _printf(n`.data) + _printf("'\n\0") + _printf("COMPILER ERROR: this is likely a bug with the compiler, please report it along with the\n\0") + _printf("COMPILER ERROR: code snippit which caused the bug!\n\0") + ;/ + ;/ + + # Should handle variable declarations + /; _compile_decl(~Scope s, ~parse.Node n) + ~parse.Node sub + /; loop (int i = 0; i < n`.sub.count) [i++] + sub = n`.sub.get(i) + /; if (sub`._type == parse.NTYPE_ID) + ~Var v = s`._find_var(sub`.data) + + /; if (v == NULL) + _printf("Failed to find variable '\0") + _printf(sub`.data) + _printf("'\n\0") + return + ;/ + + /; if (sub`.sub.count > 0) + sub = sub`.sub.get(0) + Var val = self._compile_value(s, sub) + # Need var name correct + _delete(val.name) + ~uint8 name = utils.strcpy(v`.name) + val.name = name + s`.mk_set_var(~val) + val.end() + ;; else + s`.mk_var(v) + ;/ + ;/ + ;/ + ;/ + + /; _compile_call(~Scope s, ~parse.Node params, ~Function f, ~Var _self, bool mth) [Var] + Var result + ~Var out + /; if (f`.outputs.count > 0) + # Generate result tmp if required + out = f`.outputs.get(0) + /; if (out`.loc < 0) + result = s`.mk_tmp_stack(out) + ;; else + result = out`.copy() + ;/ + ;; else + ~Struct t = self._find_literal_type(s, "void\0") + result._init(t) + ;/ + + # Save caller saved tmp variables + Var handle = s`.save_caller_tmp() + + # Check that parameter count matches + int param_count = params`.sub.count + /; if (mth == true) + param_count++ + ;/ + + /; if (params`.sub.count == 1) + ~parse.Node nn + nn = params`.sub.get(0) + /; if (nn`._type == parse.NTYPE_VALUE && nn`.sub.count == 0) + param_count = param_count - 1 + ;/ + ;/ + + /; if (param_count !== f`.inputs.count) + ~uint8 blab = s`.base_label() + _printf("ERROR: Failed to call function \"\0") + _printf(f`.name) + _printf("\" from scope \"\0") + _printf(blab) + _printf("\"\n\0") + _print_num(" Number of arguments (%d) did not match number of provided parameters \0", f`.inputs.count) + _print_num("(%d)\n\0", param_count) + _delete(blab) + result.loc = 0 + return result + ;/ + + # Then generate a vector to store all the tmp variables which will store the + # params as intermediaries + utils.Vector tmps + tmps.init(len result) + tmps._grow(param_count) + tmps.count = param_count + + # Create all stack based tmps + ~Var inp + Var last_stack + last_stack.loc = 0 + /; loop (int i = 1; i !> f`.inputs.count) [i++] + int pidx = f`.inputs.count - i + inp = f`.inputs.get(pidx) + /; if (inp`.loc < 0) + # Make tmp on stack and compute + Var to_set = s`.mk_tmp_stack(inp) + + /; if (to_set.is_ref() == true) + # Correct for ref + int32 ptrc = 0 + ptrc = ptrc - 1 + to_set.ptr_pop() + to_set.ptr_push(ptrc) + ;/ + + tmps.replace(pidx, ~to_set) + last_stack = to_set + ;/ + ;/ + + # Create all register based tmps + Var last_reg + last_reg.loc = 0 + /; loop (int i = 0; i < f`.inputs.count) [i++] + inp = f`.inputs.get(i) + /; if (inp`.loc > 0) + # Make tmp on stack and compute + Var to_set = s`.mk_tmp_stack(inp) + + /; if (to_set.is_ref() == true) + # Correct for ref + int32 ptrc = 0 + ptrc = ptrc - 1 + to_set.ptr_pop() + to_set.ptr_push(ptrc) + ;/ + + tmps.replace(i, ~to_set) + last_reg = to_set + ;/ + ;/ + + # Set 'self' if required + int use_self = 0 + /; if (mth == true) + inp = tmps.get(0) + + Var _sptr = _self`.take_ptr(s`.cb, 1) + inp`.set(s`.cb, ~_sptr) + _sptr.end() + use_self++ + ;/ + + # compile all parameters and put them into tmp vars + ~parse.Node pnode + /; loop (int i = use_self; i < f`.inputs.count) [i++] + pnode = params`.sub.get(i) + inp = tmps.get(i) + + Var param = self._compile_value(s, pnode) + inp`.set(s`.cb, ~param) + /; if (s`.is_tmp(~param)) + s`.free_to(~param, true) + ;/ + param.end() + ;/ + + # Move all register parameters into registers and free the stack space + ~Var ptmp + /; loop (int i = 0; i < f`.inputs.count) [i++] + inp = f`.inputs.get(i) + /; if (inp`.loc > 0) + ptmp = tmps.get(i) + /; if (inp`.is_ref() == true) + Var to_set = inp`.take_ptr(s`.cb, 1) + to_set.set(s`.cb, ptmp) + to_set.end() + ;; else + inp`.set(s`.cb, ptmp) + ;/ + ;/ + ;/ + + /; if (last_stack.loc !== 0) + s`.free_after(~last_stack, true) + ;/ + + # Pad out the stack for return if required + ~CompBuf buf = s`.cb + /; if (f`.call_padding > 0) + ~uint8 pad_num = utils.int_to_str(f`.call_padding) + buf`.add_c(" sub rsp, \0") + buf`.add_c(pad_num) + buf`.add_c("\n\0") + _delete(pad_num) + ;/ + + # Do call + ~uint8 call = f`._call_label() + buf`.add_c(" call \0") + buf`.add_c(call) + buf`.add_c("\n\0") + _delete(call) + + # Generate result variable and move into the result if required + /; if (f`.outputs.count > 0) + /; if (out`.loc < 0) + Var out_pos = out`.copy() + + /; if (out_pos.is_ref() == true) + int32 pp = 0 + pp = pp - 1 + out_pos.ptr_pop() + out_pos.ptr_push(pp) + ;/ + + out_pos.loc = last_stack.loc + out_pos.offset = last_stack.offset + + /; if (f`.call_padding > 0) + int offset = out_pos.offset + offset = offset - f`.call_padding + out_pos.offset = offset + ;/ + + bool is_ref = false + /; if (result.is_ref() == true) + is_ref = true + int32 pp = 0 + pp = pp - 1 + result.ptr_pop() + result.ptr_push(pp) + ;/ + + result.set(s`.cb, ~out_pos) + out_pos.end() + + /; if (is_ref == true) + result.ptr_pop() + result.ptr_push(0) + ;/ + ;/ + ;/ + + # Free all in tmp vector + /; loop (int i = 0; i < tmps.count) [i++] + inp = tmps.get(i) + inp`.end() + ;/ + tmps.end() + + # Restore tmps in regs + s`.restore_caller_tmp(~handle) + + /; if (result.loc < 0) + s`.free_after(~result, true) + ;/ + + return result + ;/ + + /; _set_var_ptr(~void v, ~void new_val) + Var v + int sz = len v + /; loop (int i = 0; i < sz) [i++] + ~uint8 out = v + i + ~uint8 in = new_val + i + out` = in` + ;/ + ;/ + + # Chain compilation functions get pretty long. Read at your own risk + + /; _compile_chain_r_post(~Scope s, ~parse.Node post, ~Var v) + # Post op type + /; if (utils.strcmp(post`.data, "(\0") == true) + _printf("TODO: tnslc does not yet support calling variables\n\0") + ;; else if (utils.strcmp(post`.data, "`\0") == true) + Var tmp = v`.de_ref() + v`.end() + self._set_var_ptr(v, ~tmp) + ;; else if (utils.strcmp(post`.data, "{\0") == true) + post = post`.sub.get(0) + + # make sure we store variable if it's in a register which may be overwritten + /; if (v`.loc > 0) + /; if (s`.is_tmp(v) == false) + bool is_ref = false + /; if (v`.is_ref() == true) + is_ref = true + int32 pp = 0 + pp = pp - 1 + v`.ptr_pop() + v`.ptr_push(pp) + ;/ + + Var tmp = s`.mk_tmp(v) + tmp.set(s`.cb, v) + v`.end() + /; if (is_ref == true) + tmp.ptr_pop() + tmp.ptr_push(0) + ;/ + self._set_var_ptr(v, ~tmp) + ;/ + ;/ + + # Compute index + Var idx = self._compile_value(s, post) + /; if (idx.loc == 6 || idx.loc == 5) + ~CompBuf buf = s`.cb + buf`.add_c(" mov rax, rdi\n\0") + idx.loc = 1 + ;/ + + # Take index and set result + Var tmp = v`.index(s`.cb, ~idx, 6) + + # Clear tmp + /; if (s`.is_tmp(v) == true) + s`.free_to(v, true) + ;/ + + v`.end() + idx.end() + self._set_var_ptr(v, ~tmp) + ;; else if (utils.strcmp(post`.data, "++\0") == true) + # Generate copy (make sure no refs) + Var copy = v`.copy() + copy.strip_refs() + # Make tmp var + Var tmp = s`.mk_tmp(~copy) + copy.end() + # Set the new tmp to the value of the old + tmp.set(s`.cb, v) + # Increment the old + v`.inc(s`.cb) + v`.end() + self._set_var_ptr(v, ~tmp) + ;; else if (utils.strcmp(post`.data, "--\0") == true) + # Generate copy (make sure no refs) + Var copy = v`.copy() + copy.strip_refs() + # Make tmp var + Var tmp = s`.mk_tmp(~copy) + copy.end() + # Set the new tmp to the value of the old + tmp.set(s`.cb, v) + # Decrement the old + v`.dec(s`.cb) + v`.end() + self._set_var_ptr(v, ~tmp) + ;/ + ;/ + + /; _compile_chain_r_base (~Scope s, ~parse.Node base, ~utils.Vector v) [Var] + # Base case for dot chains + Var result + result.loc = 0 + + # Sanity + /; if (base`._type !== parse.NTYPE_ID) + _printf("TODO: tnslc does not currently support dot-chains with literals or non-identifiers as primary\n\0") + _printf(" will look into either intuiting the type from context or requiring a cast in the future\n\0") + v`.end() + v`.count = 0 + return result + ;/ + + # Check for var + ~Var vv = s`.find_var(base`.data) + /; if (vv == NULL) + # If could not find in scope then try to find in module + ~uint8 name = base`.data + v`.push(~name) + ~Module mod = s`.mod + vv = mod`.find(SEARCH_VAR, v) + /; if (vv !== NULL) + result = vv`.as_global() + v`.end() + v`.count = 0 + ;/ + ;; else + result = vv`.copy() + v`.end() + v`.count = 0 + ;/ + + # If has a post op + /; if (base`.sub.count > 0) + ~parse.Node sub = base`.sub.get(0) + /; if (utils.strcmp(sub`.data, "(\0") == true) + /; if (vv !== NULL) + result.end() + result.loc = 0 + _printf("TODO: tnslc does not yet support calling variables\n\0") + return result + ;/ + + # Try to find function. Make sure scope not in method module first. + ~Module mod = s`.mod + /; if (mod`.is_method() == true) + mod = mod`.parent + ;/ + + # Already tried to find in module so can just use v for search. + ~Function to_call = mod`.find(SEARCH_FUNC, v) + + /; if (to_call == NULL) + _printf("ERROR: Unable to find function or variable with name \"\0") + _printf(base`.data) + _printf("\" in scope \"\0") + ~uint8 blab = s`.base_label() + _printf(blab) + _printf("\"\n\0") + _delete(blab) + v`.end() + v`.count = 0 + return result + ;/ + + # Do call + result = self._compile_call(s, sub, to_call, NULL, false) + v`.end() + v`.count = 0 + + ;; else if (result.loc == 0) + # Exit + _printf("ERROR: Unable to find variable \"\0") + _printf(base`.data) + _printf("\" in scope \"\0") + ~uint8 blab = s`.base_label() + _printf(blab) + _printf("\"\n\0") + _delete(blab) + v`.end() + v`.count = 0 + return result + ;; else + self._compile_chain_r_post(s, sub, ~result) + ;/ + ;/ + + # Loop over all post-ops past the first + ~parse.Node post + /; loop (int i = 1; i < base`.sub.count) [i++] + post = base`.sub.get(i) + /; if (utils.strcmp(post`.data, "(\0") == true) + result.end() + result.loc = 0 + _printf("TODO: tnslc does not yet support calling variables\n\0") + v`.end() + v`.count = 0 + return result + ;/ + self._compile_chain_r_post(s, post, ~result) + ;/ + + return result + ;/ + + # Function: the very long one. + /; _compile_chain_r (~Scope s, ~parse.Node n, ~utils.Vector v) [Var] + ~parse.Node lhn = n`.sub.get(0) + Var working = self._compile_chain_r_base(s, lhn, v) + + /; if (v`.count == 0) + /; if (working.loc == 0) + # If we have encountered an error or determined we could not find the variable for certain. + return working + ;/ + ;/ + + # The way in which chains are parsed puts all dots in the second sub-node + + # Several points here: if have not found var or func yet need to try + # if we can't and there is a post-op, then we need to stop. + bool run = true + ~parse.Node rhn + /; loop (run == true) + rhn = n`.sub.get(1) + + /; if (utils.strcmp(rhn`.data, ".\0") == true) + n = rhn + rhn = n`.sub.get(0) + ;; else + run = false + ;/ + + # Sanity + /; if (rhn`._type !== parse.NTYPE_ID) + _printf("TODO: tnslc does not currently support dot-chains with literals or non-identifiers in the middle\n\0") + v`.end() + v`.count = 0 + working.loc = 0 + return working + ;/ + + /; if (v`.count !== 0) + # If we haven't yet found the var add to the vec and search the module + ~uint8 dat = rhn`.data + v`.push(~dat) + + ~Module mod = s`.mod + ~Var find = mod`.find(SEARCH_VAR, v) + + /; if (find !== NULL) + working = find`.as_global() + v`.end() + v`.count = 0 + ;/ + + # Then check for post-op. + /; if (rhn`.sub.count > 0) + ~parse.Node post_op = rhn`.sub.get(0) + /; if (utils.strcmp(post_op`.data, "(\0") == true) + # Try a call + /; if (find !== NULL) + _printf("TODO: TNSL doesn't currently have good support for function calling by value\n\0") + return working + ;/ + + # Try to find function. Does not matter if we are a module since this is multi-part search + ~Module mod = s`.mod + + # Already tried to find in module so can just use v for search. + ~Function to_call = mod`.find(SEARCH_FUNC, v) + + /; if (to_call == NULL) + _printf("ERROR: Unable to find function or variable with name \"\0") + _printf(rhn`.data) + _printf("\" in scope \"\0") + ~uint8 blab = s`.base_label() + _printf(blab) + _printf("\"\n\0") + _delete(blab) + v`.end() + v`.count = 0 + return working + ;/ + + # Do call + working = self._compile_call(s, post_op, to_call, NULL, false) + ;; else if (working.loc == 0) + # Didn't find a variable but there was a post op + _printf("ERROR: Failed to find variable (dot chain ends in \"\0") + _printf(rhn`.data) + _printf("\" in scope \"\0") + ~uint8 blab = s`.base_label() + _printf(blab) + _printf("\"\n\0") + _delete(blab) + v`.end() + v`.count = 0 + return working + ;; else + # Do the post op on the found variable + self._compile_chain_r_post(s, post_op, ~working) + ;/ + ;/ + ;; else + # We already have a var, not doing anything with the vector any more + + # Check method call + int post_idx = 0 + /; if (rhn`.sub.count > 0) + ~parse.Node post = rhn`.sub.get(0) + /; if (utils.strcmp(post`.data, "(\0") == true) + # Try method + post_idx++ + ~Function to_call = working.find_method(rhn`.data) + + /; if (to_call == NULL) + working.end() + working.loc = 0 + return working + ;/ + + # Create space for the variable in a tmp + Var result = self._compile_call(s, post, to_call, ~working, true) + working.end() + working = result + ;/ + ;/ + + /; if (post_idx == 0) + # Normal member + Var mbr = working.member(s`.cb, rhn`.data) + + /; if (mbr.loc == 0) + # If the member didn't exist we end here + # The member function already prints some info + working.end() + working.loc = 0 + return working + ;/ + _printf("Member:\n\0") + mbr._print(0) + + working.end() + working = mbr + + /; if (rhn`.sub.count > 0) + # Do first post-op after the member + ~parse.Node post = rhn`.sub.get(0) + self._compile_chain_r_post(s, post, ~working) + ;/ + ;/ + ;/ + + # Loop over rest of post ops + ~parse.Node post + /; loop (int i = 1; i < rhn`.sub.count) [i++] + post = rhn`.sub.get(i) + self._compile_chain_r_post(s, post, ~working) + ;/ + ;/ + + return working + ;/ + + /; _compile_chain (~Scope s, ~parse.Node n) [Var] + # FIND_VAR!!!!! + utils.Vector chain + chain.init(8) + return self._compile_chain_r(s, n, ~chain) + ;/ + + /; _compile_bool_op (~Scope s, ~parse.Node n) [Var] + Scope bs = s`.gen_sub("bool\0") + ~parse.Node lhn = n`.sub.get(0) + ~parse.Node rhn = n`.sub.get(1) + + ~uint8 op = n`.data + + ~Struct t = self._find_literal_type(s, "bool\0") + Var dummy + dummy._init(t) + Var out = s`.mk_tmp(~dummy) + dummy.end() + + # Compute left hand side + Var lhs = self._compile_value(s, lhn) + /; if (s`.is_tmp(~lhs) == true) + s`.free_after(~lhs, true) + ;/ + + /; if (lhs.is_struct() == true) + _printf("Can not use struct as boolean value\n\0") + lhs.end() + bs.end() + return out + ;/ + + ~CompBuf buf = s`.cb + + # Pre-operation (jmp or mov) for this bool operator + /; if (op{1} == '&') + lhs.test(buf) + ~uint8 lab = bs.end_label() + buf`.add_c(" je \0") + buf`.add_c(lab) + buf`.add_c("\n\0") + ;; else if (op{1} == '|') + lhs.test(buf) + ~uint8 lab = bs.end_label() + buf`.add_c(" jne \0") + buf`.add_c(lab) + buf`.add_c("\n\0") + ;; else if (op{1} == '^') + /; if (lhs.loc > 0 && lhs.loc < 11) + /; if (s`.is_tmp(~lhs) == false) + bool was_ref = false + /; if (lhs.is_ref() == true) + was_ref = true + int32 ptr = 0 + ptr = ptr - 1 + lhs.ptr_pop() + lhs.ptr_push(ptr) + ;/ + + Var tmp = s`.mk_tmp(~lhs) + + tmp.set(s`.cb, ~lhs) + lhs.end() + lhs = tmp + + /; if (was_ref == true) + lhs.ptr_pop() + lhs.ptr_push(0) + ;/ + ;/ + ;/ + ;/ + + # Compute right hand side + Var rhs = self._compile_value(s, rhn) + + /; if (rhs.is_struct() == true) + _printf("Can not use struct as boolean value\n\0") + lhs.end() + rhs.end() + bs.end() + return out + ;/ + + # Post compare or test for this particular bool op + /; if (op{1} == '^') + # Mov lhs to a register + bool is_ref = lhs.is_ref() + /; if (lhs.loc < 1 || is_ref == true) + Var tmp = lhs.copy() + tmp.strip_refs() + tmp.loc = 3 + tmp.offset = 0 + tmp.set(s`.cb, ~lhs) + lhs.end() + lhs = tmp + ;/ + + /; if (op{0} == '!') + out.cmov(s`.cb, ~lhs, ~rhs, "e\0") + ;; else + out.cmov(s`.cb, ~lhs, ~rhs, "e\0") + ;/ + ;; else + rhs.test(buf) + bs.place_end_label() + /; if (op{0} == '!') + out.cset(buf, "e\0") + ;; else + out.cset(buf, "ne\0") + ;/ + ;/ + + lhs.end() + rhs.end() + s`.free_after(~out, true) + bs.end() + return out + ;/ + + /; _compile_cmp_op (~Scope s, ~parse.Node n) [Var] + ~parse.Node lhn = n`.sub.get(0) + ~parse.Node rhn = n`.sub.get(1) + + # Setup output + ~Struct t = self._find_literal_type(s, "bool\0") + Var dummy + dummy._init(t) + Var out = s`.mk_tmp(~dummy) + dummy.end() + + # Compute left hand side + Var lhs = self._compile_value(s, lhn) + /; if (s`.is_tmp(~lhs) == true) + s`.free_after(~lhs, true) + ;/ + + # TODO: move lhs maybe if it is in rdi + /; if (lhs.loc > 0 && lhs.loc < 11) + /; if (s`.is_tmp(~lhs) == false) + bool was_ref = false + /; if (lhs.is_ref() == true) + was_ref = true + int32 ptr = 0 + ptr = ptr - 1 + lhs.ptr_pop() + lhs.ptr_push(ptr) + ;/ + + Var tmp = s`.mk_tmp(~lhs) + + tmp.set(s`.cb, ~lhs) + lhs.end() + lhs = tmp + + /; if (was_ref == true) + lhs.ptr_pop() + lhs.ptr_push(0) + ;/ + ;/ + ;/ + + # Compute right hand side + Var rhs = self._compile_value(s, rhn) + + # Check not structs + /; if (lhs.is_struct() == true || rhs.is_struct() == true) + _printf("Can not compare structs in this version of tnsl\n\0") + lhs.end() + rhs.end() + s`.free_after(~out, true) + return out + ;/ + + # Mov lhs to a register + bool is_ref = lhs.is_ref() + /; if (lhs.loc < 1 || is_ref == true) + Var tmp = lhs.copy() + tmp.strip_refs() + tmp.loc = 3 + tmp.offset = 0 + tmp.set(s`.cb, ~lhs) + lhs.end() + lhs = tmp + ;/ + + # Do comparitive move + /; if (utils.strcmp(n`.data, "==\0") == true) + out.cmov(s`.cb, ~lhs, ~rhs, "e\0") + ;; else if (utils.strcmp(n`.data, "<\0") == true) + out.cmov(s`.cb, ~lhs, ~rhs, "l\0") + ;; else if (utils.strcmp(n`.data, ">\0") == true) + out.cmov(s`.cb, ~lhs, ~rhs, "g\0") + ;; else if (utils.strcmp(n`.data, "!==\0") == true) + out.cmov(s`.cb, ~lhs, ~rhs, "ne\0") + ;; else if (utils.strcmp(n`.data, "!<\0") == true) + out.cmov(s`.cb, ~lhs, ~rhs, "nl\0") + ;; else if (utils.strcmp(n`.data, "!>\0") == true) + out.cmov(s`.cb, ~lhs, ~rhs, "ng\0") + ;; else if (utils.strcmp(n`.data, "<==\0") == true) + out.cmov(s`.cb, ~lhs, ~rhs, "le\0") + ;; else if (utils.strcmp(n`.data, ">==\0") == true) + out.cmov(s`.cb, ~lhs, ~rhs, "ge\0") + ;/ + + lhs.end() + rhs.end() + s`.free_after(~out, true) + return out + ;/ + + /; _compile_bin (~Scope s, ~parse.Node n) [Var] + # TODO + /; if (utils.strcmp(n`.data, ".\0") == true) + return self._compile_chain(s, n) + ;; else if (parse._in_csv(BOOL_OPS, n`.data) == true) + return self._compile_bool_op(s, n) + ;; else if (parse._in_csv(CMP_OPS, n`.data) == true) + return self._compile_cmp_op(s, n) + ;/ + + ~parse.Node lhn = n`.sub.get(0) + ~parse.Node rhn = n`.sub.get(1) + + # If we are setting + /; if (utils.strcmp(n`.data, "=\0") == true) + Var lhs = self._compile_value(s, lhn) + + /; if (s`.is_tmp(~lhs) == true) + s`.free_after(~lhs, true) + ;/ + + # TODO: move lhs maybe if it is in rdi + /; if (lhs.loc > 0 && lhs.loc < 11) + /; if (s`.is_tmp(~lhs) == false) + bool was_ref = false + /; if (lhs.is_ref() == true) + was_ref = true + int32 ptr = 0 + ptr = ptr - 1 + lhs.ptr_pop() + lhs.ptr_push(ptr) + ;/ + + Var tmp = s`.mk_tmp(~lhs) + + tmp.set(s`.cb, ~lhs) + lhs.end() + lhs = tmp + + /; if (was_ref == true) + lhs.ptr_pop() + lhs.ptr_push(0) + ;/ + ;/ + ;/ + + Var rhs = self._compile_value(s, rhn) + lhs.set(s`.cb, ~rhs) + + # Cleanup rhs + /; if (s`.is_tmp(~rhs) == true) + s`.free_to(~rhs, true) + ;/ + rhs.end() + + return lhs + ;/ + + # Otherwise + Var lhs = self._compile_value(s, lhn) + /; if (s`.is_tmp(~lhs) == false) + Var copy = lhs.copy() + copy.strip_refs() + Var tmp = s`.mk_tmp(~copy) + copy.end() + + tmp.set(s`.cb, ~lhs) + lhs.end() + lhs = tmp + ;/ + + /; if (lhs.is_ref() == true) + Var copy = lhs.copy() + copy.strip_refs() + Var tmp = s`.mk_tmp(~copy) + copy.end() + + tmp.set(s`.cb, ~lhs) + lhs.end() + lhs = tmp + ;/ + + Var rhs = self._compile_value(s, rhn) + + /; if (utils.strcmp(n`.data, "*\0") == true) + lhs.mul(s`.cb, ~rhs) + ;; else if (utils.strcmp(n`.data, "/\0") == true) + lhs.div(s`.cb, ~rhs) + ;; else if (utils.strcmp(n`.data, "%\0") == true) + lhs.mod(s`.cb, ~rhs) + ;; else if (utils.strcmp(n`.data, "+\0") == true) + lhs.add(s`.cb, ~rhs) + ;; else if (utils.strcmp(n`.data, "-\0") == true) + lhs.sub(s`.cb, ~rhs) + ;; else if (utils.strcmp(n`.data, "&\0") == true) + lhs.and(s`.cb, ~rhs) + ;; else if (utils.strcmp(n`.data, "|\0") == true) + lhs.or(s`.cb, ~rhs) + ;; else if (utils.strcmp(n`.data, "^\0") == true) + lhs.xor(s`.cb, ~rhs) + ;/ + + s`.free_after(~lhs, true) + rhs.end() + + return lhs + ;/ + + /; _compile_pre (~Scope s, ~parse.Node n) [Var] + Var out + + /; if (utils.strcmp(n`.data, "-\0") == true) + ~parse.Node sub = n`.sub.get(0) + out = self._compile_value(s, sub) + out.neg(s`.cb) + ;; else if (utils.strcmp(n`.data, "!\0") == true) + ~parse.Node sub = n`.sub.get(0) + out = self._compile_value(s, sub) + out.not(s`.cb) + ;; else if (utils.strcmp(n`.data, "~\0") == true) + ~parse.Node sub = n`.sub.get(0) + Var tmp = self._compile_value(s, sub) + out = tmp.take_ptr(s`.cb, 5) + tmp.end() + ;; else if (utils.strcmp(n`.data, "--\0") == true) + ~parse.Node sub = n`.sub.get(0) + out = self._compile_value(s, sub) + out.dec(s`.cb) + ;; else if (utils.strcmp(n`.data, "++\0") == true) + ~parse.Node sub = n`.sub.get(0) + out = self._compile_value(s, sub) + out.inc(s`.cb) + ;; else if (utils.strcmp(n`.data, "len\0") == true) + _printf("len not impl\n\0") + ;; else + _printf("COMPILER ERROR: \"\0") + _printf(n`.data) + _printf("\" NOT RECOGNIZED AS A VALID PREOP\n\0") + ;/ + + return out + ;/ + + /; _find_literal_type (~Scope s, ~uint8 name) [~Struct] + utils.Vector vec + vec.init(8) + vec.push(~name) + + ~Struct out + out = s`.mod`.find(SEARCH_STRUCT, ~vec) + + vec.end() + + return out + ;/ + + /; _compile_literal (~Scope s, ~parse.Node n) [Var] + Var out + + /; if (utils.strcmp(n`.data, "true\0") == true) + ~Struct t = self._find_literal_type(s, "bool\0") + out._init(t) + out.offset = 1 + ;; else if (utils.strcmp(n`.data, "false\0") == true) + ~Struct t = self._find_literal_type(s, "bool\0") + out._init(t) + out.offset = 0 + ;; else if (utils.strcmp(n`.data, "null\0") == true) + ~Struct t = self._find_literal_type(s, "void\0") + out._init(t) + out.offset = 0 + out.ptr_push(1) + ;; else if (n`.data{0} == '\'') + ~Struct t = self._find_literal_type(s, "uint8\0") + out._init(t) + ~uint8 dat = n`.data + dat++ + uint8 val = utils.unquote_cha(dat) + out.offset = val + ;; else if (n`.data{0} == '\"') + _printf("TODO: in-function strings\n\0") + ;; else + ~Struct t = self._find_literal_type(s, "int\0") + out._init(t) + out.offset = utils.cstr_to_int(n`.data) + ;/ + + return out + ;/ + + # Should handle computing a value, delegate to other funcs when needed + /; _compile_value (~Scope s, ~parse.Node n) [Var] + /; if (n`._type == parse.NTYPE_VALUE) + ~parse.Node v = n`.sub.get(0) + return self._compile_value(s, v) + + ;; else if (n`._type == parse.NTYPE_LITERAL) + return self._compile_literal(s, n) + + ;; else if (n`._type == parse.NTYPE_ID) + utils.Vector tmp + tmp.init(8) + Var out = self._compile_chain_r_base(s, n, ~tmp) + /; if (tmp.count !== 0) + tmp.end() + ;; else + return out + ;/ + + ;; else if (n`._type == parse.NTYPE_PRE_OP) + return self._compile_pre(s, n) + + ;; else if (n`._type == parse.NTYPE_BIN_OP) + return self._compile_bin(s, n) + ;/ + + _printf("COMPILER ERROR: NOT IMPL NODE TYPE \"\0") + parse.print_node_type(n) + _printf("\" IN ROOT VAL COMPUTATION\n\0") + Var out + out._init(NULL) + return out + ;/ + + /; _print (int idt) + _indent(idt) + _printf("{ Function : \0") + _printf(self.name) + _printf("\n\0") + + _indent(idt + 1) + _printf("inputs:\n\0") + ~Var prtv + /; loop (int i = 0; i < self.inputs.count) [i++] + prtv = self.inputs.get(i) + prtv`._print(idt + 2) + ;/ + + _indent(idt + 1) + _printf("outputs:\n\0") + /; loop (int i = 0; i < self.outputs.count) [i++] + prtv = self.outputs.get(i) + prtv`._print(idt + 2) + ;/ + + _indent(idt) + _printf("}\n\0") + ;/ + + /; end + _delete(self.name) + + ~Var v + /; loop (int i = 0; i < self.inputs.count) [i++] + v = self.inputs.get(i) + v`.end() + ;/ + self.inputs.end() + + /; loop (int i = 0; i < self.outputs.count) [i++] + v = self.outputs.get(i) + v`.end() + ;/ + self.outputs.end() + ;/ +;/ + diff --git a/tnslc/compile/generate.tnsl b/tnslc/compile/generate.tnsl deleted file mode 100644 index ef6a76c..0000000 --- a/tnslc/compile/generate.tnsl +++ /dev/null @@ -1,6 +0,0 @@ -/; generate (~utils.File fin, fout) - parse.Node n = parse.generate_ast(fin) - n.update_children() - parse.print_ast(~n) - n.end() -;/ diff --git a/tnslc/compile/module.tnsl b/tnslc/compile/module.tnsl new file mode 100644 index 0000000..86ede96 --- /dev/null +++ b/tnslc/compile/module.tnsl @@ -0,0 +1,504 @@ + +int SEARCH_VAR = 0 +int SEARCH_STRUCT = 1 +int SEARCH_FUNC = 2 +int SEARCH_SUB = 3 + +struct Module { + # Text name of module + ~uint8 name, + + # Various contained elements + utils.Vector + vars, + structs, + funcs, + subs, + + # Whether we export or not + bool e, + + # Parent module + ~Module parent +} + +/; method Module + + /; _init + Var v + Struct s + Function f + Module m + + self.parent = NULL + + self.vars.init(len v) + self.structs.init(len s) + self.funcs.init(len f) + self.subs.init(len m) + ;/ + + /; init (~parse.Node mod, ~CompBuf buf) + self._init() + self.name = utils.strcpy(mod`.data) + self.e = mod`._type == parse.NTYPE_EXPORT + + self._from_tree(mod, buf) + ;/ + + /; update_children + ~Module sub + /; loop (int i = 0; i < self.subs.count) [i++] + sub = self.subs.get(i) + sub`.update_children() + sub`.parent = ~self + ;/ + + ~Struct str + utils.Vector n + /; loop (int i = 0; i < self.structs.count) [i++] + str = self.structs.get(i) + + n.from_cstr("_#\0") + n.push_cstr(str`.name) + + sub = self._find_sub(n.as_cstr()) + str`.methods = sub + + n.end() + ;/ + + ~Var var + /; loop (int i = 0; i < self.vars.count) [i++] + var = self.vars.get(i) + var`.parent = ~self + ;/ + + ~Function func + /; loop (int i = 0; i < self.funcs.count) [i++] + func = self.funcs.get(i) + func`.mod = ~self + ;/ + ;/ + + /; _create_methods (~uint8 name) [~Module] + utils.Vector n + n.from_cstr("_#\0") + n.push_cstr(name) + + Module m + m._init() + m.name = n.as_cstr() + m.e = self.e + self.subs.push(~m) + + int i = self.subs.count - 1 + ~Module out = self.subs.get(i) + return out + ;/ + + /; is_method [bool] + /; if (utils.strlen(self.name) < 3) + return false + ;/ + + ~uint8 name = self.name + + /; if (name{0} !== '_') + return false + ;; else if (name{1} !== '#') + return false + ;; else if (name{2} == '#') + return false + ;/ + + return true + ;/ + + /; related_type [~Struct] + /; if (self.is_method() == false) + return NULL + ;/ + + ~Module m = self.parent + ~Module match = ~self + ~Struct s + /; loop (int i = 0; i < m`.structs.count) [i++] + s = m`.structs.get(i) + /; if (s`.methods == match) + return s + ;/ + ;/ + + _printf("COMPILER ERROR: UNABLE to find a sutable struct for module \"\0") + _printf(self.name) + _printf("\"\n\0") + return NULL + ;/ + + /; _from_tree (~parse.Node mod, ~CompBuf buf) + ~parse.Node sub + /; loop (int i = 0; i < mod`.sub.count) [i++] + sub = mod`.sub.get(i) + + # TODO: Enums + + /; if (sub`._type == parse.NTYPE_MODULE) + Module m + m.init(sub, buf) + self.subs.push(~m) + ;; else if (sub`._type == parse.NTYPE_EXPORT) + Module m + m.init(sub, buf) + self.subs.push(~m) + ;; else if (sub`._type == parse.NTYPE_STRUCT) + ~Module m = self._create_methods(sub`.data) + Struct s + s.init(sub) + self.structs.push(~s) + ;; else if (sub`._type == parse.NTYPE_FUNCTION) + Function f + f.init(sub) + self.funcs.push(~f) + ;; else if (sub`._type == parse.NTYPE_DECL) + self._decl(sub) + ;; else if (sub`._type == parse.NTYPE_ASM) + buf`.add_h(sub`.data) + buf`.add_h("\n\0") + ;/ + ;/ + ;/ + + /; _method_id (~uint8 id) [utils.Vector] + utils.Vector tmp + tmp.init(1) + utils.Vector out + out.init(8) + + ~uint8 str + + /; loop (id` !== 0) [id++] + /; if (id` == '.') + str = tmp.as_cstr() + out.push(~str) + tmp.init(1) + ;; else + tmp.push(id) + ;/ + ;/ + + str = tmp.as_cstr() + ~uint8 last = utils.stradd("_#\0", str) + _delete(str) + out.push(~last) + + return out + ;/ + + /; _method_id_end(~utils.Vector v) + ~~uint8 str + /; loop (int i = 0; i < v`.count) [i++] + str = v`.get(i) + _delete(str`) + ;/ + v`.end() + ;/ + + /; _collect_methods(~parse.Node m) + ~parse.Node sub + /; loop (int i = 0; i < m`.sub.count) [i++] + sub = m`.sub.get(i) + + /; if (sub`._type == parse.NTYPE_FUNCTION) + Function f + f.init(sub) + # Make function aware that it is in a method block + f.m = true + f.mod = ~self + self.funcs.push(~f) + ;/ + ;/ + ;/ + + /; collect_methods (~parse.Node mod) + ~parse.Node sub + /; loop (int i = 0; i < mod`.sub.count) [i++] + sub = mod`.sub.get(i) + /; if (sub`._type == parse.NTYPE_METHOD) + utils.Vector id = self._method_id(sub`.data) + ~Module m = self.find(SEARCH_SUB, ~id) + /; if (m !== NULL) + _printf("Found module for method\n\0") + m`._collect_methods(sub) + ;; else + _printf("Failed to find module for method\n\0") + ;/ + _method_id_end(~id) + ;; else if (sub`._type == parse.NTYPE_MODULE) + ~Module m = self._find_sub(sub`.data) + /; if (m !== NULL) + m`.collect_methods(sub) + ;; else + _printf("Failed to find module for method\n\0") + ;/ + ;; else if (sub`._type == parse.NTYPE_EXPORT) + ~Module m = self._find_sub(sub`.data) + /; if (m !== NULL) + m`.collect_methods(sub) + ;; else + _printf("Failed to find module for method\n\0") + ;/ + ;/ + ;/ + ;/ + + /; _decl (~parse.Node decl) + /; if (decl`.sub.count < 1) + return + ;/ + + ~parse.Node tn = decl`.sub.get(0) + /; if (tn`._type !== parse.NTYPE_TYPE) + # Type must be first + return + ;/ + + ~parse.Node id + /; loop (int i = 1; i < decl`.sub.count) [i++] + id = decl`.sub.get(i) + /; if (id`._type == parse.NTYPE_ID) + # Add a new variable to the list + Var v + v.init(tn, id) + self.vars.push(~v) + ;/ + ;/ + ;/ + + /; compile (~CompBuf cb) + # First, since all the types are in place, we need to size all of them. + self._size_structs() + + # Finally, write all functions to the code section and globals to the data section + self._compile(cb) + ;/ + + /; _size_structs + ~Struct s + /; loop (int i = 0; i < self.structs.count) [i++] + s = self.structs.get(i) + /; if (s`.size == 0) + s`._compute_size() + ;/ + ;/ + + ~Var v + /; loop (int i = 0; i < self.vars.count) [i++] + v = self.vars.get(i) + v`._resolve_type(~self) + ;/ + + ~Function f + /; loop (int i = 0; i < self.funcs.count) [i++] + f = self.funcs.get(i) + f`._resolve_type(~self) + ;/ + + ~Module m + /; loop (int i = 0; i < self.subs.count) [i++] + m = self.subs.get(i) + m`._size_structs() + ;/ + ;/ + + /; _compile (~CompBuf cb) + # Static compile all global variables + ~Var v + /; loop (int i = 0; i < self.vars.count) [i++] + v = self.vars.get(i) + v`._static_compile(cb) + ;/ + + # Write function to code section + ~Function f + /; loop (int i = 0; i < self.funcs.count) [i++] + f = self.funcs.get(i) + f`._compile(~self, cb) + ;/ + + # Recurse + ~Module m + /; loop (int i = 0; i < self.subs.count) [i++] + m = self.subs.get(i) + m`._compile(cb) + ;/ + ;/ + + # + # Functions to search sub-modules + # + + /; find (int stype, ~utils.Vector key) [~void] + /; if (key`.count < 1) + return NULL + ;/ + return self._find(stype, key, 0) + ;/ + + /; _find (int stype, ~utils.Vector key, int lvl) [~void] + + /; if ((lvl + 1) < key`.count) + ~Module m + ~~uint8 str = key`.get(lvl) + + /; loop (int i = 0; i < self.subs.count) [i++] + m = self.subs.get(i) + /; if (utils.strcmp(str`, m`.name) == true) + ~void v = m._find(stype, key, lvl + 1) + /; if (v !== NULL) + return v + ;/ + ;/ + ;/ + ;; else + ~~uint8 str = key`.get(key`.count - 1) + ~void out = NULL + /; if (stype == SEARCH_VAR) + out = self._find_var(str`) + ;; else if (stype == SEARCH_STRUCT) + out = self._find_struct(str`) + ;; else if (stype == SEARCH_FUNC) + out = self._find_func(str`) + ;; else if (stype == SEARCH_SUB) + out = self._find_sub(str`) + ;/ + + /; if (out !== NULL) + return out + ;/ + ;/ + + /; if (lvl == 0 && self.parent !== NULL) + return self.parent`._find(stype, key, 0) + ;/ + + return NULL + ;/ + + /; _find_var (~uint8 name) [~void] + ~Var v + /; loop (int i = 0; i < self.vars.count) [i++] + v = self.vars.get(i) + /; if (utils.strcmp(name, v`.name) == true) + return v + ;/ + ;/ + return NULL + ;/ + + /; _find_struct (~uint8 name) [~void] + ~Struct s + /; loop (int i = 0; i < self.structs.count) [i++] + s = self.structs.get(i) + /; if (utils.strcmp(name, s`.name) == true) + return s + ;/ + ;/ + return NULL + ;/ + + /; _find_func (~uint8 name) [~void] + ~Function f + /; loop (int i = 0; i < self.funcs.count) [i++] + f = self.funcs.get(i) + /; if (utils.strcmp(name, f`.name) == true) + return f + ;/ + ;/ + return NULL + ;/ + + /; _find_sub (~uint8 name) [~void] + ~Module m + /; loop (int i = 0; i < self.subs.count) [i++] + m = self.subs.get(i) + /; if (utils.strcmp(name, m`.name) == true) + return m + ;/ + ;/ + return NULL + ;/ + + /; print + self._print(0) + ;/ + + /; _print (int indent) + + _indent(indent) + _printf("{ Module : \0") + _printf(self.name) + _printf("\n\0") + + ~Var v + /; loop (int i = 0; i < self.vars.count) [i++] + v = self.vars.get(i) + v`._print(indent + 1) + ;/ + + ~Struct s + /; loop (int i = 0; i < self.structs.count) [i++] + s = self.structs.get(i) + s`._print(indent + 1) + ;/ + + ~Function f + /; loop (int i = 0; i < self.funcs.count) [i++] + f = self.funcs.get(i) + f`._print(indent + 1) + ;/ + + ~Module m + /; loop (int i = 0; i < self.subs.count) [i++] + m = self.subs.get(i) + m`._print(indent + 1) + ;/ + + _indent(indent) + _printf("}\n\0") + ;/ + + /; end + _delete(self.name) + + ~Var v + /; loop (int i = 0; i < self.vars.count) [i++] + v = self.vars.get(i) + v`.end() + ;/ + self.vars.end() + + ~Struct s + /; loop (int i = 0; i < self.structs.count) [i++] + s = self.structs.get(i) + s`.end() + ;/ + self.structs.end() + + ~Function f + /; loop (int i = 0; i < self.funcs.count) [i++] + f = self.funcs.get(i) + f`.end() + ;/ + self.funcs.end() + + ~Module m + /; loop (int i = 0; i < self.subs.count) [i++] + m = self.subs.get(i) + m`.end() + ;/ + self.subs.end() + ;/ + +;/ + diff --git a/tnslc/compile/scope.tnsl b/tnslc/compile/scope.tnsl new file mode 100644 index 0000000..569c48a --- /dev/null +++ b/tnslc/compile/scope.tnsl @@ -0,0 +1,752 @@ +struct Scope { + ~uint8 name, + ~Module mod, + ~CompBuf cb, + + ~Scope parent, + + utils.Vector vars, tmps, + + # Used for generating unique sub-scope labels + int unique +} + +/; _recursive_mod_name(~Module mod, ~utils.Vector vec) + ~Module p = mod`.parent + /; if (p !== NULL) + _recursive_mod_name(p, vec) + /; if (vec`.count !== 0) + vec`.push_char('.') + ;/ + ;/ + vec`.push_cstr(mod`.name) +;/ + +/; _recursive_scope_name(~Scope s, ~utils.Vector vec) + ~void p = s`.parent + /; if (p == NULL) + ~void m = s`.mod + /; if (m !== NULL) + _recursive_mod_name(m, vec) + /; if (vec`.count > 0) + vec`.push_char('.') + ;/ + ;/ + ;; else + _recursive_scope_name(p, vec) + ;/ + vec`.push_cstr(s`.name) +;/ + +/; method Scope + /; init (~Module mod, ~CompBuf cb, ~uint8 name) + self.name = utils.strcpy(name) + self.mod = mod + self.cb = cb + self.parent = NULL + self.unique = 0 + + Var v + self.vars.init(len v) + self.tmps.init(len v) + ;/ + + /; print + _printf("==== SCOPE DATA ====\n\0") + + _printf("Scope label: \0") + ~uint8 lab = self.base_label() + _printf(lab) + _printf("\n\n\0") + _delete(lab) + + _printf("Pre-tracked vars:\n\0") + ~Var v + /; loop (int i = 0; i < self.vars.count) [i++] + v = self.vars.get(i) + v`._print(0) + ;/ + + _printf("====================\n\0") + ;/ + + /; end + _delete(self.name) + + ~Var v + /; loop (int i = 0; i < self.vars.count) [i++] + v = self.vars.get(i) + v`.end() + ;/ + self.vars.end() + + /; loop (int i = 0; i < self.tmps.count) [i++] + v = self.tmps.get(i) + v`.end() + ;/ + self.tmps.end() + ;/ + + # + # Make variables + # + + /; _next_reg_slot [int] + int out = 11 + /; if (self.parent !== NULL) + out = self.parent`._next_reg_slot() + ;/ + + /; if (out < 0) + return out + ;/ + + ~Var v + /; loop (int i = 0; i < self.vars.count) [i++] + v = self.vars.get(i) + /; if (v`.loc > 1) + out++ + /; if (out > 16) + out = 0 + out = out - 1 + return out + ;/ + ;/ + ;/ + + return out + ;/ + + /; _next_tmp_slot [int] + int out = 2 + /; if (self.parent !== NULL) + out = self.parent`._next_tmp_slot() + ;/ + + /; if (out < 0) + return out + ;/ + + ~Var v + /; loop (int i = 0; i < self.tmps.count) [i++] + v = self.tmps.get(i) + /; if (v`.loc > 1) + out++ + /; if (out > 4) + out = 0 + out = out - 1 + return out + ;/ + ;/ + ;/ + + return out + ;/ + + /; _next_stack_slot [int] + int out = 0 + out = out - 56 + /; if (self.parent !== NULL) + out = self.parent`._next_stack_slot() + ;/ + + ~Var v + /; loop (int i = 0; i < self.vars.count) [i++] + v = self.vars.get(i) + /; if (v`.loc < 0 && v`.offset !== 0) + int off = v`.offset + /; if (off < out) + out = off + ;/ + ;/ + ;/ + + /; loop (int i = 0; i < self.tmps.count) [i++] + v = self.tmps.get(i) + /; if (v`.loc < 0 && v`.offset !== 0) + int off = v`.offset + /; if (off < out) + out = off + ;/ + ;/ + ;/ + + return out + ;/ + + /; mk_aware (~Var v) + Var mk = v`.copy() + mk.offset = 0 + + /; if (mk.loc > 0) + mk.loc = 1 + ;/ + + self.vars.push(~mk) + ;/ + + /; mk_aware_node (~parse.Node n) + ~parse.Node sub + ~parse.Node tp = NULL + /; loop (int i = 0; i < n`.sub.count) [i++] + sub = n`.sub.get(i) + /; if (sub`._type == parse.NTYPE_TYPE) + tp = sub + ;; else if (sub`._type == parse.NTYPE_ID) + /; if (tp == NULL) + _printf("COMPILER ERROR: Should have type node before first id in decl/dlist node\n\0") + return + ;/ + + # Part 1: Add var + Var v + v.init(tp, sub) + v._resolve_type(self.mod) + + # Compute weather we can put it in a register + /; if (v.regable() == true) + v.loc = 1 + ;; else + v.loc = 0 + v.loc = v.loc - 1 + ;/ + v.offset = 0 + + self.vars.push(~v) + + # Part 2: Compute via value (if exists) + /; if (sub`.sub.count > 0) + sub = sub`.sub.get(0) + /; if (sub`._type == parse.NTYPE_VALUE) + self.precheck_stmt(sub) + ;/ + ;/ + ;/ + ;/ + ;/ + + /; try_move (~parse.Node n) + /; loop (n`._type == parse.NTYPE_PRE_OP) + /; if (n`.sub.count < 1) + return + ;/ + n = n`.sub.get(0) + ;/ + + /; if (n`._type == parse.NTYPE_ID) + ~Var to_move = self._find_var(n`.data) + /; if (to_move == NULL) + return + ;/ + + /; if (to_move`.loc > 0) + int negative = 0 + to_move`.loc = negative - 1 + ;/ + ;/ + ;/ + + /; precheck_stmt (~parse.Node n) + /; if (n`._type == parse.NTYPE_PRE_OP) + /; if (utils.strcmp(n`.data, "~\0") == true) + self.try_move(n) + return + ;/ + ;/ + + ~parse.Node sub + /; loop (int i = 0; i < n`.sub.count) [i++] + sub = n`.sub.get(i) + self.precheck_stmt(sub) + ;/ + ;/ + + /; _find_var (~uint8 name) [~Var] + ~Var v + /; loop (int i = 0; i < self.vars.count) [i++] + v = self.vars.get(i) + /; if (utils.strcmp(v`.name, name) == true) + return v + ;/ + ;/ + + /; if (self.parent !== NULL) + return self.parent`.find_var(name) + ;/ + + return NULL + ;/ + + /; find_var (~uint8 name) [~Var] + ~Var out = self._find_var(name) + + /; if (out == NULL) + return NULL + ;/ + + /; if (out`.loc > 1 || out`.offset !== 0) + return out + ;/ + + _printf("ERROR: Found variable \"\0") + _printf(name) + _printf("\" but it doesn't look like it's been initialized yet!\n\0") + _print_num(" Loc: %d\n\0", out`.loc) + _print_num(" Offset: %d\n\0", out`.offset) + return NULL + ;/ + + /; mk_var (~Var src) [~Var] + ~Var v = self._find_var(src`.name) + + /; if (v == NULL) + return NULL + ;/ + + int tmp = 0 + /; if (v`.loc == 1) + int tmp = self._next_reg_slot() + v`.loc = tmp + /; if (v`.loc + 1 == 0) + tmp = self._next_stack_slot() + int sz = v`.actual_size() + v`.offset = tmp - sz + ;/ + ;; else if (v`.loc + 1 == 0) + /; if (v`.offset == 0) + tmp = self._next_stack_slot() + int sz = v`.actual_size() + v`.offset = tmp - sz + ;/ + ;/ + + /; if (v`.loc < 0) + int stk = v`.actual_size() + ~uint8 stk_str = utils.int_to_str(stk) + self.cb`.add_c(" sub rsp, \0") + self.cb`.add_c(stk_str) + self.cb`.add_c(" ; Create stack space for local variable \0") + self.cb`.add_c(v`.name) + self.cb`.add_c("\n\0") + _delete(stk_str) + ;/ + + return v + ;/ + + /; mk_set_var (~Var src) [~Var] + ~Var v = self.mk_var(src) + + /; if (v == NULL) + return NULL + ;/ + + /; if (v`.is_ref() == true) + int rc = v`.max_ref() + rc = rc - 1 + v`.set_ref(self.cb, src, rc) + ;; else + v`.set(self.cb, src) + ;/ + + return v + ;/ + + # Make a temp variable in register or stack for use in computations + /; mk_tmp (~Var base) [Var] + Var v = base`.copy() + v.offset = 0 + + int tmp = 0 + /; if (v.regable() == true) + int tmp = self._next_tmp_slot() + v.loc = tmp + /; if (v.loc + 1 == 0) + tmp = self._next_stack_slot() + int sz = v.actual_size() + v.offset = tmp - sz + ;; else if (v.loc > 2) + v.loc = v.loc + 6 + ;/ + ;; else + int loc = 0 + loc = loc - 1 + v.loc = loc + tmp = self._next_stack_slot() + int sz = v.actual_size() + v.offset = tmp - sz + ;/ + + /; if (v.loc < 0) + int stk = v.actual_size() + ~uint8 stk_str = utils.int_to_str(stk) + self.cb`.add_c(" sub rsp, \0") + self.cb`.add_c(stk_str) + self.cb`.add_c(" ; Create stack space for tmp variable\n\0") + _delete(stk_str) + ;/ + + self.tmps.push(~v) + + return v.copy() + ;/ + + # Make a temp variable on the stack for use in computations + /; mk_tmp_stack (~Var base) [Var] + Var v = base`.copy() + v.offset = 0 + + int loc = 0 + loc = loc - 1 + v.loc = loc + int tmp = self._next_stack_slot() + int sz = v.actual_size() + v.offset = tmp - sz + + ~uint8 stk_str = utils.int_to_str(sz) + self.cb`.add_c(" sub rsp, \0") + self.cb`.add_c(stk_str) + self.cb`.add_c(" ; Create stack space for tmp variable\n\0") + _delete(stk_str) + + self.tmps.push(~v) + + return v.copy() + ;/ + + /; _get_reg_tmp (~utils.Vector vec) + /; if (self.parent !== NULL) + ~Scope p = self.parent + p`._get_reg_tmp(vec) + ;/ + + ~Var tmp + /; loop (int i = 0; i < self.tmps.count) [i++] + tmp = self.tmps.get(i) + /; if (tmp`.loc > 0) + vec`.push(tmp) + ;/ + ;/ + ;/ + + /; save_caller_tmp [Var] + Var tmp + utils.Vector tmps + tmps.init(len tmp) + + self._get_reg_tmp(~tmps) + + /; if (tmps.count == 0) + tmps.end() + return tmp + ;/ + + ~Var to_save + /; loop (int i = 0; i < tmps.count) [i++] + to_save = tmps.get(i) + tmp = self.mk_tmp_stack(to_save) + + /; if (tmp.is_ref() == true) + # Handle ref + Var ref_val = to_save`.take_ptr(self.cb, 1) + Var ref_set = tmp.take_ptr(self.cb, 2) + ref_set.set(self.cb, ~ref_val) + ref_val.end() + ref_set.end() + ;; else + tmp.set(self.cb, to_save) + ;/ + + /; if (i < tmps.count - 1) + tmp.end() + ;/ + ;/ + tmps.end() + + return tmp + ;/ + + /; restore_caller_tmp (~Var handle) + Var tmp + utils.Vector tmps + tmps.init(len tmp) + + self._get_reg_tmp(~tmps) + + /; if (tmps.count == 0) + return + ;/ + + int end_idx = 0 + ~Var tmp + /; loop (int i = 0; i < self.tmps.count) [i++] + tmp = self.tmps.get(i) + /; if (tmp`.offset == handle`.offset) + /; if (tmp`.loc == handle`.loc) + end_idx = i + 1 + i = self.tmps.count + ;/ + ;/ + ;/ + + ~Var to_set + /; loop (int i = 0; i < tmps.count) [i++] + int idx = i + end_idx - tmps.count + to_set = tmps.get(i) + tmp = self.tmps.get(idx) + + /; if (to_set`.is_ref() == true) + # Handle ref + Var ref_val = tmp`.take_ptr(self.cb, 1) + Var ref_set = to_set`.take_ptr(self.cb, 2) + ref_set.set(self.cb, ~ref_val) + ref_val.end() + ref_set.end() + ;; else + to_set`.set(self.cb, tmp) + ;/ + ;/ + + handle`.end() + ;/ + + # Free n tmp variables (starts from most recently created) + # If you want to generate code to set the stack pointer then + # set code to true + /; free_tmp (int tmps, bool code) + /; if (self.tmps.count < tmps) + tmps = self.tmps.count + ;/ + + int stk_mv = 0 + + ~Var tmp + /; loop (int i = 1; i !> tmps) [i++] + tmp = self.tmps.get(self.tmps.count - i) + /; if (tmp`.loc < 0) + stk_mv = stk_mv + tmp`.actual_size() + ;/ + tmp`.end() + self.tmps.pop() + ;/ + + /; if (stk_mv > 0) + ~uint8 stk_mv_str = utils.int_to_str(stk_mv) + self.cb`.add_c(" add rsp, \0") + self.cb`.add_c(stk_mv_str) + self.cb`.add_c(" ; Free stack space (freeing tmp variables)\n\0") + _delete(stk_mv_str) + ;/ + ;/ + + # Returns true if the variable is in a temp spot + /; is_tmp (~Var v) [bool] + ~Var tmp + /; loop (int i = 0; i < self.tmps.count) [i++] + tmp = self.tmps.get(i) + /; if (tmp`.loc == v`.loc) + /; if (tmp`.offset == v`.offset) + return true + ;/ + ;/ + ;/ + return false + ;/ + + # Free all tmp variables created after and including the one provided + /; free_to (~Var tmp, bool code) + # TODO:DETERMINE HOW MANY TMPS + int tmps = 0 + tmps = tmps - 1 + ~Var v + /; loop (int i = 1; i !> self.tmps.count) [i++] + v = self.tmps.get(self.tmps.count - i) + /; if (v`.offset == tmp`.offset) + tmps = i + i = self.tmps.count + 1 + ;/ + ;/ + + /; if (tmps < 0) + ~uint8 scope_name = self.base_label() + int off = tmp`.offset + _printf("COMPILER ERROR: Unable to find temp to free to in scope \"\0") + _printf(scope_name) + _printf("\"\n\0") + _print_num(" Was looking for tmp with offset %d but could not find one.\n\0", off) + ;; else if (tmps > 0) + self.free_tmp(tmps, code) + ;/ + ;/ + + # Free all tmp variables created after the one provided + /; free_after (~Var tmp, bool code) + # TODO:DETERMINE HOW MANY TMPS + int tmps = 0 + tmps = tmps - 1 + ~Var v + /; loop (int i = 1; i !> self.tmps.count) [i++] + v = self.tmps.get(self.tmps.count - i) + /; if (v`.offset == tmp`.offset) + tmps = i - 1 + i = self.tmps.count + 1 + ;/ + ;/ + + /; if (tmps < 0) + ~uint8 scope_name = self.base_label() + int off = tmp`.offset + _printf("COMPILER ERROR: Unable to find temp to free to in scope \"\0") + _printf(scope_name) + _printf("\"\n\0") + _print_num(" Was looking for tmp with offset %d but could not find one.\n\0", off) + ;; else if (tmps > 0) + self.free_tmp(tmps, code) + ;/ + ;/ + + # + # Sub scope + # + + /; mk_sub (~uint8 name) [Scope] + Scope out + out.init(self.mod, self.cb, name) + out.parent = ~self + return out + ;/ + + # Generate a garantueed unique name for the sub scope, using + # the provided name as a base + /; gen_sub(~uint8 name) [Scope] + utils.Vector true_name + true_name.init(1) + + # Append a 'unique' number + ~uint8 u = utils.int_to_str(self.unique) + true_name.push_char('#') + true_name.push_cstr(u) + true_name.push_char('#') + true_name.push_cstr(name) + _delete(u) + + Scope out = self.mk_sub(true_name.as_cstr()) + true_name.end() + + # Inc for subsequent names + self.unique++ + + return out + ;/ + + # Get closest breakable scope + /; _closest_break [~Scope] + /; if (utils.ends_with(self.name, "#wrap\0")) + return ~self + ;; else if (utils.ends_with(self.name, "#loop\0")) + return ~self + ;/ + + /; if (self.parent == NULL) + return NULL + ;/ + + return self.parent`.closest_loop() + ;/ + + /; closest_break [~Scope] + /; if (self.parent == NULL) + return NULL + ;/ + + ~uint8 pname = self.parent`.name + /; if (utils.ends_with(pname, "#wrap\0")) + ~Scope par2 = self.parent`.parent + /; if (par2 !== NULL) + return par2`._closest_break() + ;/ + ;/ + + return self._closest_break() + ;/ + + # Get closest loop + /; closest_loop [~Scope] + /; if (utils.ends_with(self.name, "#loop\0")) + return ~self + ;/ + + /; if (self.parent == NULL) + return NULL + ;/ + + return self.parent`.closest_loop() + ;/ + + + # + # Label generation + # + + + /; _base_label [utils.Vector] + utils.Vector out + out.init(1) + + _recursive_scope_name(~self, ~out) + + return out + ;/ + + /; base_label [~uint8] + utils.Vector v = self._base_label() + return v.as_cstr() + ;/ + + /; place_base_label + ~uint8 bl = self.base_label() + self.cb`.add_c(bl) + self.cb`.add_c(":\n\0") + _delete(bl) + ;/ + + /; start_label [~uint8] + utils.Vector v = self._base_label() + v.push_cstr("#start\0") + return v.as_cstr() + ;/ + + /; place_start_label + ~uint8 sl = self.start_label() + self.cb`.add_c(sl) + self.cb`.add_c(":\n\0") + _delete(sl) + ;/ + + /; rep_label [~uint8] + utils.Vector v = self._base_label() + v.push_cstr("#rep\0") + return v.as_cstr() + ;/ + + /; place_rep_label + ~uint8 rl = self.rep_label() + self.cb`.add_c(rl) + self.cb`.add_c(":\n\0") + _delete(rl) + ;/ + + /; end_label [~uint8] + utils.Vector v = self._base_label() + v.push_cstr("#end\0") + return v.as_cstr() + ;/ + + /; place_end_label + ~uint8 el = self.end_label() + self.cb`.add_c(el) + self.cb`.add_c(":\n\0") + _delete(el) + ;/ +;/ + diff --git a/tnslc/compile/struct.tnsl b/tnslc/compile/struct.tnsl new file mode 100644 index 0000000..ed0b76a --- /dev/null +++ b/tnslc/compile/struct.tnsl @@ -0,0 +1,282 @@ + +struct Struct { + ~uint8 name, + ~Module methods, + utils.Vector members, + int size, + + ~parse.Node _up +} + +~uint8 PRIMITIVE_1 = "bool,uint8,int8\0" +~uint8 PRIMITIVE_2 = "uint16,int16\0" +~uint8 PRIMITIVE_4 = "uint32,int32,float32\0" +~uint8 PRIMITIVE_8 = "uint64,int64,float64,int,uint,float,void\0" + +/; _is_primitive(~uint8 str) [int] + /; if (parse._in_csv(PRIMITIVE_1, str) == true) + return 1 + ;; else if (parse._in_csv(PRIMITIVE_2, str) == true) + return 2 + ;; else if (parse._in_csv(PRIMITIVE_4, str) == true) + return 4 + ;; else if (parse._in_csv(PRIMITIVE_8, str) == true) + return 8 + ;/ + return 0 +;/ + +/; is_primitive (~parse.Node tn) [int] + /; if (tn`.sub.count < 1) + return 0 + ;/ + + ~parse.Node n = tn`.sub.get(0) + + # Check for pointer, array + /; if (n`._type == parse.NTYPE_PRE_OP) + return 8 + ;/ + + # Check for ref + n = tn`.sub.get(tn`.sub.count - 1) + /; if (n`._type == parse.NTYPE_POST_OP) + return 8 + ;/ + + int id = 0 - 1 + /; loop (int i = 0; i < tn`.sub.count) [i++] + n = tn`.sub.get(i) + /; if (n`._type == parse.NTYPE_ID) + /; if (id < 0) + id = i + ;; else + return 0 + ;/ + ;/ + ;/ + + /; if (id < 0) + return 0 + ;/ + + n = tn`.sub.get(id) + return _is_primitive(n`.data) +;/ + +/; _print_type(~parse.Node tn) + ~parse.Node n + bool seen_id = false + /; loop (int i = 0; i < tn`.sub.count) [i++] + + n = tn`.sub.get(i) + + /; if (n`._type == parse.NTYPE_ID) + /; if (seen_id == true) + _printf(".\0") + ;/ + _printf(n`.data) + seen_id = true + ;; else + return + ;/ + ;/ +;/ + +# Might be wrong +/; _type_is_ptr (~parse.Node n) [bool] + # Sanity + /; if (n`.sub.count < 1) + return false + ;/ + + ~parse.Node op = n`.sub.get(0) + /; if (op`._type == parse.NTYPE_PRE_OP) + return true + ;/ + + op = n`.sub.get(n`.sub.count - 1) + /; if (op`._type == parse.NTYPE_POST_OP) + return true + ;/ + + return false +;/ + +/; method Struct + + /; init (~parse.Node node) + self._up = node + self.size = 0 + self.methods = NULL + self.name = utils.strcpy(node`.data) + Var v + self.members.init(len v) + ;/ + + /; _print (int idt) + _indent(idt) + _printf("{ Struct : \0") + _printf(self.name) + _printf("\n\0") + + _indent(idt + 1) + _print_num("size: %d\n\0", self.size) + + _indent(idt + 1) + _printf("members:\n\0") + + ~Var v + /; loop (int i = 0; i < self.members.count) [i++] + v = self.members.get(i) + v`._print(idt + 2) + ;/ + + _indent(idt) + _printf("}\n\0") + ;/ + + /; add_member(~parse.Node tn, ~parse.Node id, int offset) + Var v + v.init(tn, id) + v._resolve_type(self.methods) + v.offset = offset + self.members.push(~v) + ;/ + + /; get_member(~uint8 name) [~Var] + ~Var out = NULL + + ~Var v + /; loop (int i = 0; i < self.members.count) [i++] + v = self.members.get(i) + /; if (utils.strcmp(v`.name, name) == true) + return v + ;/ + ;/ + + return out + ;/ + + /; _dlist [~parse.Node] + ~parse.Node up = self._up + ~parse.Node n + /; loop (int i = 0; i < up`.sub.count) [i++] + n = up`.sub.get(i) + /; if (n`._type == parse.NTYPE_DLIST) + return n + ;/ + ;/ + return NULL + ;/ + + /; _compute_size + /; if (self.size !== 0) + return + ;/ + + self.size = self.size - 1 + + int total = 0 + ~parse.Node up = self._dlist() + ~parse.Node tn = NULL + ~parse.Node n = NULL + int add_size = 0 + /; loop (int i = 0; i < up`.sub.count) [i++] + n = up`.sub.get(i) + + /; if (n`._type == parse.NTYPE_TYPE) + # Store for generating new variables + tn = n + add_size = self._compute_type_size(n) + /; if (add_size == 0) + return + ;/ + ;; else if (n`._type == parse.NTYPE_ID) + /; if (tn == NULL) + _printf("ERROR: Unable to find type when trying to create member for struct '\0") + _printf(self.name) + _printf("\n\0") + return + ;/ + self.add_member(tn, n, total) + total = total + add_size + ;/ + ;/ + + self.size = total + ;/ + + /; _compute_type_size(~parse.Node tn) [int] + ~Struct ft = self._find_type(tn) + + /; if (ft == NULL) + # Type not found + _printf("ERROR: Unable to find type '\0") + _print_type(tn) + _printf("' for use in struct '\0") + _printf(self.name) + _printf("'\n\0") + return 0 + ;/ + + /; if (_type_is_ptr(tn) == true) + return 8 + ;/ + + /; if (ft !== NULL && ft`.size == 0) + ft`._compute_size() + ;/ + + /; if (ft`.size < 0) + # Cyclical dependency + _printf("ERROR: Cyclic struct definition: '\0") + _printf(ft`.name) + _printf("' imported from '\0") + _printf(self.name) + _printf("'\n\0") + return 0 + ;/ + + return ft`.size + ;/ + + /; _find_type (~parse.Node tn) [~Struct] + + # Init vector of strings + utils.Vector sv + sv.init(8) + + ~uint8 str + ~parse.Node n + bool seen_id = false + + /; loop (int i = 0; i < tn`.sub.count) [i++] + n = tn`.sub.get(i) + /; if (n`._type == parse.NTYPE_ID) + str = n`.data + sv.push(~str) + seen_id = true + ;; else if (seen_id == true) + i = tn`.sub.count + ;/ + ;/ + + # Find struct and compute its size + ~Struct out = self.methods`.find(SEARCH_STRUCT, ~sv) + sv.end() + return out + ;/ + + /; end + _delete(self.name) + ~Var v + /; loop (int i = 0; i < self.members.count) [i++] + v = self.members.get(i) + v`.end() + ;/ + self.members.end() + ;/ + +;/ + diff --git a/tnslc/compile/tests_var.tnsl b/tnslc/compile/tests_var.tnsl new file mode 100644 index 0000000..a8fd573 --- /dev/null +++ b/tnslc/compile/tests_var.tnsl @@ -0,0 +1,248 @@ + +/; var_test_mov (~Module root, ~CompBuf buf) + ~Struct _int = root`._find_struct("int\0") + + Var a + Var b + + a._init(_int) + b._init(_int) + + buf`.add_c("; test: register to register move, both int\n\0") + + a.loc = 1 + b.loc = 2 + b.set(buf, ~a) + + buf`.add_c("; test: register to stack move\n\0") + + b.loc = b.loc - 3 + b.offset = b.offset - 56 + b.set(buf, ~a) + + buf`.add_c("; test: stack to register move\n\0") + + a.set(buf, ~b) + + buf`.add_c("; test: stack to stack move\n\0") + + a.loc = a.loc - 2 + a.offset = 56 + b.set(buf, ~a) + + buf`.add_c("\n\0") + + a.end() + b.end() +;/ + +/; var_test_movsx (~Module root, ~CompBuf buf) + ~Struct _int = root`._find_struct("int\0") + ~Struct _int32 = root`._find_struct("int32\0") + ~Struct _int8 = root`._find_struct("int8\0") + + Var a + Var b + Var c + + a._init(_int) + b._init(_int32) + c._init(_int8) + + a.loc = 1 + b.loc = 2 + c.loc = 3 + + buf`.add_c("; test: mov with extension - int8 to int32\n\0") + b.set(buf, ~c) + buf`.add_c("; test: mov with extension - int8 to int\n\0") + a.set(buf, ~c) + buf`.add_c("; test: mov with extension - int32 to int\n\0") + a.set(buf, ~b) + + buf`.add_c("; test: mov with extension - int8 to int32 (stack to reg)\n\0") + c.loc = c.loc - 4 + c.offset = 4 + b.set(buf, ~c) + buf`.add_c("; test: mov with extension - int8 to int32 (reg to stack)\n\0") + c.loc = 3 + c.offset = 0 + b.loc = b.loc - 3 + b.offset = 5 + b.set(buf, ~c) + buf`.add_c("; test: mov with extension - int8 to int32 (stack to stack)\n\0") + c.loc = c.loc - 4 + c.offset = 4 + b.set(buf, ~c) + + b.loc = 2 + b.offset = 0 + c.loc = 3 + c.offset = 0 + + buf`.add_c("; test: mov with extension - int8 to int (stack to reg)\n\0") + c.loc = c.loc - 4 + c.offset = 4 + a.set(buf, ~c) + buf`.add_c("; test: mov with extension - int8 to int (reg to stack)\n\0") + c.loc = 3 + c.offset = 0 + a.loc = a.loc - 2 + a.offset = 5 + a.set(buf, ~c) + buf`.add_c("; test: mov with extension - int8 to int (stack to stack)\n\0") + c.loc = c.loc - 4 + c.offset = 4 + a.set(buf, ~c) + + a.loc = 1 + a.offset = 0 + c.loc = 3 + c.offset = 0 + + buf`.add_c("; test: mov with extension - int32 to int (stack to reg)\n\0") + b.loc = b.loc - 3 + b.offset = 4 + a.set(buf, ~b) + buf`.add_c("; test: mov with extension - int32 to int (reg to stack)\n\0") + b.loc = 2 + b.offset = 0 + a.loc = a.loc - 2 + a.offset = 5 + a.set(buf, ~b) + buf`.add_c("; test: mov with extension - int32 to int (stack to stack)\n\0") + b.loc = b.loc - 3 + b.offset = 4 + a.set(buf, ~b) + + + buf`.add_c("\n\0") + a.end() + b.end() + c.end() +;/ + +/; var_test_movzx (~Module root, ~CompBuf buf) + ~Struct _uint = root`._find_struct("uint\0") + ~Struct _uint32 = root`._find_struct("uint32\0") + ~Struct _uint8 = root`._find_struct("uint8\0") + + Var a + Var b + Var c + + a._init(_uint) + b._init(_uint32) + c._init(_uint8) + + a.loc = 1 + b.loc = 2 + c.loc = 3 + + buf`.add_c("; test: mov with extension - uint8 to uint32\n\0") + b.set(buf, ~c) + buf`.add_c("; test: mov with extension - uint8 to uint\n\0") + a.set(buf, ~c) + buf`.add_c("; test: mov with extension - uint32 to uint\n\0") + a.set(buf, ~b) + + buf`.add_c("; test: mov with extension - uint8 to uint32 (stack to reg)\n\0") + c.loc = c.loc - 4 + c.offset = 4 + b.set(buf, ~c) + buf`.add_c("; test: mov with extension - uint8 to uint32 (reg to stack)\n\0") + c.loc = 3 + c.offset = 0 + b.loc = b.loc - 3 + b.offset = 5 + b.set(buf, ~c) + buf`.add_c("; test: mov with extension - uint8 to uint32 (stack to stack)\n\0") + c.loc = c.loc - 4 + c.offset = 4 + b.set(buf, ~c) + + b.loc = 2 + b.offset = 0 + c.loc = 3 + c.offset = 0 + + buf`.add_c("; test: mov with extension - uint8 to uint (stack to reg)\n\0") + c.loc = c.loc - 4 + c.offset = 4 + a.set(buf, ~c) + buf`.add_c("; test: mov with extension - uint8 to uint (reg to stack)\n\0") + c.loc = 3 + c.offset = 0 + a.loc = a.loc - 2 + a.offset = 5 + a.set(buf, ~c) + buf`.add_c("; test: mov with extension - uint8 to uint (stack to stack)\n\0") + c.loc = c.loc - 4 + c.offset = 4 + a.set(buf, ~c) + + a.loc = 1 + a.offset = 0 + c.loc = 3 + c.offset = 0 + + buf`.add_c("; test: mov with extension - uint32 to uint (stack to reg)\n\0") + b.loc = b.loc - 3 + b.offset = 4 + a.set(buf, ~b) + buf`.add_c("; test: mov with extension - uint32 to uint (reg to stack)\n\0") + b.loc = 2 + b.offset = 0 + a.loc = a.loc - 2 + a.offset = 5 + a.set(buf, ~b) + buf`.add_c("; test: mov with extension - uint32 to uint (stack to stack)\n\0") + b.loc = b.loc - 3 + b.offset = 4 + a.set(buf, ~b) + + + buf`.add_c("\n\0") + a.end() + b.end() + c.end() +;/ + +# Test moving references +/; var_test_movref (~Module root, ~CompBuf buf) + ~Struct _int = root`._find_struct("int\0") + ~Struct _int32 = root`._find_struct("int32\0") +;/ + +# Generate a test_struct type for use in testing struct moves and sub-refs +/; _gen_test_struct(~Module m) + ~Module mds = m`._create_methods("test\0") + + Var t + + Struct s + s.size = 9 + s.methods = NULL + s.members.init(len t) + s.name = utils.strcpy("test\0") + s._up = NULL + + m`.structs.push(~s) +;/ + +/; var_tests (~CompBuf buf) + # Setup dummy root + Module mod + mod._init() + mod.name = utils.strcpy("\0") + mod.e = true + _gen_prims(~mod) + _gen_test_struct(~mod) + + var_test_mov(~mod, buf) + var_test_movsx(~mod, buf) + var_test_movzx(~mod, buf) + var_test_movref(~mod, buf) + + mod.end() +;/ diff --git a/tnslc/compile/type.tnsl b/tnslc/compile/type.tnsl deleted file mode 100644 index 474f705..0000000 --- a/tnslc/compile/type.tnsl +++ /dev/null @@ -1,360 +0,0 @@ -struct Variable { - ~uint8 name, - ~Type _type, - utils.Vector ptr -} - -/; method Variable - /; init (~uint8 name) - self.name = name - self.ptr.init(8) - ;/ - - /; add_ptr(uint ptp) - self.ptr.push(~ptp) - ;/ - - /; get_ptr [uint] - /; if (self.ptr.count == 0) - return 0 - ;/ - ~uint p = self.ptr.get(self.ptr.count - 1) - return p` - ;/ - - /; pop_ptr [uint] - /; if (self.ptr.count == 0) - return 0 - ;/ - ~uint p = self.ptr.get(self.ptr.count - 1) - uint out = p` - self.ptr.pop() - return out - ;/ - - /; end - _delete(self.name) - self.ptr.end() - ;/ -;/ - -struct Type { - ~uint8 name, - uint size, - utils.Vector vars, - ~Module methods, -} - -/; method Type - /; init(~uint8 name) - self.name = name - Variable tmp - self.vars.init(len tmp) - ;/ - - /; add_var (~Variable v) - self.vars.push(v) - ;/ - - /; end - _delete(self.name) - /; loop (int i = 0; i < self.vars.count) [i++] - ~Variable v = self.vars.get(i) - v`.end() - ;/ - self.vars.end() - ;/ -;/ - -struct Function { - ~uint8 - name, - ~parse.Node - body, - utils.Vector - inputs, - outputs -} - -/; method Function - /; init (~uint8 name) - self.name = name - Variable vtmp - ~uint i - self.inputs.init(len vtmp) - self.outputs.init(len i) - ;/ - - /; add_input (~Variable v) - self.inputs.push(v) - ;/ - - /; add_output (~Type t) - self.outputs.push(~t) - ;/ - - /; end - _delete(self.name) - - /; loop (int i = 0; i < self.inputs.count) [i++] - ~Variable v = self.inputs.get(i) - v`.end() - ;/ - self.inputs.end() - - self.outputs.end() - ;/ -;/ - -struct Enum { - ~uint8 name, - ~Type _type, - utils.Vector vals -} - -/; method Enum - /; init (~uint8 name) - self.name = name - Variable vtmp - self.vals.init(len vtmp) - ;/ - - /; end - _delete(self.name) - /; loop (int i = 0; i < self.vals.count) [i++] - ~Variable v = self.vals.get(i) - v`.end() - ;/ - self.vals.end() - ;/ -;/ - -struct Module { - ~uint8 name, - ~Module parent, - bool exp, - utils.Vector - sub, - vars, - types, - funcs, - enums - -} - -uint8 MOD_FIND_SUB = 0 -uint8 MOD_FIND_VAR = 1 -uint8 MOD_FIND_TYP = 2 -uint8 MOD_FIND_FUN = 3 -uint8 MOD_FIND_ENM = 4 - -/; method Module - /; init (~uint8 name) - self.name = name - Module mtmp - Variable vtmp - Type ttmp - Function ftmp - Enum etmp - self.sub.init(len mtmp) - self.vars.init(len vtmp) - self.types.init(len ttmp) - self.funcs.init(len ftmp) - self.enums.init(len etmp) - ;/ - - /; update_children - /; loop (int i = 0; i < self.sub.count) [i++] - ~Module s = self.sub.get(i) - s`.parent = ~self - ;/ - ;/ - - /; add_sub(~Module m) [~Module] - self.sub.push(m) - /; loop (int i = 0; i < self.sub.count) [i++] - ~Module s = self.sub.get(i) - s`.update_children() - ;/ - return self.sub.get(self.sub.count - 1) - ;/ - - /; add_var (~Variable v) - self.vars.push(v) - ;/ - - /; add_type (~Type t) - self.types.push(t) - ;/ - - /; add_funcs (~Function f) - self.funcs.push(f) - ;/ - - /; add_enum (~Enum e) - self.enums.push(e) - ;/ - - /; _find_rec (utils.Artifact pth, uint8 typ, int sub) [~void] - /; if (sub !< pth.count) - return NULL - ;; else if ((sub + 1) < pth.count) - /; loop (int i = 0; i < self.sub.count) [i++] - ~Module m = self.sub.get(i) - /; if (utils.strcmp(pth.get(sub), m`.name) == true) - return _find_rec(pth, typ, sub + 1) - ;/ - ;/ - ;; else - /; if (typ == MOD_FIND_SUB) - /; loop (int i = 0; i < self.sub.count) [i++] - ~Module m = self.sub.get(i) - /; if (utils.strcmp(pth.get(sub), m`.name) == true) - return self.sub.get(i) - ;/ - ;/ - ;; else if (typ == MOD_FIND_VAR) - /; loop (int i = 0; i < self.vars.count) [i++] - ~Variable v = self.vars.get(i) - /; if (utils.strcmp(pth.get(sub), v`.name) == true) - return self.vars.get(i) - ;/ - ;/ - ;; else if (typ == MOD_FIND_TYP) - /; loop (int i = 0; i < self.types.count) [i++] - ~Type t = self.types.get(i) - /; if (utils.strcmp(pth.get(sub), t`.name) == true) - return self.types.get(i) - ;/ - ;/ - ;; else if (typ == MOD_FIND_FUN) - /; loop (int i = 0; i < self.funcs.count) [i++] - ~Function f = self.funcs.get(i) - /; if (utils.strcmp(pth.get(sub), f`.name) == true) - return self.funcs.get(i) - ;/ - ;/ - ;; else if (typ == MOD_FIND_ENM) - /; loop (int i = 0; i < self.enums.count) [i++] - ~Enum e = self.enums.get(i) - /; if (utils.strcmp(pth.get(sub), e`.name) == true) - return self.enums.get(i) - ;/ - ;/ - ;/ - ;/ - - /; if (self.parent == NULL || sub !== 0) - return NULL - ;/ - - return self.parent._find_rec(pth, typ, 0) - ;/ - - /; find (utils.Artifact pth, uint8 typ) [~void] - return _find_rec(pth, typ, 0) - ;/ - - /; end - _delete(self.name) - - /; loop (int i = 0; i < self.sub.count) [i++] - ~Module m = self.sub.get(i) - m`.end() - ;/ - self.sub.end() - - /; loop (int i = 0; i < self.vars.count) [i++] - ~Variable v = self.vars.get(i) - v`.end() - ;/ - self.vars.end() - - /; loop (int i = 0; i < self.types.count) [i++] - ~Type t = self.types.get(i) - t`.end() - ;/ - self.types.end() - - /; loop (int i = 0; i < self.funcs.count) [i++] - ~Function f = self.funcs.get(i) - f`.end() - ;/ - self.funcs.end() - - /; loop (int i = 0; i < self.enums.count) [i++] - ~Enum e = self.enums.get(i) - e`.end() - ;/ - self.enums.end() - ;/ -;/ - -{}~uint8 GEN_VAR_NAMES = { "int\0", "int8\0", "int16\0", "int32\0", "int64\0", "uint\0", "uint8\0", "uint16\0", "uint32\0", "uint64\0", "float\0", "float32\0", "float64\0", "vect\0", "bool\0", "void\0" } - -{}uint GEN_VAR_SIZES = { 8, 1, 2, 4, 8, 8, 1, 2, 4, 8, 8, 4, 8, 0, 1, 8} - -/; find_type(utils.Artifact a, ~parse.Node n) [~parse.Node] - return NULL -;/ - -/; transform_struct(~parse.Node n, ~Module parent) -;/ - -/; _tfn_mod_loop (~parse.Node n, ~Module m) - /; loop (int i = 0; i < n`.sub.count) [i++] - ~parse.Node s = n`.sub.get(i) - /; if (s`._type == parse.NTYPE_MODULE || s`._type == parse.NTYPE_EXPORT) - transform_module(s, m) - ;; else if (s`._type == parse.NTYPE_STRUCT) - transform_struct(s, m) - ;/ - ;/ -;/ - -/; transform_module (~parse.Node n, ~Module parent) - ~Module s = NULL - - /; loop (int i = 0; i < parent`.sub.count) [i++] - ~Module tmp = parent`.sub.get(i) - /; if (utils.strcmp(n`.data, tmp`.name) == true) - s = tmp - ;/ - ;/ - - ~int cmp = s - /; if (cmp == NULL) - Module out - out.init(utils.strcpy(n`.data)) - - /; if (n`._type == parse.NTYPE_EXPORT) - out.exp = true - ;; else - out.exp = false - ;/ - - s = parent`.add_sub(~out) - ;/ - - _tfn_mod_loop(n, s) -;/ - -/; _tfn_gen_default_types (~Module m) - /; loop (int i = 0; i < len GEN_VAR_NAMES) [i++] - Type t - t.init(utils.strcpy(GEN_VAR_NAMES{i})) - t.size = GEN_VAR_SIZES{i} - m`.add_type(~t) - ;/ -;/ - -/; transform_tree (~parse.Node n) [Module] - Module out - out.init(utils.strcpy(n`.data)) - out.exp = true - - _tfn_gen_default_types(~out) - _tfn_mod_loop(n, ~out) - - return out -;/ diff --git a/tnslc/compile/var.tnsl b/tnslc/compile/var.tnsl new file mode 100644 index 0000000..5f87cc7 --- /dev/null +++ b/tnslc/compile/var.tnsl @@ -0,0 +1,1822 @@ + +# Location enum +int VLOC_DATA = 2 +int VLOC_STCK = 1 +int VLOC_LITL = 0 + +# Should be -2 +int32 PTYPE_NONE = 2 +# Should be -1 +int32 PTYPE_PTR = 1 +int32 PTYPE_REF = 0 +# 1 Arr is ptr to arr, larger #s are static size arrs +int32 PTYPE_ARR = 1 + +~uint8 PRIM_CSV_BOO = "bool\0" +~uint8 PRIM_CSV_INT = "int,int8,int16,int32,int64,uint,uint8,uint16,uint32,uint64\0" +~uint8 PRIM_CSV_FLT = "float,float32,float64\0" + +# Should dispose of this constructed string +# 1-8 are ax, bx, cx, dx, si, di, sp, bp +# 9-16 are r8, r9, r10, r11, r12, r13, r14, r15 +# 17-32 are xmm0, xmm1, xmm2, ..., xmm15 +/; reg_string (int r, int size) [~uint8] + utils.Vector out + out.init(1) + uint8 add + + /; if (r < 9) + /; if (size == 4) + add = 'e' + out.push(~add) + ;; else if (size == 8) + add = 'r' + out.push(~add) + ;/ + + add = 'a' + /; if (r < 5) + add = add + r - 1 + ;; else if (r == 5 || r == 7) + add = 's' + ;; else if (r == 6) + add = 'd' + ;; else if (r == 8) + add = 'b' + ;/ + out.push(~add) + + /; if (r == 5 || r == 6) + add = 'i' + out.push(~add) + ;; else if (r == 7 || r == 8) + add = 'p' + out.push(~add) + ;; else if (size !== 1) + add = 'x' + out.push(~add) + ;/ + + /; if (size == 1) + add = 'l' + out.push(~add) + ;/ + ;; else if (r < 17) + add = 'r' + out.push(~add) + + ~uint8 num = utils.int_to_str(r - 1) + out.push_cstr(num) + _delete(num) + /; if (size == 1) + add = 'b' + out.push(~add) + ;; else if (size == 2) + add = 'w' + out.push(~add) + ;; else if (size == 4) + add = 'd' + out.push(~add) + ;/ + ;; else if (r < 33) + out.push_cstr("xmm\0") + ~uint8 num = utils.int_to_str(r - 17) + out.push_cstr(num) + _delete(num) + ;/ + + return out.as_cstr() +;/ + +# Valid value states: +# When loc is DATA value is in the data section and the name is actually the label +# When loc is STCK value is offset from rbp pointer using the offset +# When loc is 0 then offset represents a numeric literal +# When loc is positive then the variable exists in that register* +# Structs and references don't exactly exist in a register and so must +# be combined with the offset to get a resonable approximation + +# So when computing the location of a standard type... +# ...just load from the register +# ...Unless it's a ref in which case take into account the offset +# ...Or it's a DATA in which case load from rel label +# ...Or it's on the stack in which case load from +# ...Or it's a literal in which case just use the literal value + +# So when computing the location of a struct... +# ...Load as an offset from a register +# ...Unless it's on the stack (offset from rbp) +# ...Or it's in DATA (offset from rel label) +# ...Or it's a ref (lea first ref and then just load direct values from there) + +struct Var { + ~uint8 name, + ~Struct _type, + utils.Vector ptrc, + int loc, offset, + + ~parse.Node _tn, _id, + + # Used for globals + ~Module parent +} + +/; method Var + + ########################### + # Init and copy functions # + ########################### + + # Dummy init + /; _init (~Struct _type) + self.name = utils.strcpy("dummy\0") + self.ptrc.init(4) + self.loc = 0 + self.offset = 0 + self._type = _type + self.parent = NULL + self._tn = NULL + self._id = NULL + ;/ + + # Initial init function, requires type node and + # identifier node + /; init (~parse.Node tn, id) + self.name = utils.strcpy(id`.data) + self.ptrc.init(4) + self.loc = 0 + self.offset = 0 + self.parent = NULL + + self._tn = tn + self._id = id + ;/ + + # Deep copy the variable + /; copy [Var] + Var out = self.shallow_copy() + + /; loop (int i = 0; i < self.ptrc.count) [i++] + ~int32 p = self.ptrc.get(i) + out.ptrc.push(p) + ;/ + + return out + ;/ + + # A copy without the pointer chain + /; shallow_copy [Var] + Var out + /; if (self._tn !== NULL) + out.init(self._tn, self._id) + out._type = self._type + ;; else + out._init(self._type) + _delete(out.name) + out.name = utils.strcpy(self.name) + ;/ + out.loc = self.loc + out.offset = self.offset + return out + ;/ + + ############################# + # Variable inspection funcs # + ############################# + + # Get a pointer to the top of the pointer chain, returns + # null if the pointer chain is empty + /; top_ptrc [~int32] + # Sanity + /; if (self.ptrc.count < 1) + return NULL + ;/ + + ~int32 out = self.ptrc.get(self.ptrc.count - 1) + return out + ;/ + + /; is_ptrc (int idx, int32 ptype) [bool] + /; if (idx !< self.ptrc.count) + return false + ;/ + + idx = self.ptrc.count - idx + idx = idx - 1 + ~int32 chk = self.ptrc.get(idx) + return chk` == ptype + ;/ + + /; strip_refs + /; loop (self.is_ptrc(0, 0) == true) + self.ptr_pop() + ;/ + ;/ + + # Returns true if the variable is a reference + /; is_ref [bool] + return self.is_ptrc(0, 0) + ;/ + + # Returns true if two or more ref layers + /; double_ref [bool] + /; if (self.is_ref() == false) + return false + ;/ + return self.is_ptrc(1, 0) + ;/ + + # Ref level + /; max_ref [int] + int out = 0 + /; loop (int i = 0; i < self.ptrc.count) [i++] + /; if (self.is_ptrc(i, 0) == true) + out++ + ;; else + i = self.ptrc.count + ;/ + ;/ + return out + ;/ + + # Returnes true if the underlying type is a signed integer + /; is_signed [bool] + /; if (_is_primitive(self._type`.name) !== 0) + return self._type`.name{0} == 'i' + ;/ + return false + ;/ + + # Returns true if the variable is a pointer + /; is_ptr [bool] + ~int32 p = self.top_ptrc() + /; if (p == NULL) + return false + ;/ + + return p` < 0 + ;/ + + # Returns true if the variable is an array + /; is_arr [bool] + ~int32 p = self.top_ptrc() + /; if (p == NULL) + return false + ;/ + + return p` > 0 + ;/ + + # Whether the variable can be stored within a register + /; regable [bool] + ~int p + /; if (self.ptrc.count > 0) + return true + ;/ + return _is_primitive(self._type`.name) !== 0 + ;/ + + /; is_struct [bool] + # Check first if we are a pointer of some sort + ~int32 p + /; loop (int i = 0; i < self.ptrc.count) [i++] + p = self.ptrc.get(i) + /; if (p` !== 0) + return false + ;/ + ;/ + + return _is_primitive(self._type`.name) == 0 + ;/ + + # Compute and add the correct pointer chain value for an array + # type prefix + /; _arr_ptr(~parse.Node a) + int32 ptr = 1 + /; if (a`.sub.count > 0) + ~parse.Node l = a`.sub.get(0) + ptr = utils.cstr_to_int(l`.data) + /; if (ptr < 2) + return + ;/ + ;/ + self.ptrc.push(~ptr) + ;/ + + # The "actual size" of the variable (if we were to do a mov on it + # how much space would we need) + /; actual_size [uint] + /; if (self.ptrc.count > 0) + return 8 + ;; else if (self._type == NULL) + return 0 + ;/ + + return self._type`.size + ;/ + + /; type_size [uint] + /; loop (int i = 0; i < self.ptrc.count) [i++] + ~int32 p = self.ptrc.get(i) + /; if (p` !== 0) + return 8 + ;/ + ;/ + + return self._type`.size + ;/ + + /; find_method (~uint8 name) [~Function] + ~int32 p + /; loop (int i = 0; i < self.ptrc.count) [i++] + p = self.ptrc.get(i) + /; if (p` !== 0) + _printf("ERROR: Tried to find method \"\0") + _printf(name) + _printf("\" on a variable but we had a pointer in the chain\n\0") + return false + ;/ + ;/ + + ~Module mod = self._type`.methods + ~Function out = mod`._find_func(name) + return out + ;/ + + ##################################### + # Variable manipulation (comp time) # + ##################################### + + # Reverse the pointer chain + /; _reverse_ptrc + int max = self.ptrc.count / 2 + ~int32 l, r + /; loop (int i = 0; i < max) [i++] + l = self.ptrc.get(i) + r = self.ptrc.get(self.ptrc.count - (i + 1)) + int32 tmp = l` + l` = r` + r` = tmp + ;/ + ;/ + + # Sets up both the ptrc and the _type members, requires + # parent module for resolution of types + /; _resolve_type (~Module parent) + int idx = 0 + bool running = true + ~parse.Node t, _tn + _tn = self._tn + + # Pre-op pointer + /; loop (running == true) + /; if (idx !< _tn`.sub.count) + running = false + ;; else + t = _tn`.sub.get(idx) + /; if (t`._type == parse.NTYPE_PRE_OP) + /; if (utils.strcmp(t`.data, "~\0") == true) + int32 ptr = 0 + ptr = ptr - PTYPE_PTR + self.ptrc.push(~ptr) + ;; else + self._arr_ptr(t) + ;/ + ;; else + running = false + ;/ + ;/ + + /; if (running == true) + idx++ + ;/ + ;/ + + self._reverse_ptrc() + + # After pre-ops comes id + utils.Vector strv + strv.init(8) + running = true + /; loop (running == true) + /; if (idx !< _tn`.sub.count) + running = false + ;; else + t = _tn`.sub.get(idx) + /; if (t`._type == parse.NTYPE_ID) + ~uint8 str = t`.data + strv.push(~str) + ;; else + running = false + ;/ + ;/ + + /; if (running == true) + idx++ + ;/ + ;/ + + # Main type resolution + # TODO: FUNCTION POINTER + self._type = parent`.find(SEARCH_STRUCT, ~strv) + strv.end() + + # Post-op pointer + running = true + /; loop (running == true) + /; if (idx !< _tn`.sub.count) + running = false + ;; else + t = _tn`.sub.get(idx) + /; if (t`._type == parse.NTYPE_POST_OP) + int32 ptr = 0 + self.ptrc.push(~ptr) + ;/ + ;/ + + /; if (running == true) + idx++ + ;/ + ;/ + ;/ + + ###################### + # Static compilation # + ###################### + + /; _static_compile_arr(~parse.Node n, ~utils.Vector out, int depth) [~uint8] + return utils.strcpy("TODO\0") + ;/ + + /; _static_compile_ptr(~parse.Node n, ~utils.Vector out, int depth) [~uint8] + + /; if (n`._type == parse.NTYPE_VALUE) + n = n`.sub.get(0) + return self._static_compile_ptr(n, out, depth) + ;/ + + /; if (n`._type == parse.NTYPE_LITERAL) + + /; if (n`.data{0} == '"') + # Generate string + ~uint8 tmp = self._global_ptr() + out`.push_cstr(tmp) + out`.push_cstr(":\n\0") + _delete(tmp) + + + utils.Vector vout = utils.unquote_str(n`.data) + tmp = utils.int_to_str(vout.count) + out`.push_cstr(" dq \0") + out`.push_cstr(tmp) + out`.push_cstr("\n\0") + _delete(tmp) + + /; if (vout.count > 0) + out`.push_cstr(" db \0") + ;/ + + /; loop (int i = 1; i < vout.count) [i++] + ~uint8 uptr = vout.get(i - 1) + uint8 utmp = uptr` + tmp = utils.int_to_str(utmp) + out`.push_cstr(tmp) + out`.push_cstr(", \0") + _delete(tmp) + ;/ + + /; if (vout.count > 0) + int i = vout.count + ~uint8 uptr = vout.get(i - 1) + uint8 utmp = uptr` + tmp = utils.int_to_str(utmp) + out`.push_cstr(tmp) + _delete(tmp) + ;/ + + out`.push_cstr("\n\0") + + # Generate response text + vout.end() + tmp = self._global_ptr() + vout.from_cstr(" dq \0") + vout.push_cstr(tmp) + _delete(tmp) + + int idx = self.ptrc.count - 1 + idx = idx - depth + ~int32 pc = self.ptrc.get(idx) + /; if (pc` < 1) + vout.push_cstr(" + 8\0") + ;/ + vout.push_cstr("\n\0") + + return vout.as_cstr() + ;/ + + # Literal not string - literal address + # TODO + ;; else if (n`._type == parse.NTYPE_VLIST) + return self._static_compile_arr(n, out, depth) + ;/ + + # Do a literal evaluation and then + return utils.strcpy("TODO\0") + ;/ + + /; _static_compile_struct(~parse.Node n, ~utils.Vector out) [~uint8] + return utils.strcpy("TODO\0") + ;/ + + /; _static_comp_prim_r_dot(~parse.Node n) [Var] + # Has to do: + # Variable dot chain mod + # Variable dot chain struct + _printf("TODO: _static_comp_prim_r_dot\n\0") + Var out + out._init(self._type) + return out + ;/ + + /; _static_comp_prim_r_bin(~parse.Node n) [Var] + /; if (n`.data{0} == '.') + return self._static_comp_prim_r_dot(n) + ;/ + + ~parse.Node na = n`.sub.get(0) + ~parse.Node nb = n`.sub.get(1) + + Var a = self._static_comp_prim_r(na) + Var b = self._static_comp_prim_r(nb) + + /; if (a.loc !== 0) + _printf("TODO: _static_comp_prim_r_bin: Pointer types not yet supported in binary ops at compile time\n\0") + b.end() + return a + ;; else if (b.loc !== 0) + _printf("TODO: _static_comp_prim_r_bin: Pointer types not yet supported in binary ops at compile time\n\0") + a.end() + return b + ;/ + + /; if (utils.strcmp(n`.data, "*\0") == true) + a.offset = a.offset * b.offset + ;; else if (utils.strcmp(n`.data, "/\0") == true) + a.offset = a.offset / b.offset + ;; else if (utils.strcmp(n`.data, "%\0") == true) + a.offset = a.offset % b.offset + ;; else if (utils.strcmp(n`.data, "+\0") == true) + a.offset = a.offset + b.offset + ;; else if (utils.strcmp(n`.data, "-\0") == true) + a.offset = a.offset - b.offset + ;; else + _printf("TODO: _static_comp_prim_r_bin: Bin op \"\0") + _printf(n`.data) + _printf("\" not yet supported at comptime\n\0") + ;/ + + b.end() + return a + ;/ + + /; _static_comp_prim_r_pre(~parse.Node n) [Var] + _printf("TODO: _static_comp_prim_r_pre\n\0") + Var out + out._init(self._type) + return out + ;/ + + /; _static_comp_prim_r_post(~parse.Node n) [Var] + # Has to do: + # deref of variables + # array index + _printf("TODO: _static_comp_prim_r_post\n\0") + Var out + out._init(self._type) + return out + ;/ + + /; _static_comp_prim_r_id(~parse.Node n) [Var] + # Has to do: + # Get variable and static comp + ~uint8 name = n`.data + utils.Vector v + v.init(8) + v.push(~name) + ~Var vid = self.parent`.find(SEARCH_VAR, ~v) + v.end() + + /; if (vid == NULL) + _printf("ERROR: Unable to find id \"\0") + _printf(name) + _printf("\" when compiling global \"\0") + _printf(self.name) + _printf(" returning dummy\n\0") + Var out + out._init(self._type) + return out + ;/ + + /; if (vid`.loc > 0) + _printf("ERROR: CIRCULAR REFERENCE DETECTED WHEN INITIALIZING GLOBAL!\n\0") + _printf(" Something in \"\0") + _printf(name) + _printf("\" depends on \"") + _printf(self.name) + _printf("\" which depends on the former.\n\0") + Var out + out._init(self._type) + return out + ;/ + + /; if (vid`.is_struct() == true) + _printf("ERROR: Attempted to set primitive global \"\0") + _printf(self.name) + _printf("\" to \"") + _printf(name) + _printf("\" which is a struct, not a primitive\n\0") + Var out + out._init(self._type) + return out + ;; else if (vid`.ptrc.count > 0) + return vid`.as_global() + ;/ + + # If this is also a primitive, we want to do a static comp and + # return whatever the variable spits out + ~parse.Node cross_n = vid`._id + + /; if (cross_n`.sub.count < 1) + cross_n = NULL + ;; else + cross_n = cross_n`.sub.get(0) + /; if (cross_n`._type !== parse.NTYPE_VALUE) + _printf("COMPILER ERROR: When static compiling var \"\0") + _printf(name) + _printf("\" the sub-node was not a value\n\0") + Var out + out._init(self._type) + return out + ;; else if (cross_n`.sub.count < 1) + cross_n = NULL + ;; else + cross_n = cross_n`.sub.get(0) + ;/ + ;/ + + /; if (cross_n !== NULL) + return vid`._static_comp_prim_r(cross_n) + ;/ + + Var out + out._init(self._type) + return out + ;/ + + /; _static_comp_prim_r(~parse.Node n) [Var] + /; if (n`._type == parse.NTYPE_VALUE) + n = n`.sub.get(0) + return self._static_comp_prim_r(n) + ;/ + + /; if (n`._type == parse.NTYPE_LITERAL) + int lval = 0 + /; if (utils.strcmp(n`.data, "true\0") == true) + lval = 1 + ;; else if (utils.strcmp(n`.data, "iota\0") == true) + _printf("COMPILER ERROR: iota not yet implemented\n\0") + ;; else if (n`.data{0} !< '0' && n`.data{0} !> '9') + lval = utils.cstr_to_int(n`.data) + ;; else if (n`.data{0} == '\'') + ~uint8 cha = n`.data + cha++ + uint8 uval = utils.unquote_cha(cha) + lval = uval + ;; else if (n`.data{0} == '"') + _printf("ERROR: Attempted to set a non-pointer or non-array to a string literal!\n\0") + _printf(" Literal: \0") + _printf(n`.data) + _printf("\n\0") + ;/ + Var out + out._init(self._type) + out.offset = lval + return out + ;; else if (n`._type == parse.NTYPE_BIN_OP) + return self._static_comp_prim_r_bin(n) + ;; else if (n`._type == parse.NTYPE_PRE_OP) + return self._static_comp_prim_r_pre(n) + ;; else if (n`._type == parse.NTYPE_POST_OP) + return self._static_comp_prim_r_post(n) + ;; else if (n`._type == parse.NTYPE_ID) + return self._static_comp_prim_r_id(n) + ;/ + + _printf("COMPILER ERROR: _static_comp_prim_r: Unable to work on node given\n\0") + Var out + out._init(self._type) + return out + ;/ + + /; _static_compile_prim(~parse.Node n) [~uint8] + Var vout = self._static_comp_prim_r(n) + + utils.Vector out + out.init(1) + + int sz = self.type_size() + /; if (sz == 1) + out.push_cstr(" db \0") + ;; else if (sz == 2) + out.push_cstr(" dw \0") + ;; else if (sz == 4) + out.push_cstr(" dd \0") + ;; else if (sz == 8) + out.push_cstr(" dq \0") + ;/ + + /; if (vout.loc == 0) + ~uint8 nstr = utils.int_to_str(vout.offset) + out.push_cstr(nstr) + out.push_cstr("\n\0") + _delete(nstr) + ;; else + _print_num("COMPILER ERROR: Type of var %d not yet supported for static comp\n\0", vout.loc) + ;/ + + vout.end() + return out.as_cstr() + ;/ + + /; _static_compile_zero [~uint8] + utils.Vector zero + zero.from_cstr(" db \0") + int sz = self.type_size() + /; loop (int i = 1; i < sz) [i++] + zero.push_cstr("0, \0") + ;/ + zero.push_cstr("0\n\0") + return zero.as_cstr() + ;/ + + # Compile the variable into the data section + /; _static_compile (~CompBuf buf) + # Mark we are in compilation stage to detect circular refs + self.loc = 1 + + # Get node which we are statically compiling + ~parse.Node n = self._id + /; if (n`.sub.count < 1) + n = NULL + ;; else + n = n`.sub.get(0) + /; if (n`._type !== parse.NTYPE_VALUE) + _printf("COMPILER ERROR: When static compiling var \"\0") + _printf(self.name) + _printf("\" the sub-node was not a value\n\0") + return + ;; else if (n`.sub.count < 1) + n = NULL + ;; else + n = n`.sub.get(0) + ;/ + ;/ + + utils.Vector out + out.init(1) + + ~uint8 after + /; if (n == NULL) + # Fallback to zero initialization if no value provided + after = self._static_compile_zero() + ;; else if (self.ptrc.count > 0) + # Pointer, reference, or array + after = self._static_compile_ptr(n, ~out, 0) + ;; else if (self.is_struct() == true) + # struct, should either be a vlist with all required parameters or a + # variable of the same type which we can static compile from + after = self._static_compile_struct(n, ~out) + ;; else + # primitive + after = self._static_compile_prim(n) + ;/ + + # Generate label and data + ~uint8 lab = self._global_base() + out.push_cstr(lab) + out.push_cstr(":\n\0") + out.push_cstr(after) + _delete(after) + _delete(lab) + + # Add to data sec + ~uint8 outs = out.as_cstr() + buf`.add_d(outs) + buf`.add_d("\n\0") + out.end() + + # No longer compiling this variable + self.loc = 0 + ;/ + + /; _global_base [~uint8] + utils.Vector name + name.init(1) + _recursive_mod_name(self.parent, ~name) + + /; if (name.count !== 0) + name.push_char('.') + ;/ + + name.push_cstr(self.name) + return name.as_cstr() + ;/ + + /; _global_ptr [~uint8] + utils.Vector name + name.init(1) + _recursive_mod_name(self.parent, ~name) + + /; if (name.count !== 0) + name.push_char('.') + ;/ + + name.push_cstr(self.name) + name.push_cstr("#ptr\0") + + return name.as_cstr() + ;/ + + # Get a proper global variable + /; as_global [Var] + Var out = self.copy() + out.name = self._global_base() + + int loc = 0 + loc = loc - 2 + out.loc = loc + + return out + ;/ + + /; ptr_push (int32 p) + self.ptrc.push(~p) + ;/ + + /; ptr_pop + self.ptrc.pop() + ;/ + + /; end + _delete(self.name) + self.ptrc.end() + ;/ + + #################################### + # Variable manipulation (run time) # + #################################### + + # Returns true if the variable is known to be stored in memory + /; in_mem [bool] + /; if (self.loc < 1) + return true + ;/ + + ~int32 ptr = self.top_ptrc() + /; if (ptr !== NULL) + /; if (ptr` == 0) + return true + ;; else if (ptr` > 1) + return true + ;/ + ;/ + + return false + ;/ + + + # Typechecking + + # Typechecking structs + /; _tc_struct (~Var other) [bool] + /; if (other`.is_struct() == false) + return false + ;/ + ~void a = self._type + ~void b = other`._type + + return a == b + ;/ + + /; _tc_prim (~Var other) [bool] + # Allow implicit ptr conversions + /; if (self.is_ptr() == true) + return other`.is_ptr() + ;/ + + return false + ;/ + + # Operations + + # Helper to gen register when setting a struct + /; _set_struct_r (~CompBuf buf, int reg) + ~uint8 r = reg_string(reg, 8) + + buf`.add_c(" ; putting struct address into register\n\0") + + # Initial deref or move + /; if (self.ptrc.count > 1) + buf`.add_c(" mov \0") + ;; else + buf`.add_c(" lea \0") + ;/ + buf`.add_c(r) + buf`.add_c(", [\0") + + # Reg, stack, or data + /; if (self.loc + VLOC_DATA == 0) + buf`.add_c("rel \0") + buf`.add_c(self.name) + ;; else if (self.loc + VLOC_STCK == 0) + ~uint8 get_reg = reg_string(8, 8) + buf`.add_c(get_reg) + _delete(get_reg) + ;; else + ~uint8 get_reg = reg_string(self.loc, 8) + buf`.add_c(get_reg) + _delete(get_reg) + ;/ + + # Deal with offset + /; if (self.offset !== 0) + int o = self.offset + /; if (o < 0) + o = 0 - o + buf`.add_c(" - \0") + ;; else + buf`.add_c(" + \0") + ;/ + ~uint8 its = utils.int_to_str(o) + buf`.add_c(its) + _delete(its) + ;/ + + buf`.add_c("] ; initial struct addr move\n\0") + + # For as many more times as there are references + /; loop (int i = 1; i < self.ptrc.count) [i++] + buf`.add_c(" mov \0") + buf`.add_c(r) + buf`.add_c(", [\0") + buf`.add_c(r) + buf`.add_c("] ; reference chain\n\0") + ;/ + + _delete(r) + ;/ + + # Helper to properly move a struct in memory + /; _set_struct(~CompBuf buf, ~Var other) + # Typecheck + /; if (self._tc_struct(other) == false) + _printf("ERROR: Types do not match when setting struct. [\0") + _printf(self._type`.name) + _printf("] !== [\0") + _printf(other`._type`.name) + _printf("]\n\0") + return + ;/ + + # Have to get struct address (to set) into rdi + self._set_struct_r(buf, 6) + # Have to get struct address (to read) into rsi + other`._set_struct_r(buf, 5) + + # Setup move size + ~uint8 str + str = utils.int_to_str(self._type`.size) + buf`.add_c(" mov rcx, \0") + buf`.add_c(str) + buf`.add_c(" ; size of struct [\0") + buf`.add_c(self._type`.name) + buf`.add_c("] in bytes\n\0") + _delete(str) + + # Move byte (rcx times) + buf`.add_c(" rep movsb ; move struct\n\0") + + ;/ + + # Helper to properly get the lhs of a set + /; _set_prim_l (~CompBuf buf) [~uint8] + /; if (self.loc == 0) + _printf("SET WAS CALLED ON A LITERAL! THIS IS A COMPILER ERROR!\0") + return utils.strcpy("ERR SHOULD NOT HAPPEN - TRIED TO SETPRIML ON A LITERAL\0") + ;/ + + # Base of address/register + ~uint8 out + /; if (self.loc > 0) + /; if (self.in_mem() == true) + out = reg_string(self.loc, 8) + /; if (self.offset !== 0) + utils.Vector vout + vout.from_cstr(out) + + int off = self.offset + /; if (off < 0) + off = 0 - off + vout.push_cstr(" - \0") + ;; else if (off > 0) + vout.push_cstr(" + \0") + ;/ + + _delete(out) + out = utils.int_to_str(off) + vout.push_cstr(out) + _delete(out) + out = vout.as_cstr() + ;/ + ;; else + uint sz = self.type_size() + out = reg_string(self.loc, sz) + ;/ + ;; else + utils.Vector vout + /; if (self.loc + 1 == 0) + # Stack + vout.from_cstr("rbp\0") + ;; else + vout.from_cstr("rel \0") + vout.push_cstr(self.name) + ;/ + + int off = self.offset + /; if (off < 0) + off = 0 - off + vout.push_cstr(" - \0") + ;; else if (off > 0) + vout.push_cstr(" + \0") + ;/ + + /; if (off !== 0) + out = utils.int_to_str(off) + vout.push_cstr(out) + _delete(out) + ;/ + + out = vout.as_cstr() + + /; if (self.is_ref() == true) + # Need to move into rdi + buf`.add_c(" mov rdi, [\0") + buf`.add_c(out) + buf`.add_c("]\n\0") + _delete(out) + out = utils.strcpy("rdi\0") + ;/ + ;/ + + # If in memory we need to wrap in [] + /; if (self.in_mem() == true) + utils.Vector vout + vout.from_cstr("[\0") + vout.push_cstr(out) + vout.push_cstr("]\0") + + _delete(out) + out = vout.as_cstr() + ;/ + + # Loop and make sure we are dereferencing properly + /; loop (int i = 1; i < self.ptrc.count) [i++] + /; if (self.is_ptrc(i, 0) == true) + buf`.add_c(" mov rdi, [rdi] ; auto deref\n\0") + ;; else + i = self.ptrc.count + ;/ + ;/ + + /; if (self.double_ref() == true) + _delete(out) + out = utils.strcpy("[rdi]\0") + ;/ + + /; if (out{0} == '[') + utils.Vector vout + uint ts = self.type_size() + /; if (ts == 1) + vout.from_cstr("byte \0") + ;; else if (ts == 2) + vout.from_cstr("word \0") + ;; else if (ts == 4) + vout.from_cstr("dword \0") + ;; else if (ts == 8) + vout.from_cstr("qword \0") + ;/ + vout.push_cstr(out) + + _delete(out) + out = vout.as_cstr() + ;/ + + return out + ;/ + + # Helper to properly get the rhs of a set + /; _set_prim_r (~CompBuf buf, ~Var lhs) [~uint8] + /; if (self.loc == 0) + ~uint8 o = utils.int_to_str(self.offset) + return o + ;/ + + ~uint8 out = self._set_prim_l(buf) + + /; if (self.in_mem() == true) + utils.Vector vout + uint ts = self.type_size() + /; if (ts == 1) + vout.from_cstr("byte \0") + ;; else if (ts == 2) + vout.from_cstr("word \0") + ;; else if (ts == 4) + vout.from_cstr("dword \0") + ;; else if (ts == 8) + vout.from_cstr("qword \0") + ;/ + vout.push_cstr(out) + + _delete(out) + out = vout.as_cstr() + ;/ + + # Sign extend if required + bool ext = false + uint R = self.type_size() + uint L = lhs`.type_size() + /; if (R < L) + ext = true + ~uint8 vout = reg_string(5, L) + + /; if (lhs`.is_signed() == true && self.is_signed() == true) + /; if (R < 4) + buf`.add_c(" movsx \0") + ;; else + buf`.add_c(" movsxd \0") + ;/ + buf`.add_c(vout) + buf`.add_c(", \0") + ;; else if (R < 4) + buf`.add_c(" movzx \0") + buf`.add_c(vout) + buf`.add_c(", \0") + ;; else + buf`.add_c(" mov esi, \0") + ;/ + + buf`.add_c(out) + buf`.add_c("\n\0") + _delete(out) + out = vout + ;/ + + /; if (ext == false) + /; if (self.in_mem() == true && lhs`.in_mem() == true) + ~uint8 vout = reg_string(5, L) + buf`.add_c(" mov \0") + buf`.add_c(vout) + buf`.add_c(", \0") + buf`.add_c(out) + buf`.add_c("\n\0") + _delete(out) + out = vout + ;/ + ;/ + + return out + ;/ + + + # Set this Variable to the value of other + /; set (~CompBuf buf, ~Var other) + # Options: + # - If builtin then move based on size (byte, word, dword, qword) + # - If pointer then move qword + # - If struct then move via rep movsb + + /; if (self.is_struct() == true) + # Struct set + self._set_struct(buf, other) + return + ;/ + + # Generate lhs set and rhs set + ~uint8 sr = other`._set_prim_r(buf, ~self) + ~uint8 sl = self._set_prim_l(buf) + + buf`.add_c(" mov \0") + buf`.add_c(sl) + buf`.add_c(", \0") + buf`.add_c(sr) + buf`.add_c("\n\0") + + _delete(sl) + _delete(sr) + ;/ + + # Set the address which this reference points to + /; set_ref (~CompBuf buf, ~Var other, int depth) + # count how many refs + int max_depth = self.max_ref() + + /; if (depth !< max_depth) + _print_num("ERROR: Unable to set reference, depth %d equals or exceeds max_depth \0", depth) + _print_num("%d\n\0", max_depth) + _printf(" When taking ref of variable \"\0") + _printf(other`.name) + _printf("\" into variable \"\0") + _printf(self.name) + _printf("\"\n\0") + return + ;/ + + int32 set = 0 + set = set - 1 + + max_depth = self.ptrc.count - max_depth + max_depth = max_depth + depth + + Var copy = self.copy() + ~int32 ptr = copy.ptrc.get(max_depth) + ptr` = set + copy.set(buf, other) + copy.end() + ;/ + + # Copy the pointer to this variable into the output reg (or not if not needed) + /; take_ptr (~CompBuf buf, int reg) [Var] + Var vcpy = self.copy() + /; if (self.in_mem() == false) + _printf("ERROR: UNABLE TO GET REFERENCE OF VAR \"\0") + _printf(self.name) + _printf("\"\n\0") + return vcpy + ;/ + + int32 set = 0 + set = set - 1 + /; if (vcpy.is_ref() == true) + # If we are a ref, we want to set the first one and that's it + int idx = 0 + /; loop (int i = 0; i < vcpy.ptrc.count) [i++] + /; if (vcpy.is_ptrc(i, 0) == false) + i = vcpy.ptrc.count + ;; else + idx++ + ;/ + ;/ + + idx = vcpy.ptrc.count - idx + ~int32 p = vcpy.ptrc.get(idx) + p` = set + ;; else + /; if (self.is_struct() == true) + self._set_struct_r(buf, reg) + ;; else + ~uint8 source = self._set_prim_l(buf) + ~uint8 rstr = reg_string(reg, 8) + buf`.add_c(" lea \0") + buf`.add_c(rstr) + buf`.add_c(", \0") + buf`.add_c(source) + buf`.add_c("\n\0") + _delete(source) + _delete(rstr) + ;/ + + vcpy.loc = reg + vcpy.offset = 0 + vcpy.ptrc.end() + vcpy.ptrc.init(4) + vcpy.ptr_push(set) + ;/ + + return vcpy + ;/ + + /; _de_ref + uint idx = self.ptrc.count + ~int32 ptr + /; loop (idx > 0) + idx = idx - 1 + ptr = self.ptrc.get(idx) + /; if (ptr` !== 0) + /; if (ptr` > 0) + _printf("ERROR: Can't take direct de-ref of array variable, try an index (name: \"\0") + _printf(self.name) + _printf("\")\n\0") + ;; else + ptr` = 0 + ;/ + return + ;/ + ;/ + ;/ + + /; de_ref [Var] + Var out = self.copy() + out._de_ref() + return out + ;/ + + /; index (~CompBuf buf, ~Var idx, int reg) [Var] + buf`.add_c(" ; Gen index\n\0") + + # Create a literal with the size of the type + # Don't need to init or end this one + uint mul_sz = self.type_size() + Var oo + oo.offset = mul_sz + oo.loc = 0 + + # Generate output variable + Var out = self.copy() + out.offset = 0 + out.loc = reg + + # Strip off any refs + /; loop (out.is_ref() == true) + out.ptr_pop() + ;/ + + # Multiply index by size of type + out.set(buf, idx) + out.mul(buf, ~oo) + + # If we are an array, want to add 8 since the beginning encodes the length + /; if (out.is_arr() == true) + oo.offset = 8 + out.add(buf, ~oo) + ;/ + + # Get address in rsi + ~uint8 to_str = self._set_prim_l(buf) + + # If we are a "known length array" we need to lea instead of mov + ~int32 optrc = out.top_ptrc() + /; if (optrc !== NULL) + /; if (optrc` > 1) + buf`.add_c(" lea rsi, \0") + ;; else + buf`.add_c(" mov rsi, \0") + ;/ + ;; else + _printf("ERROR: Can't take index of non-array variable \"\0") + _printf(self.name) + _printf("\"\n\0") + _delete(to_str) + return out + ;/ + buf`.add_c(to_str) + buf`.add_c("\n\0") + _delete(to_str) + + # Add address to computed offset + to_str = out._set_prim_l(buf) + buf`.add_c(" add \0") + buf`.add_c(to_str) + buf`.add_c(", rsi\n\0") + + # deref + int32 ptr_ref = 0 + out.ptr_pop() + out.ptr_push(ptr_ref) + + return out + ;/ + + ####################### + # Standard operations # + ####################### + + /; standard_op (~CompBuf buf, ~Var other, ~uint8 op_str) + ~uint8 from_str = other`._set_prim_r(buf, ~self) + ~uint8 to_str = self._set_prim_l(buf) + + buf`.add_c(" \0") + buf`.add_c(op_str) + buf`.add_c(" \0") + buf`.add_c(to_str) + buf`.add_c(", \0") + buf`.add_c(from_str) + buf`.add_c("\n\0") + + _delete(from_str) + _delete(to_str) + ;/ + + /; add (~CompBuf buf, ~Var other) + /; if (self.loc == VLOC_LITL && other`.loc == VLOC_LITL) + self.offset = self.offset + other`.offset + return + ;/ + + self.standard_op(buf, other, "add\0") + ;/ + + /; sub (~CompBuf buf, ~Var other) + /; if (self.loc == VLOC_LITL && other`.loc == VLOC_LITL) + self.offset = self.offset - other`.offset + return + ;/ + + self.standard_op(buf, other, "sub\0") + ;/ + + /; and (~CompBuf buf, ~Var other) + /; if (self.loc == VLOC_LITL && other`.loc == VLOC_LITL) + self.offset = self.offset & other`.offset + return + ;/ + + self.standard_op(buf, other, "and\0") + ;/ + + /; or (~CompBuf buf, ~Var other) + /; if (self.loc == VLOC_LITL && other`.loc == VLOC_LITL) + self.offset = self.offset | other`.offset + return + ;/ + + self.standard_op(buf, other, "or\0") + ;/ + + /; xor (~CompBuf buf, ~Var other) + /; if (self.loc == VLOC_LITL && other`.loc == VLOC_LITL) + self.offset = self.offset ^ other`.offset + return + ;/ + + self.standard_op(buf, other, "xor\0") + ;/ + + # Product op + + /; mul (~CompBuf buf, ~Var other) + /; if (self.loc == VLOC_LITL && other`.loc == VLOC_LITL) + self.offset = self.offset * other`.offset + return + ;/ + + ~uint8 from_str = other`._set_prim_r(buf, ~self) + ~uint8 to_str = self._set_prim_l(buf) + + /; if (self.in_mem() == true) + uint sz = self.type_size() + ~uint8 ax = reg_string(1, sz) + + # Move to ax register for the mul and move back afterward + buf`.add_c(" mov \0") + buf`.add_c(ax) + buf`.add_c(", \0") + buf`.add_c(to_str) + buf`.add_c("\n\0") + + # Do actual product + buf`.add_c(" imul \0") + buf`.add_c(ax) + buf`.add_c(", \0") + buf`.add_c(from_str) + buf`.add_c("\n\0") + + # Move back + buf`.add_c(" mov \0") + buf`.add_c(to_str) + buf`.add_c(", \0") + buf`.add_c(ax) + buf`.add_c("\n\0") + + _delete(ax) + ;; else + buf`.add_c(" imul \0") + buf`.add_c(to_str) + buf`.add_c(", \0") + buf`.add_c(from_str) + buf`.add_c("\n\0") + ;/ + + _delete(from_str) + _delete(to_str) + ;/ + + # Division op + + /; _div(~CompBuf buf, ~Var other, int r) + ~uint8 from_str = other`._set_prim_r(buf, ~self) + ~uint8 to_str = self._set_prim_l(buf) + uint sz = self.type_size() + ~uint8 rstr = reg_string(1, sz) + + /; if (other`.loc == 0) + # Move literal into register + buf`.add_c(" mov rcx, \0") + buf`.add_c(from_str) + buf`.add_c("\n\0") + _delete(from_str) + from_str = reg_string(3, sz) + ;/ + + # Move dividend into rax + buf`.add_c(" mov \0") + buf`.add_c(rstr) + buf`.add_c(", \0") + buf`.add_c(to_str) + buf`.add_c("\n\0") + + /; if (self.is_signed() == true) + # Sign extend + /; if (sz == 1) + buf`.add_c(" movsx ax, al\n\0") + ;; else if (sz == 2) + buf`.add_c(" cwd\n\0") + ;; else if (sz == 4) + buf`.add_c(" cdq\n\0") + ;; else if (sz == 8) + buf`.add_c(" cqo\n\0") + ;/ + + buf`.add_c(" idiv \0") + ;; else + # Zero out + buf`.add_c(" xor ") + /; if (sz == 1) + buf`.add_c("ah, ah\n\0") + ;; else if (sz == 2) + buf`.add_c("dx, dx\n\0") + ;; else if (sz == 4) + buf`.add_c("edx, edx\n\0") + ;; else if (sz == 8) + buf`.add_c("rdx, rdx\n\0") + ;/ + + buf`.add_c(" div \0") + ;/ + buf`.add_c(from_str) + buf`.add_c("\n\0") + + # Fix for bytes + _delete(rstr) + /; if (sz == 1 && r == 4) + rstr = utils.strcpy("ah\0") + ;; else + rstr = reg_string(r, sz) + ;/ + + + buf`.add_c(" mov \0") + buf`.add_c(to_str) + buf`.add_c(", \0") + buf`.add_c(rstr) + buf`.add_c("\n\0") + + _delete(from_str) + _delete(to_str) + _delete(rstr) + ;/ + + /; div (~CompBuf buf, ~Var other) + /; if (self.loc == VLOC_LITL && other`.loc == VLOC_LITL) + self.offset = self.offset / other`.offset + return + ;/ + + self._div(buf, other, 1) + ;/ + + /; mod (~CompBuf buf, ~Var other) + /; if (self.loc == VLOC_LITL && other`.loc == VLOC_LITL) + self.offset = self.offset % other`.offset + return + ;/ + + self._div(buf, other, 4) + ;/ + + /; _unary (~CompBuf buf, ~uint8 op) + ~uint8 to_str = self._set_prim_l(buf) + buf`.add_c(" \0") + buf`.add_c(op) + buf`.add_c(" \0") + /; if (self.in_mem() == true) + uint sz = self.type_size() + /; if (sz == 1) + buf`.add_c("byte \0") + ;; else if (sz == 2) + buf`.add_c("word \0") + ;; else if (sz == 4) + buf`.add_c("dword \0") + ;; else if (sz == 8) + buf`.add_c("qword \0") + ;/ + ;/ + buf`.add_c(to_str) + buf`.add_c("\n\0") + _delete(to_str) + ;/ + + /; not (~CompBuf buf) + /; if (self.loc == VLOC_LITL) + self.offset = !self.offset + return + ;/ + + self._unary(buf, "not\0") + ;/ + + /; neg (~CompBuf buf) + /; if (self.loc == VLOC_LITL) + int off = 0 + self.offset = off - self.offset + return + ;/ + + self._unary(buf, "neg\0") + ;/ + + /; inc (~CompBuf buf) + /; if (self.loc == VLOC_LITL) + self.offset++ + return + ;/ + + self._unary(buf, "inc\0") + ;/ + + /; dec (~CompBuf buf) + /; if (self.loc == VLOC_LITL) + self.offset++ + return + ;/ + + self._unary(buf, "dec\0") + ;/ + + # Boolean related + + /; test (~CompBuf buf) + ~uint8 to_str = self._set_prim_l(buf) + buf`.add_c(" cmp \0") + /; if (self.in_mem() == true) + uint sz = self.type_size() + /; if (sz == 1) + buf`.add_c("byte \0") + ;; else if (sz == 2) + buf`.add_c("word \0") + ;; else if (sz == 4) + buf`.add_c("dword \0") + ;; else if (sz == 8) + buf`.add_c("qword \0") + ;/ + ;/ + buf`.add_c(to_str) + buf`.add_c(", 0\n\0") + _delete(to_str) + ;/ + + /; cmov (~CompBuf buf, ~Var a, ~Var b, ~uint8 cc) + + buf`.add_c(" cmp \0") + ~uint8 a_str = a`._set_prim_l(buf) + buf`.add_c(a_str) + _delete(a_str) + buf`.add_c(", \0") + ~uint8 b_str = b`._set_prim_r(buf, a) + buf`.add_c(b_str) + _delete(b_str) + buf`.add_c("\n\0") + + buf`.add_c(" mov ax, 0\n\0") + buf`.add_c(" mov cx, 1\n\0") + buf`.add_c(" cmov\0") + buf`.add_c(cc) + buf`.add_c(" ax, cx ; Compare set\n\0") + + ~uint8 to_str = self._set_prim_l(buf) + + Var ax = self.copy() + ax.loc = 1 + + buf`.add_c(" mov \0") + ~uint8 to_str = self._set_prim_l(buf) + buf`.add_c(to_str) + _delete(to_str) + buf`.add_c(", \0") + ~uint8 ax_str = ax._set_prim_r(buf, ~self) + buf`.add_c(ax_str) + _delete(ax_str) + buf`.add_c(" ; Mov after compare\n\0") + ;/ + + /; cset (~CompBuf buf, ~uint8 cc) + buf`.add_c(" mov ax, 0\n\0") + buf`.add_c(" mov cx, 1\n\0") + buf`.add_c(" cmov\0") + buf`.add_c(cc) + buf`.add_c(" ax, cx ; Compare set\n\0") + + ~uint8 to_str = self._set_prim_l(buf) + + Var ax = self.copy() + ax.loc = 1 + + buf`.add_c(" mov \0") + ~uint8 to_str = self._set_prim_l(buf) + buf`.add_c(to_str)scope + _delete(to_str) + buf`.add_c(", \0") + ~uint8 ax_str = ax._set_prim_r(buf, ~self) + buf`.add_c(ax_str) + _delete(ax_str) + buf`.add_c(" ; Mov after compare\n\0") + ;/ + + /; member (~CompBuf buf, ~uint8 name) [Var] + /; if (self.is_struct() == false) + _printf("ERROR: Attempted to get a member named \"\0") + _printf(name) + _printf("\" from a primitive type \"\0") + _printf(self._type`.name) + _printf("\"\n\0") + _printf(" (If the previous type was in fact a structure then the variable was probably a pointer)\n\0") + Var out + out.loc = 0 + return out + ;/ + + ~Var mbr = self`._type`.get_member(name) + + /; if (mbr == NULL) + _printf("ERROR: member \"\0") + _printf(name) + _printf("\" not found in struct \"\0") + _printf(self._type`.name) + _printf("\n\0") + Var out + out.loc = 0 + return out + ;/ + + Var out = mbr`.copy() + + /; if (self.is_ref() == true) + self._set_struct_r(buf, 5) + out.loc = 5 + out.ptr_push(0) + ;; else + out.loc = self.loc + out.offset = out.offset + self.offset + ;/ + + # out._print(0) + return out + ;/ + + # Printing + + /; _print (int idt) + _indent(idt) + _printf("{ Var : \0") + _printf(self.name) + _printf("\n\0") + + _indent(idt + 1) + _printf("type: \0") + /; if (self._type !== NULL) + _printf(self._type`.name) + ;; else + _printf("(nil)\0") + ;/ + _printf("\n\0") + + _indent(idt + 1) + _printf("ptrc: \0") + ~int32 istr + /; loop (int i = 0; i < self.ptrc.count) [i++] + istr = self.ptrc.get(i) + _print_num("%d \0", istr`) + ;/ + _printf("\n\0") + + _indent(idt + 1) + _print_num("loc: %d\n\0", self.loc) + + _indent(idt + 1) + _print_num("off: %d\n\0", self.offset) + + _indent(idt) + _printf("}\n\0") + ;/ +;/ + + diff --git a/tnslc/parse/ast.tnsl b/tnslc/parse/ast.tnsl index 420f3d3..a9b48d7 100644 --- a/tnslc/parse/ast.tnsl +++ b/tnslc/parse/ast.tnsl @@ -204,6 +204,20 @@ int errors_shown = 0 return true ;/ +# Canonical operation order from strongest (lowest number prec) to weakest (highest number prec) +# Parenthesis or function call done before any of these +# 0: deref - (`) +# 1: member - (.) +# 2: refer - (~) +# 3: inc/dec - (++ --) +# 4: bit shift - (<< >>) +# 5: len prefix op - (len) +# 6: mul/div - (* / %) +# 7: add/sub - (+ -) +# 8: bitwise ops - (! & | ^ !& !| !^) +# 9: comparison - (== < > <== >== !== !< !>) +# 10: boolean - (&& || ^^ !&& !|| !^^) +# 11: assignment - variants of = /; _op_order(~uint8 op) [int] int l = utils.strlen(op) @@ -255,6 +269,8 @@ int errors_shown = 0 return 5 ;; else if (op{1} == '=') return 9 + ;; else if (op{2} == '=') + return 11 ;/ return 10 ;/ @@ -912,7 +928,7 @@ int errors_shown = 0 _ast_decl(fin, ~list, first) ;; else if (first`._type == TTYPE_USRWD || first`.eq("~\0") == true) _maybe_helper_fun(fin, ~list, first, true) - ;; else if (first`._type == TTYPE_LITRL || _op_prefix(first)) + ;; else if (first`._type == TTYPE_LITRL || first`.eq("(\0") == true || _op_prefix(first)) _ast_value(fin, ~list, first, true) ;; else if (first`._type == TTYPE_KEYWD) _ast_keyword_expr(fin, ~list, first) @@ -1054,7 +1070,7 @@ int errors_shown = 0 _ast_decl(fin, ~out, first) ;; else if (first`._type == TTYPE_USRWD || first`.eq("~\0") == true) _maybe_helper_fun(fin, ~out, first, false) - ;; else if (first`._type == TTYPE_LITRL || _op_prefix(first)) + ;; else if (first`._type == TTYPE_LITRL || first`.eq("(\0") == true || _op_prefix(first)) _ast_value(fin, ~out, first, false) ;; else if (first`._type == TTYPE_KEYWD) _ast_keyword_expr(fin, ~out, first) @@ -1815,7 +1831,7 @@ int errors_shown = 0 _ast_decl(fin, ~fn, first) ;; else if (first`._type == TTYPE_USRWD || first`.eq("~\0") == true) _maybe_helper_fun(fin, ~fn, first, false) - ;; else if (first`._type == TTYPE_LITRL || _op_prefix(first)) + ;; else if (first`._type == TTYPE_LITRL || first`.eq("(\0") == true || _op_prefix(first)) _ast_value(fin, ~fn, first, false) ;; else if (first`._type == TTYPE_KEYWD) _ast_keyword_expr(fin, ~fn, first) @@ -1848,8 +1864,9 @@ int errors_shown = 0 return ;/ - - ~uint8 rel = utils.unquote_str(first`.data) + + utils.Vector relv = utils.unquote_str(first`.data) + ~uint8 rel = relv.as_cstr() utils.File imp = fin`.relative(rel) _delete(rel) @@ -1880,7 +1897,8 @@ int errors_shown = 0 ;/ Node an - an.init(NTYPE_ASM, utils.unquote_str(first`.data)) + utils.Vector asmv = utils.unquote_str(first`.data) + an.init(NTYPE_ASM, asmv.as_cstr()) mod`.add_child(~an) Token tmp = produce_next_token(fin, first`) @@ -2096,7 +2114,7 @@ int errors_shown = 0 ;; else if (first._type == TTYPE_KEYTP || first._type == TTYPE_USRWD || first.eq("~\0") == true || first.eq("{\0") == true) _ast_decl(fin, mod, ~first) ;; else - _ast_print_err(fin, ~first, "Expected 'import', 'struct', 'asm', block, or declaration in top level\0") + _ast_print_err(fin, ~first, "Expected 'import', 'struct', 'asm', block (starts with '/;'), or type (for variable declaration) in top level\0") Token tmp = produce_next_token(fin, first) first.end() @@ -2115,7 +2133,7 @@ int errors_shown = 0 utils.Vector v v.init(1) - out.init(NTYPE_MODULE, v.as_cstr()) + out.init(NTYPE_EXPORT, v.as_cstr()) _ast_file(fin, ~out) @@ -2187,7 +2205,7 @@ int errors_shown = 0 ;/ /; print_node_head (~Node n) - _printf("{ NODE_TYPE: \0") + _printf("{ \0") print_node_type(n) _printf(", DATA: \0") _printf(n`.data) diff --git a/tnslc/test.tnsl b/tnslc/test.tnsl index 6b55408..896897e 100644 --- a/tnslc/test.tnsl +++ b/tnslc/test.tnsl @@ -1,11 +1,16 @@ -int i = 1 + 2 * 4 - 3 + 4 +/; five [int] + return 5 +;/ -~uint8 a = b{0} +/; main (int argc, ~~uint8 argv) [int] + # Mov input from C + asm "mov r10, rdi" + asm "mov r11, rsi" -/; main ({}{}uint8 args) [int] + /; if (argc < 5 && argc > 1) + return argc + ;/ - ~uint8 as = 12 - as` = 3 + 4 + return five() ;/ - diff --git a/tnslc/tnslc.tnsl b/tnslc/tnslc.tnsl index e95a63b..6176026 100644 --- a/tnslc/tnslc.tnsl +++ b/tnslc/tnslc.tnsl @@ -5,10 +5,20 @@ ~uint8 DEFAULT_FOUT = "out.asm\0" ~uint8 USAGE = " -TNSLC v0.6.0 (C) 2024 CircleShift (MPL 2.0) +TNSLC v0.7.0 (C) 2026 CircleShift (MPL 2.0) usage: - tnslc (file in) [file out] + tnslc [options] (file in) + +options: + -o [output file] + Sets the file to output asm code to (default is \"out.asm\") + + -p + Print the parse tree and exit without compiling + + -m + Print the module tree and exit without compiling \0" @@ -25,20 +35,51 @@ usage: _printf(USAGE) return 1 ;/ - + utils.File fin, fout - fin.init(argv{1}) - - /; if (argc > 2) - fout.init(argv{2}) + int arg_counter = 1 + int mode = 0 + ~uint8 first_str = argv{arg_counter} + + /; if (first_str{0} == '-') + /; if (utils.strcmp(argv{arg_counter}, "-p\0") == true) + mode = 1 + arg_counter++ + ;; else if (utils.strcmp(argv{arg_counter}, "-m\0") == true) + mode = 2 + arg_counter++ + ;; else if (utils.strcmp(argv{arg_counter}, "-o\0") == true) + arg_counter++ + /; if (arg_counter !< argc) + _printf(USAGE) + return 1 + ;/ + fout.init(argv{arg_counter}) + ;; else + _printf(USAGE) + return 1 + ;/ ;; else fout.init(DEFAULT_FOUT) ;/ - compile.generate(~fin, ~fout) + /; if (arg_counter !< argc) + _printf(USAGE) + return 1 + ;/ + + fin.init(argv{arg_counter}) + + /; if (mode == 0) + compile.generate(~fin, ~fout) + fout.end() + ;; else if (mode == 1) + compile.parse_tree(~fin) + ;; else if (mode == 2) + compile.mod_tree(~fin) + ;/ fin.end() - fout.end() return 0 ;/ diff --git a/tnslc/utils/algo.tnsl b/tnslc/utils/algo.tnsl index 73cfb7f..ed11aad 100644 --- a/tnslc/utils/algo.tnsl +++ b/tnslc/utils/algo.tnsl @@ -15,6 +15,25 @@ return out ;/ +/; ends_with (~uint8 str, suffix) [bool] + int chk = strlen(suffix) + int counter = 0 + + /; loop (str` !== 0) [str++] + /; if (suffix{counter} !== 0) + /; if (suffix{counter} == str`) + counter++ + ;; else + counter = 0 + ;/ + ;; else + counter = 0 + ;/ + ;/ + + return chk == counter +;/ + /; base_for_char (uint8 c) [int] /; if (c == 'b' || c == 'B') return 2 @@ -150,7 +169,7 @@ /; loop (i > 0) [i = i / 16] int n = i % 16 /; if (n > 9) - out.push_char('a' + n - 10) + out.push_char('A' + n - 10) ;; else out.push_char('0' + n) ;/ @@ -200,6 +219,13 @@ return out.as_cstr() ;/ +/; stradd(~uint8 a, ~uint8 b) [~uint8] + Vector out + out.from_cstr(a) + out.push_cstr(b) + return out.as_cstr() +;/ + /; unquote_cha(~uint8 cha) [uint8] /; if (cha` !== '\\') return cha` @@ -224,7 +250,7 @@ return cha` ;/ -/; unquote_str(~uint8 str) [~uint8] +/; unquote_str(~uint8 str) [Vector] Vector out out.init(1) @@ -237,6 +263,6 @@ out.push(~buf) ;/ - return out.as_cstr() + return out ;/ diff --git a/tnslc/utils/vector.tnsl b/tnslc/utils/vector.tnsl index d00d698..b38978f 100644 --- a/tnslc/utils/vector.tnsl +++ b/tnslc/utils/vector.tnsl @@ -56,6 +56,19 @@ uint VECTOR_MAX_GROW = 256 self.count++ ;/ + /; replace (int index, ~void el) + ~uint8 start = self.get(index) + /; if (start == NULL) + return + ;/ + + /; loop (int i = 0; i < self._elsz) [i++] + ~uint8 to = start + i + ~uint8 from = el + i + to` = from` + ;/ + ;/ + /; _shrink(uint i) /; if (i !< self.size) self.size = 1 @@ -67,6 +80,22 @@ uint VECTOR_MAX_GROW = 256 ;/ /; pop + self.remove(self.count - 1) + ;/ + + /; remove (int index) + /; if (index < 0 || index !< self.count) + return + ;/ + + /; if (self.count > 1) + /; loop (int i = index * self._elsz; i < (self.count - 1) * self._elsz) [i++] + ~uint8 to = self.data + i + ~uint8 from = self.data + i + self._elsz + to` = from` + ;/ + ;/ + self.count-- /; if (self.count < self.size / 2) @@ -96,6 +125,25 @@ uint VECTOR_MAX_GROW = 256 self._elsz = 0 _delete(self.data) ;/ + + /; copy [Vector] + Vector out + + out.init(self._elsz) + /; loop (int i = 0; i < self.count) [i++] + ~int tmp = self.get(i) + out.push(tmp) + ;/ + + return out + ;/ + + /; back [~void] + /; if (self.count > 0) + return self.get(self.count - 1) + ;/ + return NULL + ;/ ;/ diff --git a/tnslc/vec_test.tnsl b/tnslc/vec_test.tnsl new file mode 100644 index 0000000..95544e3 --- /dev/null +++ b/tnslc/vec_test.tnsl @@ -0,0 +1,47 @@ +:import "utils/utils.tnsl" + +/; print_vec(~utils.Vector v) + _printf("vec: [ \0") + /; if (v`.count > 0) + ~int n = v`.get(0) + _print_num("%d\0", n`) + /; loop (int i = 1; i < v`.count) [i++] + ~int n = v`.get(i) + _print_num(", %d\0", n`) + ;/ + ;/ + _printf(" ]\n\0") +;/ + +/; main [int] + utils.Vector vec + vec.init(8) + int a = 1295 + vec.push(~a) + a = 1984 + vec.push(~a) + a = 3498 + vec.push(~a) + a = 8972 + vec.push(~a) + + print_vec(~vec) + _printf("pop_front\n\0") + vec.remove(0) + print_vec(~vec) + _printf("pop_mid\n\0") + vec.remove(1) + print_vec(~vec) + _printf("pop_end\n\0") + vec.pop() + print_vec(~vec) + _printf("pop_final\n\0") + vec.pop() + print_vec(~vec) + + vec.end() + + return 0 +;/ + + |