Hotfix/jcli upgrade fix (#24)

* 🐞️ isSameType: Add a base case (#8)

* Core

- Added a TODO where we are meant to insert the fallback check in `isSameType(Type t1, Type t2)`

* TypeChecker

- Added backported `isSameType(Type 1, Type t2)` fix for #114

* 🐞️ Functions: Return position enforcement (#6)

* Parser

- Added a TODO in `wantsBody == true` case in `parseFuncDef()` to check for the return keyword's position

* Parser

- Added a check in `parseFuncDef()` which, is a `ReturnStmt` is found, then crash the parser if it is found anywhere besides the last statement

* Test cases

- Added test cases to test the `return` statement position enforcement

* 🧠 Feature: Direct function calls (#11)

* Test cases

- Added `simple_direct_func_call.t` to test direct function calls

* Test cases

- Removed tabs which broke lexing

* AST nodes

- `FunctionCall` now has the ability to be marked as statement-level by calling `makeStatementLevel()`, this can then be queried later via `isStatementLevelFuncCall()`

* Parser

- Statement-level function calls were never actually returned, resulting in `null` being returned by `parseName()` - this has now been fixed.
- Along with this we now "mark" this `FunctionCall` AST node as statement-level when it occurs in `parseName()`

* Instruction

- Allow `FuncCallInstr` to be makred as statement-level and queired much in the same manner as its corresponding AST-node/parser-node `FunctionCall`

* Dependency

- Added support for `DNode` generation in `generalPass()` for `FunctionCall` AST nodes

* TypeChecker

- Handle `FunctionCall`s differently in terms of code generation dependent on whether or not rhe call is within an expression of statement-level

* DGen

- Handle statement-level function calls (`FuncCallInstr`s) differently by tacking on an additional `";"` to the emit string

* - Added `simple_direct_func_call.t` to GitHub pipeline

* DGen

- Added instrumentation for semantic code generation for `simple_function_recursion_factorial.t`
- Added TODO for future `simple_func_call_direct.t`

Test cases

- Added `simple_function_recursion_factorial.t` to test recursion

Pipelines

- Added `simple_function_recursion_factorial.t` to `emit` stage

* DGen

- Made `if` an `else if` - this wouldn't of affected anything but just to be correct

* DGen

- Added semantic code generation instrumentation for test case `simple_direct_func_call.t`

Test cases

- Updated test case `simple_direct_func_call.t`

* 🧠 Feature: Meta-programming engine (#10)

* Parser

- Added new interface `Cloneable`

* Symbols

- Added new `Statement`-based type: `Macro` to support `Macro`(s)

* MetaProcessor

- Added the `MetaProcessor`

TypeChecker

- Added the `MetaProcessor` instance to the `TypeChecker`, it will be instantiated upon the `TypeChecker`'s construction and later have its `.process()` method called as the first call in `beginCheck()`

* TypedEntity

- Added a `setType(string)` method to update the internal `type` field

* MetaProcessor

- Added a type-re-writing facility via `typeRewrite(TypedEntity)` which will re-write the types such as `size_t`, `ssize_t` and so forth

Test cases

- Added a test case `meta/types.t` which tests this

* MetaProcessor

- Updated the constructor to only take in an instance of the `TypeChecker`
- Updated the `process()` method to take in a `Container` such that it can be used recursively
- Commented code
- Added a recursive call to `process(Container)` when `curStmt` is a kind-of `Container` (this ensures that we reach the `VariableParameter`s of `Function` (die to them making up the `Statement[]` of `Function` type)
- Removed unused `cmp` import `std.string`
- Added type-rewrite for `ssize_t` -> `long`

TypeChecker

- Updated the constructor call to `MetaProcessor` to use its new API
- Updated call to `process()` to now be `process(modulle)`

Test cases

- Updated `types.t` to test re-writing of the `Function`'s parameters

* MetaProcessor

- Added another FIXME

* Mcro

- Added interface `MTypeRewritable` to represent any AST node which has a `setType(string)` and `string getType()` method for rewriting- Added `Sizeof` which is a kind-of `IntegerLiteral`

Data

- Made `TypedEntity` implement the `MTypeRewritable` interface

Expressions

- Made `IntegerLiteral` non-final such that we can inherit from it
- Added a final `setNumber(string)` method to `NumberLiteral` to update the literal

MetaProcessor

- The type rewriting mechanism now operates on `MTypeRewritable` AST nodes
- Work has begun on `sizeOf_Literalize(Sizeof)` which is to determine the `Type` of the `Sizeof` statement and then calculate the memory width and update its literal (as it is a kind-of `NunberLiteral`) to said size

Test cases

- Added `meta/sizeof.t` to test `sizeof` functionality

* Mcro

- Added interface type `MStatementSearchable`

* Mcro

- Redefined `MStatementSearchable`
- Added `MStatementReplaceable`
- Added `Repr` (a kind-of `Expression`) which will be used as an example to test out the aforementioned two interfaces

* MStatementSearchable

- Added method `search(TypeInfo_Class clazzType)` which uses a `TypeInfo_Class` to search for all types matching that (and which are sub-types of `Statement` and then adds these to a list and returns it in the form of `Statement[]`

* MStatementReplaceable

- Implemented `replace(Statement thiz, Statement that)` which replaces the `Statement` in the first argument with that of the `Statement` in the second argument

* MStatementSearchable

- Added documentation for `search(TypeInfo_Class)` method

* Mcro

- Made `Repr` implement `MStatementSearchable`
- Added new interface `MCloneable` to represent PNode's which are deeply-cloneable

* Data

- Made `DiscardStatement` searchabe via implementing `MStatementSearchable`
- Added a stub override for implementing `MStatementReplaceable` in `DiscardStatement`

* Mcro

- Updated `MStatementReplaceable` to have its `replace(Statement, Statement)` method return a boolean `true` if the replacement was successful, else `false`

* Parsing

- Added the ability to parse a `Repr` statement

Check

- Added `GENERIC_TYPE_DECLARE` and `REPR` as new `SymbolType`(s)

Data

- `DiscardStatement` now implements the `bool replace(Statement, Statement)` method

MetaProcessor

- Added the ability to replace statements that occur within any _other_ statements which implement `MStatementSearchable` and `MStatementReplaceable`

Test cases

- Added `meta/simple_meta_replace.t` to test all of this

Diagrams

- Added diagram on how the meta system works

* MetaProcessor

- Removed unused `replace` method

* MetaProcessor

- Accept a new argument to the `MetaProcessor(TypeChecker)` constructor indicating whether or not the `MetaProcessor` is enabled or not

TypeChecker

- Enable the `MetaProcessor`

* Mcro

- Removed `Sizeof`, we will let it be parsed as a normal `FunctionCall` and then inspect in `MetaProcessor`

* MetaProcessor

- Disabled `sizeOf_Literalize` for now
- Search for all `FunctionCall` statements in `process(Container)`

* MetaProcessor

- Removed old code for testing `MStatementSearchable` and `MStatementReplaceable`
- Added note that we will have to investigate a recursive `MTypeRewritable` to be able to support things like `sizeof(size_t)`
- Implemented module-level `sizeof(<ident_type>)` support
- Added `Number`-kind of types support to `sizeOf_Literalize`(string)`

Containers (`Module`)

- Added `MStatementSearchable` and `MStatementReplaceable` support to `Module` container type

Data

- Added `MStatementSearchable` and `MStatementReplaceable` support to `Variable`
- Added `MStatementSearchable` and `MStatementReplaceable` support to `VariableAssignment`
- Work-in-progress for adding `MStatementSearchable` and `MStatementReplaceable` support to `FunctionCall`
- Added a note to return `false` in `DiscardStatement` if the statement to be replaced is us (the `DiscardSTatement`) ourselves

Test cases

- Updated the `meta/sizeof.t` test case

* Containers

- Inherit from `MStatementSearchable` and `MStatementReplaceable`
- Removed direct interface inheritance of `MStatementSearchable, MStatementReplaceable` and `MStatementReplaceable`, rely on `Container` now

* Struct

- Implemented the `search` method for `MStatementSearchable`
- Implemented the `replace` method for `MStatementReplaceable`

Clazz

- Implemented the `search` method for `MStatementSearchable`
- Implemented the `replace` method for `MStatementReplaceable`

BinaryOperatorExpression

- Implemented the `search` method for `MStatementSearchable`
- Implemented the `replace` method for `MStatementReplaceable`

Function

- Implemented the `search` method for `MStatementSearchable`
- Implemented the `replace` method for `MStatementReplaceable`

Variable

- Fixed the `replace` method implementation which had a bug that would never replace the node `VariableAssignment` inside of it

VariableAssignmentStdAlone

- Implemented the `search` method for `MStatementSearchable`
- Implemented the `replace` method for `MStatementReplaceable`

IfStatement

- Implemented the `search` method for `MStatementSearchable`
- Implemented the `replace` method for `MStatementReplaceable`

WhileLoop

- Implemented the `search` method for `MStatementSearchable`
- Implemented the `replace` method for `MStatementReplaceable`

ForLoop

- Implemented the `search` method for `MStatementSearchable`
- Implemented the `replace` method for `MStatementReplaceable`

Branch

- Implemented the `search` method for `MStatementSearchable`
- Implemented the `replace` method for `MStatementReplaceable`

* MetaProcessor

- Added a future TODO for occurence of certain types that are alises (i.e. `size_t` appearing in a expression context like `sizeof(size_t)`
- Now that all `Container`-based types are `MStatementReplaceable` we no longer need this check

* MetaProcessor

- Removed `break` which caused only one `sizeof()` function call to be replaced, we should have been looping over each `FunctionCall` found with `search` and then replacing ech that had a name of `sizeof` with a `NumberLiteral` expression
- Moved all type alias replacement code into a new method; `doTypeAlias(Container, Statement)`
- Added some rudiementary support for replacing any `IdentExpression` containing `size_t` with `uint`

IdentExpression

- Made it `MStatementSearchable` and `MStatementReplaceable`

Test cases

- Updated test case `meta/sizeof.t` to test the `sizeof(<expr>)` AST node in the `MetaProcessor`

Documentation

- Added diagram showing the `MStatementSearchable` and `MStatementReplaceable` in action

* Compiler

- If the T compiler was built on `X86` then set the maximum width to 4 bytes
- If the T compiler was built on `X86_64` then set the maximum width to 8 bytes
- Pass in the `Compiler` instance to the `TypeChecker` in `doTypeCheck()`

TypeChecker

- Added `Compiler` field and now accept it in constructor
- If just single parameter constructor then pass in `null` as the `Compiler` instance
- Added `getCompiler()` to get the instance

MetaProcessor

- Extract the `CompilerConfiguration` via the `TypeChecker`'s `getCompiler()`
- Implemented `getSystemType(string)` which will map `size_t`/`ssize_t` to the correct maximum width'd type your system supports via the `types:max_width` config entry

* CompilerConfiguration

- Added `defaultConfig()`

* Compiler

- Removed `defaultConfig()`
- During construction call `CompilerConfiguration.defaultConfig()` in order to generate the default config
- Pass the config into `TypeChecker` in `doTypeCheck()`

* TypeChecker

- No longer store a field for the `Compiler` but rather store a field for the `CompilerConfiguration`
- Removed single parameter constructor for `TypeChecker`
- Constructor now uses the default `CompilerConfiguration` if not specified; therefore fixing the issue with unit tests failing

MetaProcessor

- Extract the compiler configuration via `tc.getConfig()`
- Updated `typeRewrite(MTypeRewritable)` to use `getSystemType(string)` to lookup concrete types for `size_t`/`ssize_t`
- `doTypeAlias(Container, Statement)` now uses `getSsstemType(string)` to lookup the concrete types for alises such as `size_t`/`ssize_t`

* MetaProcessor

- Implemented `isSystemType(string)` which returns `true` if the provided type is a system type alias (such as `size_t` or `ssize_t`), `false` otherwise

* MetaProcessor

- Implemented `isTypeAlias(string)` which determines if the given type is a type alias.
- Implemented `getConcreteType(string typeAlias)` which transforms the type alias into its
concrete type; this method incorporates defensive programming in that it will only apply the transformation IF
the provided type alias is infact a type alias, otherwise it performs an identity transformation
and returns the "alias" untouched.

* TypeChecker

- Clean up

* MetaProcessor

- `doTypeAlias(Container, Statement)` now makes use of `isTypeAlias(string)` and `getConcreteType(string)`

* - `typeRewrite(MTypeRewritable)` now makes use of `isTypeAlias(string)` and `getConcreteType(string)`

* MetaProcessor

- Cleaned up

* MetaProcessor

- Removed unused import

* MetaProcessor

- Removed now-completed TODO

* MetaProcessor

- Updated comment before call to `doTypeAlias(Container, Statement)`

* Containers

- `Module` now applies re-parenting in its `replace()`
- `Struct` now applies re-parenting in its `replace()`
- `Clazz` now applies re-parenting in its `replace()`

Data

- `Function` now applies re-parenting in its `replace()`

* Test cases

- Added `simple_template_type_def.t` for future test cases

* - Updated `.gitignore`

* Check

- Removed `SymbolType.REPR` which was used for testing early versions of the `MetaProcessor`

* Mcro

- Removed `Repr` which was used for testing in the early stages of `MetaProcessor`

* Check

- Removed reference to `SymbolType.REPR` in checker

* Parser

- Removed reference to `SymbolType.REPR` and `Repr` which was used for testing the `MetaProcessor` in early stages

* 🧹🧼️ Cleanup: Clean up series 1 (#16)

* ️ Feature: Lexer Interface cleanup (#14)

* LexerInterface

Defined the lexer interface

* Parser

- Fixed import for `Token` class
- Removed the token management fields such as `tokens`, `currentToken` and `tokenPtr` as these are now replaced by our `LexerInterface`, `lexer` field which manages this all for us
- Removed constructor which accepts a `Token[]`, now onyl accept a `LexerInterface`
- Removed `nextToken()`, `hasTokens()`, `getCurrentToken()`, `previousToken()`, `setCursor(ulong)` and `getCursor()`.
- The above now are called via the `lexer` instance

Parser (unit tests)

- Migrated to new `LexerInterface`+`BasicLexer` system
- Hoisted out common imports for unit tests into a `version(unittest)`

TypeChecker (unittests)

- Hoisted out common imports for unit tests into a `version(unittest)`
- Migrated to new `LexerInterface`+`BasicLexer` system

LexerInterface

- Moved to new `lexer.core` package
- Documented module and class

Commands

- Fixed imports for the (now) `BasicLexer`
- Fixed imports for the (now) `lexer.core` package

Compiler

- Fixed imports for the (now) `BasicLexer`
- Use `LexerInterface` instead of `Lexer`
- The `doLex()` method now uses an instance of `BasicLexer` and then downcasts to quickly call `performLex()` in order to tokenize and make them available
- The `doParse()` method now takes in an instance of `LexerInterface` rather than `Token[]`

BasicLexer (previously Lexer)

- Moved to the `lexer.kinds` package
- Now implements `LexerInterface`
- Documented module and class
- Documented the `LexerInterface` methods

Exceptions

- Moved to the `lexer.core` package
- Fixed import of `Token` class
- Now uses `LexerInterface`

Core.Lexer.Package

- Documented package module

Tokens

- Moved to the `lexer.core` package
- Documented module and class

Check

- Fixed import for `Token`
- Fixed import for `BasicLexer`

* `core.lexer` (package)

- Documented all public imports

* Exceptions

- Documented the module
- Documented `LexerError` and its members
- Documented `LexerException`, its members too

* Tokens

- Documented the fields (using proper syntax)
- Documented constructor and methods

* BasicLexer

- Removed now-completed TODO
- Added (for clarity) `override` keywords to the `getLine()` and `getColumn()` methods
- Moved `getLine()`, `getColumn()` and `getTokens()` altoghether
- Made `getTokens()` override-marked
- Documented `getTokens()`

* Check

- Removed weird TODO that makes no sense
- Documented some of the members of `SymbolType`

* Check

- Documented a few more enum members of `SymbolType`
- Fixed documentation (and added a TODO) for the `SymbolType.LE_SYMBOL`

* Check

- Documented a few more enum members of `SymbolType`

* Check

- Documented `isType(string)`
- Added a TODO for `isTYpe(string)` to  "Check if below is even used
- Documented `isPathIdentifier(string)`

* Check

- Updated description of `isPathIdentifier(string)` to note it can contain underscores
- Documented isIdentifier(string)`
- Updated `SymbolType.IDENT_TYPE` to acknowledge underscores
- Documented `isAccessor(Token token)` and `isModifier(Token)`

* Check

- Documented `isIdentifier_NoDot(Token tokenIn)`, `isIdentifier_Dot(Token tokenIn)`, `isNumericLiteral(string token)`
- Removed uneeded import of `BasicLexer`
- Moved import to the top of file

* Check

- Documented `getSymbolType(Token tokenIn)`, `isMathOp(Token token)`, `isBinaryOp(Token token)`

* Check

- Documented the `symbols.check` module

* Builtins

- Properly documented `getBuiltInType(TypeChecker, string)`

* Builtins

- Documented module

* Typing (core)

- Documented module
- Documented all members

* Exceptions (lexer)

- Fixed documentation missing parameters

* Check

- Make comments docs/ddox compatible

* BasicLexer

- Fixed parameter name in documentation

* BasixLexer

- Fixed formatting in documentation for class

* Typing (core)

- Documented all remaining class members and fields

* - Update `.gitignore`

* Commands

- Handle `Exception` as well, I know DGen emits this on CC error so we should DO this to make tests more accurate

* Commands

- Fixed variable usage

* Instructions

- Made `BinOpInstr`'s left-hand side instruction of type `Value` and right-hand side instruction of type `Value` too
- Doesn't make sense to use anything but `Value`-based instructions for it

* Containers

- Removed redundant import that causes compilation failure on `ldc2`

* 🐞️ Functions: Expressionless return and enforcing requirement (#7)

* Parser

- Added a TODO in `parseReturn()` for issue #113

* Data

- The `ReturnStmt` now has a default constructor which is for cases where one doesn't want to provide an expression (for expressionless returns)

Parser

- `parseReturn()` now supports expressionless returns

Test cases

- Added `simple_return_expressionless.t` to test expressionless return statement

* Data

- Added a method `hasReturnExpression()` to `ReturnStmt` which returns `true` if the return statement has an expression attached, `false` otherwise

* Dependency

- When processing a `ReturnStmt` only run do dependency generation for the return statement's expression IF it has one

* Instruction

- Made `ReturnInstruction` have a constructor which takes in no `Value` instruction (intended for return expression)
- Added a `hasReturnExpInstr()` to `ReturnInstruction`such that during typechecking/codegen we can check for it

* TypeChecker

- Added a TODO regarding the missing typechecking for `ReturnStmt` typechecking. Added notes on how we'd go about this.
- Fixed crash due to assuming there was always an expression on the stack that could be popped off for generating a `ReturnInstruction` (this is not the case when the return statement is expressionless)

* Tests

- Added a typecheck test for `simple_return_expressionless.t`

* TypeChecker

- Update `isSameType(Type t1, Type t2)` to check if the actual types of both `Type` objects are the same as a last resort
- Added a `NOTE` comment on how `isSameType(Type t1, Type t2)` is implemented

- Added typechecking code for `ReturnStmt` and updated the code generation with it. We now do the following:
    1. We extract the container of the `ReturnStmt` and cast it to a `Function`; if it is not a `Function` we throw an error because you cannot have a `ReturnStmt` appear in a non-`Function` container
    2. We extract the function's name relative to it container (the function's container) for use of it in error messages
    3. Next, we get the return type of the function and do the following:
        a. If the return type is `void`
            i. If the return has an expression we throw an error
            ii. If the return has NO expression we pass typechecking and generate the `ReturnInstr`
        b. If the return type is non-`void`
            i. If the return has an expression we ensure that its type matches that of the function's return type and generate the `ReturnInstr`
            ii. If the return has NO expression we raise an exception as one is expected
    4. If we pass and got here then we set the `ReturnInstr`'s context and `addInstrB(returnInstr)`

* Test cases

- Added test case `simple_return_type.t` which is here to test our return type checking

* - Updated `.gitignore`

* Parser

- Use `lexer` for all `Token`-based operations

* Resolution

- Implemented `findContainerOfType(TypeInfo_Class, Statement)` which, given a type-of `Container` and a starting `Statement` (AST node) this will swim upwards to try and find the first matching parent of which is of the given type (exactly, not kind-of).

* TypeChecker

- Fixed the `Function` `Container` resolution for the `ReturnStmt` to now find the nearest `parent` in its parenthood tree which is a `Function`, extract the `Function`'s name and then use that where needed

* 🐞 Bugfix: Ensure that typed functions contain a return statement (#17)

* Parser

- Added TODO for checking for non-`void` function's return statements

* Parser

- If a `ReturnStmt` is not found after calling `parseFuncDef()`

* Parser

- Added a FIXME comment that needs to be completed soon

* Parser

- Only check that a `ReturnStmt` is present in a function definition when `wantsBody` is true ELSE we'd expect it for `efunc` `extern`'d statements
- Fixed a unit test which had a missing `return <expr>`
- Added a unit test which tests for a function definition with a body with a non-void return type that it DOES fail
- Removed two now-completed TODOs and FIXMEs

Test cases

- Fixed test case in `typecheck/simple_function_call.t` to now include the required `return <expr>` statements

* 🐞 Bugfix: Recursively check for return statemen tprescence (#19)

* Parsing

- Added a TODO for where the fix needs to be

* Parser

- Implemented `findOfType(TypeInfo_Class statementType, Container from)` which given a type of `Statement` to look for and a `Container` of which to search with in. This method will recursively search down the given container and look for any statements which are a kind-of (`isBaseOf`) the requested type. it will return an array of `Statement` (`Statement[]`) of the matches.
- Implemented `existsWithin(TypeInfo_Class statementType, Container from)` given a type of `Statement` to look for and a `Container` of which to search with in. This method will recursively search down the given container and look for any statements which are a kind-of (`isBaseOf`) the requested type. It will return `true` if any matches are found.
- We now will recursively explore the `Function`'s body statements in search of a `ReturnStmt`

* Parser (unit tests)

- Updated unit test which was meant to check for a greater number of body statements of the `Function` at question

* Parser (unit tests)

- Because this causes `expect(string)` to be called (this unit test) we must catch `TError` as that is what `expect(string)` throws
- We should update this (see https://deavmi.assigned.network/git/tlang/tlang/issues/147)

* Tets cases

- Added missing `return` to function `banana` in `simple_functions.t` test case

* Test cases

- Fixed `simple_function_decls.t` to include the required (but missing) `return` statement

* 🐞 Bugfix: expect(string) should throw ParserException atleast (#20)

* Parser

- Removed commented-out code

* Parser

- Removed `isUnitTest` variable and how `expect(string)` relies on it

* Parser

- Now throw a new `ParserException` instead of a `TError` when calling `expect(string)`

* Parser

- Made `expect(string)` not static

* TypeChecker

- Implemented `expect(string)` which throws a `TypeCheckerException` in a similar fashion to `Parser`'s `expect(string)`

* Dependency

- Implemented `expect(string)` which throws a `DependencyException` in a similar fashion to `Parser`'s `expect(string)`

Exceptions (dependency)

- Added enum member `GENERAL_ERROR` to `DependencyError`

* Parser (unit tests)

- Made more specific

* Parser

- Removed now-completed TODO

* Data

- Removed unused type `ArgumentList`

* Expressions

- Made the `Expression` class abstract
- Removed irrelevant TODOs and method
- Removed uneeded constructor

VariableExpression

- Removed unused junk

* 🧠 Feature/Meta: Cloneable (round 1) (#21)

* Data

- Moved AST manipulation imports to the top
- Made `VariableAssignment` cloneable
- Made `Variable` cloneable

* Data

- When trying to `clone()` an `Expression`, do a runtime type check to check if we can (else `null` is used)

* Expressions

- Moved AST-manipulation related import to the top

* Expressions

- `BinaryOperatorExpression` now overrides `clone()` from `MCloneable`

* Expressions

- `IntegerLiteral` now implements `MCloneable`

* Expressions

- `CastedExpression` now implements `clone()` for `MCloneable`

* Containers

- Moved all AST maniuplation-related imports to the top

* Containers

- Made `Struct` support `MCloneable`'s `clone()` method

* Data

- Only clone the `VariableAssignment` if the `Variable` has one

* Containers (unit test)

- Added test which tests the `clone()` on a `Struct`

* DGen

- If `typeTransform(Type)` fails then the debug text should be in the error coloring

* DGen

- If `typeTransform(Type)` is called on an unknown type then print out the type in the error message asx well

* Mcro

- Made `MCloneable`'s `clone()` require a `Container` argument of which the final cloned `Statement` must parent-itself to

* Expressions

- `BinaryOperatorExpression`, `CastedExpression` and `IntegerLiteral`  now implements the new `MCloneable` API

* Expressions

- Added notes about where parenting must manually be done

* Data

- `Variable` and `VariableAssignment` now uses the new `MCloneable` API

* Containers

- `Struct`'s `MCloenable` API adhered to

* Containers

- Fixed `clone(Container)` implementation for `Struct` whereby it would not parent the newly created copy

* TypeChecker

- Added a TODO regarding the `ClassStaticNode` handling

* TypeCheck

- Undo incorrect FIXME

* CodeEmitter

- Added a new parameter to `transform(Instruction)` (now `transform(Instruction, Object)`. `customRules` is an `Object` to be interpreted by the underlying emitter which can change how certain transformations are done when it is in a certain state

DGen

- Uses new `CodeEmitter` API

* Revert "CodeEmitter"

This reverts commit 75372ee132.

* DGen

- Added ability to flag whether symbol mapping should occur or not
- By default enable symbol mapping

* Variable

- When calling `replace(Statement, Statement)`, if we hit the `variableAssignment` replacement check, first check and ensure it is not null`, try

* 🧠️ Feature: Universal coercion and type enforcer (#9)

* TypeChecker

- Added `bool isSameType2(Value v1, Value v2, bool attemptCoercion = false)` for future implementation of universal coercion as per #115

* TypeChecker

- Renamed `isSameType2` to `typeEnforce`
- Updated `typeEnforce`'s default parameter documentation from `false` to `attemptCoercion` (as it should have been in the beginning)

* TypeCheckerException

- Save the `TypecheckError` coming in as `errType` and make it available via `getError()`

TypemMismatchException

- Save the original (expected) and attempted types and make them available via `getExpectedType()` and `getATtemptedType()` respectively

* TypeChecker

- Updated `typeEnforce` from taking in `Value v1, Value v2, bool` to `Type t1, Value v2, bool`.
- `typeEnforce()` will now extract the `Type` of `Value v2` and call `isSameType(t1, t2)`, if that fails and coercion is allowed then it is attempted, however if that fails then it causes an exception to be thrown. In the case coercion is not allowed, then a `TypeMismatchException` is thrown

Unit tests

- Tested the new `typeEnforce(Type t1, Value v2, bool)` and it seems to work, both a case of failing matching (coercion disallowed) and working coercion (coercion allowed)

* TypeChecker

- Documented existing unittest for `typeEnforce(Type, Value, bool)`
- Added new unit test for `typeEnforce(Type, Value, bool)` which tests when the types ARE the same

* TypeChecker

- Cleaned up `typeEnforce(Type, Value, bool)`

* TypeChecker

- Added a work-in-progress unit test to test how I would use `typeEnforce(Type t1, Value v2, bool coercion = false)` in practice
- Added TODOs in `attemptCoercion(Type, Type)` where I must add support

* TypeChecker

- Finished the unit test testing out the usage for `typeEnforce(Type, Value, bool coerce = false)`
- Added TODOs to `attemptCoercion(Type, Value)` for the changes required to it

* TypeChecker

- Removed incorrect TODOs from `attemptCoerce(Type, Value)` and updated the message when the coercion fails

Unit tests

- Updated first unit test for `typeEnforce()` to test failing coercion on a non-`LiteralValue` instruction
- Added a unit test where `typeEnforce()` WILL pass as it coerces a `LiteralValue` instruction

* Exceptions (`typechecker`)

- Added new exception type `CoercionException` to be thrown whenever a coercion cannot take place.

* TypeChecker

- Ensure that `attemptCoercion(Type, Value)` only throws instances of `CoercionException`

* Unit tests

- Fixed failing-coercion check by catching the correct exception when it fails `CoercionException` instead of `TypeMismatchException`)

* TypeChecker

- Added documentation for `isSameType(Type t1, Type t2)`

* TypeChecker

- Updated documentation for `isCoercibleRange(Type, Value)`
- Updated `attemptCoercion(Type, Value)` with new documentation and renamed parameters

* Unit tests (typechecker)

- Added comments

* TypeChecker

- Removed now-completed TODO in `typeEnforce(Type t1, Value v2, bool allowCoercion = false)`

* TypeChecker

- Removed unused `typeStatus` variable in `typeEnforce(Type, Value, bool)`

* TypeChecker

- Variable declarations (with assignments) now use the `typeEnforce()` method with coercion allowed in order to do the type checking and coercion changes
- Added a comment explaining a certain branch of `attemptCoercion(Type, Value)`

* TypeChecker

- If the to-type and provided-type are both numerical then use a size-based test

Test cases

- Added two test cases which test `typeEnforce()` on incoming `Value`-based instructions as part of variable declarations

* Test cases

- Fixed negative test case - it MUST have an error and that should be seen as a pass

* TypeChecker (unit tests)

- Disabled invalid unit test (marked for re-writing)
- I should re-write the below. It is now incorrect as I DO ALLOW coercion of non literal-based instructions now - so it fails because it is using an older specification of TLang

* TypeChecker

- Migrated the type checking of standalone variable assignments to using `typeEnforce()`

Test cases

- Added positive and negative test cases

* - Updated `.gitignore`

* Feature/type enforcer cast instr emit (#13)

* TypeChecker

- `typeEnforce()` now will not change the type of `Value`-based instruction `v2` but rather return, on successful coercion set a `ref`-based argument to a new instance of a `CastedValueInstruction`, if coercion fails or was disabled and types mismatched then an exeption is thrown as normal.
- If the types are an exact same match, a-la `isSameType(Type, Type)`, then this `ref` value is set to `v2` (makes programming easy) else we would have no way to know
- `attemptCoerce()` now, to go with the above changes to `typeEnforce()`, returns a `CatsedValueInstruction` to the to-type on successful coercion, else an exception is thrown as usual
- Updated two cases of `typeEnforce()` usage to the new method signature, also now add a sanity check assertion that the types now DO match as they should

* TypeChecker

- We need not set it again, look the value we use when we CALL `typeEnforce()` is that of the `fromInstruction` and if no changes occur we still have it, it is fine - if it changes via the call to `typeEnforce()` via the `ref` based argument thne same old
- No need for us to set it here in the event of no changes, we are writing back the exact same Instruction/object-reference

* TypeChecker (unit tests)

- Upgraded to the new `typeEnforcer()` method signature

* TypeChecker

- Improved documentation for `typeEnforce()`

* TypeChecker

- Added TODO regarding pointer coercion with integers in `Pointer + Integer` case (for pointer airthmetic)

* TypeChecker

- Added a new branch which currently throws an exception as it is unimplememted
- This branch (above) is in `attemptCoercion()` and is to handle the coercion of `Integer` to `Pointer` for pointer arithmetic
- When doing the typechecking/codegen for `BinaryOp`, disable the pointer coercion call to `attemptPointerAriehmeticCoercion()`, instead now make calls in those cases they apply, to `typeEnforce()`

- The above stuff is still broken, not yet implemented.

* TypeChecker

- Cannot use cast as that can return false positives for an all pointer case as all `Pointer`s are `Integer`s
- Added `isPointerType(Type)` to check the above
- Added then also `isIntegralTypeButNotPointer(Type)` which checks for an `Integer` type but excluding if it is a `Pointer`
- Updated the checks in the `BinaryOperator` branch of `typeCheckThing(DNode)` to do this

* TypeChecker

- Need to do the `Pointer` checks first in `attemptCoercion(Type, Value)`

* TypeChecker

- `attemptCoercion(Type, Value)` now returns a `CastedValueInstruction` to cast the `Integer` type to the `Pointer` type

* TypeCHecker

- Catch mis use of type enforcement by using `isIntegralTypeButNotPointer(Type)` and isPointerType`(Type)` for the previous commit

* TypeChecker

- Refresh the types after the potential calls to `typeEnforce(..., ..., ..., ...)`

* Pipeline

- Use `set -e` for `simple_pointer.t` test in emit stage

* Pipelines (emit stage)

- Previous compilation may have succeeded, meaning ./tlang.out never gets updated and exits fine with 0, but we only use the last commands exit status to check for a pass for a test.
- By setting this if COMPILATION fails then we exit with its code and the test status is set via that

* Pipelines

- Removed the `set -e` code as the correct `Exception` now causes a non-zero exit code from the changes made in `varass_vardec_dependency`

* DGen

- Added notice for issue #140

* TypeChecker

- Made `isIntegralTypeButNotPointer(Type)` public
- Made `isPointerType(Type)` public

* Instructions

- `CastedValueInstruction` now is unrelaxed by default but can be set (tis aids in how it can be emitted later for issue #140)

* DGen

- Added some checks for certain conditions whereby pointer coercion requires relaxing the casted operands (coerced operands)

* DGen

- Relax `CastedValueInstruction`(s) when appropriate in `BinaryOpInstr` handling code
- Removed panics

* DGen

- Added relaxation support to the code emitting code for `CastedValueInstruction`

* DGen

- make debug messages for when relaxation occurs for `CastedValueInstruction` emitting more clear

* TypeChecker

- Implemented `biggerOfTheTwo(Integer, Integer)` which determines the biggest of the two `Integer`-based types and returns that one.

* TypeChecker

- Fixed incorrect variable name in `biggerOfTheTwo(Integer, Integer)`

* TypeChecker

- Throw an error in the case where a `BinaryOperatorExpression` occurs with non-`Integer`-based instructions (at least for now)

* TypeChecker

- If both types are `Integral` (but not `Pointer`) then smaller coerces to bigger, if they however are equal then signed coerces to unsigned

* TypeChecker

- Removed now irrelevant comment

* TypeChecker

- Don't throw exception here, rather let the `isSameType(Type, Type)` check handle that
- We still keep the warning we print about missing cases implementation-wise

* TypeChecker

- Fixed explanation

* TypeChecker

- Marked related issue

* TypeChecker

- Implemented ` isStackArrayType(Type typeIn)`
- WIP: Added a check for handling `StackArray -> Pointer` coercion to `attemptCoercion(Type, Value)`

* TypeChecker

- `attemptCoercion(Type, Value)` will now ensure firstly that the `StackArray`'s component type matches that of the `Pointer`'s referred type, if not throw an exception, if so, then return a `CastedValueInstruction`

* TypeChecker

- Print out a debug message when attempting to coerce a `StackArray` to a `Pointer`
- Fixed the error message thrown when a `StackArray` could not be coerced to a `Pointer` due to the component type != ptr's referred type
- `FunctionCall` handling now has the `canCoerceStackArray()` code disabled and uses the `typeEnforce()` method

* TypeChecker

- Type checking code for `FunctionCall`

* TypeCheck

- Completed TODO comment

* TypeChecker

- Added a TODO

* TypeChecker

- Added FIXME where the `typeEnforce()` call need to be made for the `ReturnStmt`'s return expression's type to match or be checked-against the containing `Function`'s

* TypeChecker

- `ReturnStmt` now uses `typeEnforce()`

* Test cases

- Added two new checks for checking the return type of a function and matching a `ReturnStmt`'s expression's type to it

* TypeChecker

- Removed assertion check, rather let the exception thrown handle the error
- Only after we know the finally-parenting `Container` is a `Function` (should we reference `funcContainer`

* Test cases

- Removed explicit cast from `simple_function_recursion_factorial.t`

* TypeChecker

- If we have a `LiteralValue` and a non-`LiteralValue` then coerce the `LiteralValue` towards the non`-LiteralValue` via `typeEnforce()`
- This should allow the correct range checking of literal values within the range of the to-type and not require annoying explicit casts

* Test cases

- Removed now-unneeded explicit casts on literal values in `simple_function_recursion_factorial.t`

* TypeChecker

- Added comment describing the process used
- Removed now-completed TODO

* TypeChecker

- Removed some dead code
- Removed now-completed FIXME/TODO

* TypeChecker

- Removed old type checking code for variable declarations with assignments
- Removed old type checking code for standalone variable assignments

* Dub

- Upgraded `jcli` to version `0.25.0-beta.2`

* Dub

- Upgtraded package `jcli` to version `0.25.0-beta.3`

* - Try using latest version of `dmd`
master
Tristan B. V. Kildaire 9 months ago committed by GitHub
parent e5610be6f2
commit 01afa43e93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -20,9 +20,9 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
dc: [dmd-2.101.0]
dc: [dmd-latest]
exclude:
- { os: macOS-latest, dc: dmd-2.085.0 }
- { os: macOS-latest, dc: dmd-latest }
runs-on: ${{ matrix.os }}
steps:
@ -100,6 +100,24 @@ jobs:
fi
- name: Simple string
run: ./tlang syntaxcheck source/tlang/testing/typecheck/simple_string.t
- name: Simple return (good)
run: |
./tlang syntaxcheck source/tlang/testing/return/simple_return_good.t
- name: Simple return (bad return position)
run: |
set +e
./tlang syntaxcheck source/tlang/testing/return/simple_return_bad.t
if [ $? = 255 ]
then
exit 0
else
exit 1
fi
- name: Simple return (expressionless)
run: ./tlang syntaxcheck source/tlang/testing/return/simple_return_expressionless.t
typecheck:
needs: [build, unittests]
@ -115,6 +133,14 @@ jobs:
- name: Chmod compiler
run: chmod +x tlang
- name: Simple return (expressionless)
run: ./tlang typecheck source/tlang/testing/return/simple_return_expressionless.t
- name: Simple return (with expression)
run: ./tlang typecheck source/tlang/testing/return/simple_return_type.t
- name: Simple function call
run: ./tlang typecheck source/tlang/testing/typecheck/simple_function_call.t
@ -297,6 +323,51 @@ jobs:
run: ./tlang typecheck source/tlang/testing/simple_literals5.t
- name: Simple literals 6
run: ./tlang typecheck source/tlang/testing/simple_literals6.t
# All the universal coercion tests are below
#
# Over time those above will be ported over to it and will
# infact make part of the test suite of typeEnforce()
- name: Simple Coerce Literal Good (to variable declaration)
run: ./tlang typecheck source/tlang/testing/universal_coerce/simple_coerce_literal_good.t
- name: Simple Coerce Literal Bad [Size loss] (to variable declaration)
run:
set +e
./tlang typecheck source/tlang/testing/universal_coerce/simple_coerce_literal_bad.t
if [ $? = 255 ]
then
exit 0
else
exit 1
fi
- name: Simple Coerce Literal Good (standalone variable assignment)
run: ./tlang typecheck source/tlang/testing/universal_coerce/simple_coerce_literal_good_stdalo.t
- name: Simple Coerce Literal Bad [Size loss] (standalone variable assignment)
run:
set +e
./tlang typecheck source/tlang/testing/universal_coerce/simple_coerce_literal_bad_stdalon.t
if [ $? = 255 ]
then
exit 0
else
exit 1
fi
- name: Function return expression coercion (good)
run: ./tlang typecheck source/tlang/testing/simple_function_return_type_check_good.t
- name: Function return expression coercion (bad)
run:
set +e
./tlang typecheck source/tlang/testing/simple_function_return_type_check_bad.t
if [ $? = 255 ]
then
exit 0
else
exit 1
fi
emit:
needs: [build, unittests]
@ -310,12 +381,24 @@ jobs:
name: tbin
- name: Chmod compiler
run: chmod +x tlang
run: chmod +x tlang
- name: Simple functions
run: |
./tlang compile source/tlang/testing/simple_functions.t
./tlang.out
- name: Simple functions (statement-level)
run: |
./tlang compile source/tlang/testing/simple_direct_func_call.t
./tlang.out
- name: Simple functions (recursive)
run: |
./tlang compile source/tlang/testing/simple_function_recursion_factorial.t
./tlang.out
# TODO: Re-enable when we support the `discard` keyword again
#- name: Simple variables
# run: |

4
.gitignore vendored

@ -17,3 +17,7 @@ dub.selections.json
# Documentation )generated)
docs/
docs.json
# Codium workspace files
testing.code-workspace
desktop_dev.code-workspace

@ -5,7 +5,7 @@
"copyright": "Copyright © 2021-2023, Tristan B. Kildaire",
"dependencies": {
"gogga": "~>0.1.0",
"jcli": "0.25.0-beta.1"
"jcli": "0.25.0-beta.3"
},
"description": "The official Tristan language compiler project",
"license": "GPLv3",

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

@ -10,8 +10,8 @@ import jcli;
import std.stdio;
import misc.exceptions : TError;
import std.exception : ErrnoException;
import tlang.compiler.lexer.core : Lexer;
import tlang.compiler.lexer.tokens : Token;
import tlang.compiler.lexer.kinds.basic : BasicLexer;
import tlang.compiler.lexer.core;
import tlang.compiler.parsing.core : Parser;
import tlang.compiler.typecheck.core : TypeChecker;
import gogga;
@ -170,6 +170,11 @@ struct compileCommand
writeln("Could not open source file "~sourceFile);
exit(-2);
}
catch(Exception e)
{
gprintln(e.msg, DebugType.ERROR);
exit(-1);
}
}
}

@ -22,10 +22,35 @@ import tlang.compiler.configuration : CompilerConfiguration;
public final class DCodeEmitter : CodeEmitter
{
/**
* Whether or not symbol mappi g should
* apply to identifiers
*/
private bool symbolMapping;
// NOTE: In future store the mapper in the config please
this(TypeChecker typeChecker, File file, CompilerConfiguration config, SymbolMapper mapper)
{
super(typeChecker, file, config, mapper);
// By default symbols will be mapped
enableSymbolMapping();
}
/**
* Enables symbol mapping
*/
private void enableSymbolMapping()
{
this.symbolMapping = true;
}
/**
* Disables symbol mapping
*/
private void disableSymbolMapping()
{
this.symbolMapping = false;
}
private ulong transformDepth = 0;
@ -104,7 +129,7 @@ public final class DCodeEmitter : CodeEmitter
// return "KAK TODO";
}
gprintln("Type transform unimplemented");
gprintln("Type transform unimplemented for type '"~to!(string)(typeIn)~"'", DebugType.ERROR);
assert(false);
// return stringRepr;
}
@ -249,6 +274,52 @@ public final class DCodeEmitter : CodeEmitter
// TODO: I like having `lhs == rhs` for `==` or comparators but not spaces for `lhs+rhs`
/**
* C compiler's do this thing where:
*
* If `<a>` is a pointer and `<b>` is an integer then the
* following pointer arithmetic is allowed:
*
* int* a = (int*)2;
* a = a + b;
*
* But it's WRONG if you do
*
* a = a + (int*)b;
*
* Even though it makes logical sense coercion wise.
*
* Therefore we need to check such a case and yank
* the cast out me thinks.
*
* See issue #140 (https://deavmi.assigned.network/git/tlang/tlang/issues/140#issuecomment-1892)
*/
Type leftHandOpType = (cast(Value)binOpInstr.lhs).getInstrType();
Type rightHandOpType = (cast(Value)binOpInstr.rhs).getInstrType();
if(typeChecker.isPointerType(leftHandOpType))
{
// Sanity check the other side should have been coerced to CastedValueInstruction
CastedValueInstruction cvInstr = cast(CastedValueInstruction)binOpInstr.rhs;
assert(cvInstr);
gprintln("CastedValueInstruction relax setting: Da funk RIGHT ");
// Relax the CV-instr to prevent it from emitting explicit cast code
cvInstr.setRelax(true);
}
else if(typeChecker.isPointerType(rightHandOpType))
{
// Sanity check the other side should have been coerced to CastedValueInstruction
CastedValueInstruction cvInstr = cast(CastedValueInstruction)binOpInstr.lhs;
assert(cvInstr);
gprintln("CastedValueInstruction relax setting: Da funk LEFT ");
// Relax the CV-instr to prevent it from emitting explicit cast code
cvInstr.setRelax(true);
}
return transform(binOpInstr.lhs)~to!(string)(getCharacter(binOpInstr.operator))~transform(binOpInstr.rhs);
}
/* FuncCallInstr */
@ -293,6 +364,12 @@ public final class DCodeEmitter : CodeEmitter
emit ~= ")";
// If this is a statement-level function call then tack on a `;`
if(funcCallInstr.isStatementLevel())
{
emit ~= ";";
}
return emit;
}
/* ReturnInstruction */
@ -506,6 +583,19 @@ public final class DCodeEmitter : CodeEmitter
string emit;
/**
* Issue #140
*
* If relaxed then just emit the uncasted instruction
*/
if(castedValueInstruction.isRelaxed())
{
/* The original expression */
emit ~= transform(uncastedInstruction);
return emit;
}
/* Handling of primitive types */
if(cast(Primitive)castingTo)
{
@ -963,6 +1053,41 @@ int main()
assert(t_7b6d477c5859059f16bc9da72fc8cc3b == 72);
printf("k: %u\n", t_7b6d477c5859059f16bc9da72fc8cc3b);
return 0;
}`);
}
// Test for `simple_function_recursion_factorial.t` (recursive function call testing)
else if(cmp(typeChecker.getModule().getName(), "simple_function_recursion_factorial") == 0)
{
file.writeln(`
#include<stdio.h>
#include<assert.h>
int main()
{
int result = factorial(3);
assert(result == 6);
printf("factorial: %u\n", result);
return 0;
}`);
}
// Test for `simple_direct_func_call.t` (statement-level function call)
else if(cmp(typeChecker.getModule().getName(), "simple_direct_func_call") == 0)
{
file.writeln(`
#include<stdio.h>
#include<assert.h>
int main()
{
// Before it should be 0
assert(t_de44aff5a74865c97c4f8701d329f28d == 0);
// Call the function
function();
// After it it should be 69
assert(t_de44aff5a74865c97c4f8701d329f28d == 69);
return 0;
}`);
}

@ -241,11 +241,11 @@ public final class StringLiteral : Value
*/
public class BinOpInstr : Value
{
public const Instruction lhs;
public const Instruction rhs;
public const Value lhs;
public const Value rhs;
public const SymbolType operator;
this(Instruction lhs, Instruction rhs, SymbolType operator)
this(Value lhs, Value rhs, SymbolType operator)
{
this.lhs = lhs;
this.rhs = rhs;
@ -297,6 +297,15 @@ public class CallInstr : Value
public class FuncCallInstr : CallInstr
{
/**
* This is described in the corresponding AST node
* `FunctionCall`. See that. For short, function calls
* from within expressions and those as appearing as statements
* require a tiny different code gen but for Instructions
* their emit also needs a tiny difference
*/
private bool statementLevel = false;
/* Per-argument instrructions */
private Value[] evaluationInstructions;
@ -328,6 +337,26 @@ public class FuncCallInstr : CallInstr
{
return evaluationInstructions;
}
/**
* Determines whether this function call instruction
* is within an expression or a statement itself
*
* Returns: true if statement-level, false otherwise
*/
public bool isStatementLevel()
{
return statementLevel;
}
/**
* Marks this function call instruction as statement
* level
*/
public void markStatementLevel()
{
statementLevel = true;
}
}
@ -340,10 +369,20 @@ public final class ReturnInstruction : Instruction
this.returnExprInstr = returnExprInstr;
}
this()
{
}
public Value getReturnExpInstr()
{
return returnExprInstr;
}
public bool hasReturnExpInstr()
{
return returnExprInstr !is null;
}
}
public final class IfStatementInstruction : Instruction
@ -496,10 +535,22 @@ public final class CastedValueInstruction : Value
/* The uncasted original instruction that must be executed-then-trimmed (casted) */
private Value uncastedValue;
/**
* Used in code emitting, this is related to
* #140. Really just a C+DGen thing.
*
* Signals that we shouldn't emit any special
* casting syntax in the underlying emitter.
*/
private bool relax;
this(Value uncastedValue, Type castToType)
{
this.uncastedValue = uncastedValue;
this.type = castToType;
// Relaxing is disabled by default
this.relax = false;
}
public Value getEmbeddedInstruction()
@ -511,6 +562,16 @@ public final class CastedValueInstruction : Value
{
return type;
}
public bool isRelaxed()
{
return relax;
}
public void setRelax(bool relax)
{
this.relax = relax;
}
}
public final class ArrayIndexInstruction : Value

@ -189,4 +189,43 @@ public final class CompilerConfiguration
ConfigEntry _discard;
return hasConfig_internal(key, _discard);
}
/**
* Generates the default compiler configuration
*
* Returns: a `CompilerConfguration`
*/
public static CompilerConfiguration defaultConfig()
{
/* Generate a fresh new config */
CompilerConfiguration config = new CompilerConfiguration();
/* Enable Behaviour-C fixes */
config.addConfig(ConfigEntry("behavec:preinline_args", true));
/* Enable pretty code generation for DGen */
config.addConfig(ConfigEntry("dgen:pretty_code", true));
/* Enable entry point test generation for DGen */
config.addConfig(ConfigEntry("dgen:emit_entrypoint_test", true));
/* Set the mapping to hashing of entity names (TODO: This should be changed before release) */
config.addConfig(ConfigEntry("emit:mapper", "hashmapper"));
/**
* Configure, at compile time, the system type aliases
*/
version(X86)
{
/* Set maximum width to 4 bytes (32-bits) */
config.addConfig(ConfigEntry("types:max_width", 4));
}
else version(X86_64)
{
/* Set maximum width to 8 bytes (64-bits) */
config.addConfig(ConfigEntry("types:max_width", 8));
}
return config;
}
}

@ -3,7 +3,7 @@ module tlang.compiler.core;
import gogga;
import std.conv : to;
import tlang.compiler.lexer.core;
import tlang.compiler.lexer.tokens : Token;
import tlang.compiler.lexer.kinds.basic : BasicLexer;
import std.stdio : File;
import tlang.compiler.parsing.core;
import tlang.compiler.symbols.check;
@ -96,7 +96,7 @@ public class Compiler
private string inputSource;
/* The lexer */
private Lexer lexer;
private LexerInterface lexer;
/* The lexed tokens */
private Token[] tokens;
@ -115,21 +115,6 @@ public class Compiler
/* The configuration */
private CompilerConfiguration config;
/* TODO: Make the default config */
private void defaultConfig()
{
/* Enable Behaviour-C fixes */
config.addConfig(ConfigEntry("behavec:preinline_args", true));
/* Enable pretty code generation for DGen */
config.addConfig(ConfigEntry("dgen:pretty_code", true));
/* Enable entry point test generation for DGen */
config.addConfig(ConfigEntry("dgen:emit_entrypoint_test", true));
/* Set the mapping to hashing of entity names (TODO: This should be changed before release) */
config.addConfig(ConfigEntry("emit:mapper", "hashmapper"));
}
public CompilerConfiguration getConfig()
{
@ -147,18 +132,16 @@ public class Compiler
this.inputSource = sourceCode;
this.emitOutFile = emitOutFile;
this.config = new CompilerConfiguration();
/* Enable the default config */
defaultConfig();
/* Get the default config */
this.config = CompilerConfiguration.defaultConfig();
}
/* Setup the lexer and begin lexing */
public void doLex()
{
/* Setup the lexer and begin lexing */
this.lexer = new Lexer(inputSource);
this.lexer.performLex();
this.lexer = new BasicLexer(inputSource);
(cast(BasicLexer)(this.lexer)).performLex();
this.tokens = this.lexer.getTokens();
}
@ -185,7 +168,7 @@ public class Compiler
else
{
/* Spawn a new parser with the provided tokens */
this.parser = new Parser(lexedTokens);
this.parser = new Parser(lexer);
modulle = parser.parse();
}
@ -207,7 +190,7 @@ public class Compiler
throw new CompilerException(CompilerError.PARSE_NOT_YET_PERFORMED);
}
this.typeChecker = new TypeChecker(modulle);
this.typeChecker = new TypeChecker(modulle, config);
/* Perform typechecking/codegen */
this.typeChecker.beginCheck();

@ -0,0 +1,74 @@
/**
* Exception definitions
*/
module tlang.compiler.lexer.core.exceptions;
import misc.exceptions : TError;
import tlang.compiler.lexer.core.lexer : LexerInterface;
import std.conv : to;
/**
* The specified error which occurred
*/
public enum LexerError
{
/**
* If all the characters were
* exhausted
*/
EXHAUSTED_CHARACTERS,
/**
* Generic error
*/
OTHER
}
/**
* Represents an exception that can occur
* when using a `LexerInterface`
*/
public final class LexerException : TError
{
/**
* The offending `LexerInterface` instance
*/
public const LexerInterface offendingInstance;
/**
* The sub-error type (specific error)
*/
public const LexerError errType;
/**
* Constructs a new `LexerException` with the given offending instance
* where the error occured from and the default error type and no
* custom message
*
* Params:
* offendingInstance = the offending `LexerInterface`
* errType = the sub-error type as a `LexerError`
* msg = the custom message (default is empty/`""`)
*/
this(LexerInterface offendingInstance, LexerError errType = LexerError.OTHER, string msg = "")
{
string positionString = "("~to!(string)(offendingInstance.getLine())~", "~to!(string)(offendingInstance.getColumn())~")";
super("LexerException("~to!(string)(errType)~")"~(msg.length ? ": "~msg : "")~" at "~positionString);
this.offendingInstance = offendingInstance;
this.errType = errType;
}
/**
* Constructs a new `LexerException` with the given offending instance
* where the error occured from and the default error type and a
* custom message
*
* Params:
* offendingInstance = the offending `LexerInterface`
* msg = the custom message
*/
this(LexerInterface offendingInstance, string msg)
{
this(offendingInstance, LexerError.OTHER, msg);
}
}

@ -0,0 +1,76 @@
/**
* Lexer interface definition
*/
module tlang.compiler.lexer.core.lexer;
import tlang.compiler.lexer.core.tokens : Token;
/**
* Defines the interface a lexer must provide
* such that is can be used to source tokens
* from in the parser
*/
public interface LexerInterface
{
/**
* Returns the token at the current cursor
* position
*
* Returns: the `Token`
*/
public Token getCurrentToken();
/**
* Moves the cursor one token forward
*/
public void nextToken();
/**
* Moves the cursor one token backwards
*/
public void previousToken();
/**
* Sets the position of the cursor
*
* Params:
* cursor = the new position
*/
public void setCursor(ulong cursor);
/**
* Retrieves the cursor's current position
*
* Returns: the position
*/
public ulong getCursor();
/**
* Checks whether more tokens are available
* of not
*
* Returns: true if more tokens are available, false otherwise
*/
public bool hasTokens();
/**
* Get the line position of the lexer in the source text
*
* Returns: the position
*/
public ulong getLine();
/**
* Get the column position of the lexer in the source text
*
* Returns: the position
*/
public ulong getColumn();
/**
* Exhaustively provide a list of all tokens
*
* Returns: a `Token[]` containing all tokens
*/
public Token[] getTokens();
}

@ -0,0 +1,19 @@
/**
* Base definitions relating to the lexer
*/
module tlang.compiler.lexer.core;
/**
* Lexer interface definition
*/
public import tlang.compiler.lexer.core.lexer;
/**
* Token definition
*/
public import tlang.compiler.lexer.core.tokens;
/**
* Exception definitions
*/
public import tlang.compiler.lexer.core.exceptions;

@ -0,0 +1,76 @@
/**
* Token definition
*/
module tlang.compiler.lexer.core.tokens;
import std.string : cmp;
import std.conv : to;
/**
* Defines a `Token` that a lexer
* would be able to produce
*/
public final class Token
{
/**
* The token
*/
private string token;
/**
* Line number information
*/
private ulong line, column;
/**
* Constructs a new `Token` with the given
* contents and line information
*
* Params:
* token = the actual string
* line = the line it occurs at
* column = the column it occurs at
*/
this(string token, ulong line, ulong column)
{
this.token = token;
this.line = line;
this.column = column;
}
/**
* Overrides the `==` operator to do equality
* based on the stored token's contents
*
* Params:
* other = the other `Token` being compared to
* Returns: true if the contents of the two tokens
* match, false otherwise
*/
override bool opEquals(Object other)
{
return cmp(token, (cast(Token)other).getToken()) == 0;
}
/**
* Rerturns a string representation of the token including
* its data and line information
*
* Returns: a `string`
*/
override string toString()
{
/* TODO (Column number): Don't adjust here, do it maybe in the lexer itself */
return token~" at ("~to!(string)(line)~", "~to!(string)(column-token.length)~")";
}
/**
* Returns the token's contents
*
* Returns: a `string`
*/
public string getToken()
{
return token;
}
}

@ -1,30 +0,0 @@
module tlang.compiler.lexer.exceptions;
import misc.exceptions : TError;
import tlang.compiler.lexer.core : Lexer;
import std.conv : to;
public enum LexerError
{
EXHAUSTED_CHARACTERS,
OTHER
}
public final class LexerException : TError
{
public const Lexer offendingInstance;
public const LexerError errType;
this(Lexer offendingInstance, LexerError errType = LexerError.OTHER, string msg = "")
{
string positionString = "("~to!(string)(offendingInstance.getLine())~", "~to!(string)(offendingInstance.getColumn())~")";
super("LexerException("~to!(string)(errType)~")"~(msg.length ? ": "~msg : "")~" at "~positionString);
this.offendingInstance = offendingInstance;
this.errType = errType;
}
this(Lexer offendingInstance, string msg)
{
this(offendingInstance, LexerError.OTHER, msg);
}
}

@ -1,14 +1,125 @@
module tlang.compiler.lexer.core;
/**
* A single-pass tokenizer
*/
module tlang.compiler.lexer.kinds.basic;
import std.container.slist;
import gogga;
import std.conv : to;
import std.ascii : isDigit;
import tlang.compiler.lexer.exceptions;
import tlang.compiler.lexer.tokens : Token;
public final class Lexer
import tlang.compiler.lexer.core;
/**
* Represents a basic lexer which performs the whole tokenization
* process in one short via a call to `performLex()`, only after
* this may the `LexerInterface` methods, such as `getCurrentToken()`,
* `nextToken()` and so forth, actually be used.
*
* This is effectively a single pass lexer.
*/
public final class BasicLexer : LexerInterface
{
/**
* Post-perform lex() data
*
* This exports the LexerInterface API.
*
* To-do, ensure these can only be used AFTER `performLex()`
* has been called.
*/
private ulong tokenPtr = 0;
/**
* Returns the token at the current cursor
* position
*
* Returns: the `Token`
*/
public override Token getCurrentToken()
{
/* TODO: Throw an exception here when we try get more than we can */
return tokens[tokenPtr];
}
/**
* Moves the cursor one token forward
*/
public override void nextToken()
{
tokenPtr++;
}
/**
* Moves the cursor one token backwards
*/
public override void previousToken()
{
tokenPtr--;
}
/**
* Sets the position of the cursor
*
* Params:
* newPosition = the new position
*/
public override void setCursor(ulong newPosition)
{
tokenPtr = newPosition;
}
/**
* Retrieves the cursor's current position
*
* Returns: the position
*/
public override ulong getCursor()
{
return tokenPtr;
}
/**
* Checks whether more tokens are available
* of not
*
* Returns: true if more tokens are available, false otherwise
*/
public override bool hasTokens()
{
return tokenPtr < tokens.length;
}
/**
* Get the line position of the lexer in the source text
*
* Returns: the position
*/
public override ulong getLine()
{
return this.line;
}
/**
* Get the column position of the lexer in the source text
*
* Returns: the position
*/
public override ulong getColumn()
{
return this.column;
}
/**
* Exhaustively provide a list of all tokens
*
* Returns: a `Token[]` containing all tokens
*/
public override Token[] getTokens()
{
return tokens;
}
/**
* Lexer state data
*/
@ -23,18 +134,6 @@ public final class Lexer
private bool floatMode; /* Whether or not we are building a floating point constant */
// TODO: Move these all to end, I don't like em here
public ulong getLine()
{
return this.line;
}
public ulong getColumn()
{
return this.column;
}
/* The tokens */
private Token[] tokens;
@ -603,13 +702,6 @@ public final class Lexer
return true;
}
/* Return the tokens */
public Token[] getTokens()
{
return tokens;
}
private bool isSpliter(char character)
{
return character == ';' || character == ',' || character == '(' ||
@ -651,7 +743,7 @@ unittest
{
import std.algorithm.comparison;
string sourceCode = "hello \"world\";";
Lexer currentLexer = new Lexer(sourceCode);
BasicLexer currentLexer = new BasicLexer(sourceCode);
currentLexer.performLex();
gprintln("Collected "~to!(string)(currentLexer.getTokens()));
assert(currentLexer.getTokens() == [new Token("hello", 0, 0), new Token("\"world\"", 0, 0), new Token(";", 0, 0)]);
@ -662,7 +754,7 @@ unittest
{
import std.algorithm.comparison;
string sourceCode = "hello \"world\"|| ";
Lexer currentLexer = new Lexer(sourceCode);
BasicLexer currentLexer = new BasicLexer(sourceCode);
currentLexer.performLex();
gprintln("Collected "~to!(string)(currentLexer.getTokens()));
assert(currentLexer.getTokens() == [new Token("hello", 0, 0), new Token("\"world\"", 0, 0), new Token("||", 0, 0)]);
@ -673,7 +765,7 @@ unittest
{
import std.algorithm.comparison;
string sourceCode = "hello \"world\"||";
Lexer currentLexer = new Lexer(sourceCode);
BasicLexer currentLexer = new BasicLexer(sourceCode);
currentLexer.performLex();
gprintln("Collected "~to!(string)(currentLexer.getTokens()));
assert(currentLexer.getTokens() == [new Token("hello", 0, 0), new Token("\"world\"", 0, 0), new Token("||", 0, 0)]);
@ -684,7 +776,7 @@ unittest
{
import std.algorithm.comparison;
string sourceCode = "hello \"world\";|";
Lexer currentLexer = new Lexer(sourceCode);
BasicLexer currentLexer = new BasicLexer(sourceCode);
currentLexer.performLex();
gprintln("Collected "~to!(string)(currentLexer.getTokens()));
assert(currentLexer.getTokens() == [new Token("hello", 0, 0), new Token("\"world\"", 0, 0), new Token(";", 0, 0), new Token("|", 0, 0)]);
@ -695,7 +787,7 @@ unittest
{
import std.algorithm.comparison;
string sourceCode = " hello";
Lexer currentLexer = new Lexer(sourceCode);
BasicLexer currentLexer = new BasicLexer(sourceCode);
currentLexer.performLex();
gprintln("Collected "~to!(string)(currentLexer.getTokens()));
assert(currentLexer.getTokens() == [new Token("hello", 0, 0)]);
@ -706,7 +798,7 @@ unittest
{
import std.algorithm.comparison;
string sourceCode = " hello;";
Lexer currentLexer = new Lexer(sourceCode);
BasicLexer currentLexer = new BasicLexer(sourceCode);
currentLexer.performLex();
gprintln("Collected "~to!(string)(currentLexer.getTokens()));
assert(currentLexer.getTokens() == [new Token("hello", 0, 0), new Token(";", 0, 0)]);
@ -717,7 +809,7 @@ unittest
{
import std.algorithm.comparison;
string sourceCode = "hello \"world\\\"\"";
Lexer currentLexer = new Lexer(sourceCode);
BasicLexer currentLexer = new BasicLexer(sourceCode);
currentLexer.performLex();
gprintln("Collected "~to!(string)(currentLexer.getTokens()));
assert(currentLexer.getTokens() == [new Token("hello", 0, 0), new Token("\"world\\\"\"", 0, 0)]);
@ -728,7 +820,7 @@ unittest
{
import std.algorithm.comparison;
string sourceCode = "'c'";
Lexer currentLexer = new Lexer(sourceCode);
BasicLexer currentLexer = new BasicLexer(sourceCode);
currentLexer.performLex();
gprintln("Collected "~to!(string)(currentLexer.getTokens()));
assert(currentLexer.getTokens() == [new Token("'c'", 0, 0)]);
@ -739,7 +831,7 @@ unittest
{
import std.algorithm.comparison;
string sourceCode = "2121\n2121";
Lexer currentLexer = new Lexer(sourceCode);
BasicLexer currentLexer = new BasicLexer(sourceCode);
currentLexer.performLex();
gprintln("Collected "~to!(string)(currentLexer.getTokens()));
assert(currentLexer.getTokens() == [new Token("2121", 0, 0), new Token("2121", 0, 0)]);
@ -752,35 +844,35 @@ unittest
{
import std.algorithm.comparison;
string sourceCode = " =\n";
Lexer currentLexer = new Lexer(sourceCode);
BasicLexer currentLexer = new BasicLexer(sourceCode);
currentLexer.performLex();
gprintln("Collected "~to!(string)(currentLexer.getTokens()));
assert(currentLexer.getTokens() == [new Token("=", 0, 0)]);
import std.algorithm.comparison;
sourceCode = " = ==\n";
currentLexer = new Lexer(sourceCode);
currentLexer = new BasicLexer(sourceCode);
currentLexer.performLex();
gprintln("Collected "~to!(string)(currentLexer.getTokens()));
assert(currentLexer.getTokens() == [new Token("=", 0, 0), new Token("==", 0, 0)]);
import std.algorithm.comparison;
sourceCode = " ==\n";
currentLexer = new Lexer(sourceCode);
currentLexer = new BasicLexer(sourceCode);
currentLexer.performLex();
gprintln("Collected "~to!(string)(currentLexer.getTokens()));
assert(currentLexer.getTokens() == [new Token("==", 0, 0)]);
import std.algorithm.comparison;
sourceCode = " = =\n";
currentLexer = new Lexer(sourceCode);
currentLexer = new BasicLexer(sourceCode);
currentLexer.performLex();
gprintln("Collected "~to!(string)(currentLexer.getTokens()));
assert(currentLexer.getTokens() == [new Token("=", 0, 0), new Token("=", 0, 0)]);
import std.algorithm.comparison;
sourceCode = " ==, = ==\n";
currentLexer = new Lexer(sourceCode);
currentLexer = new BasicLexer(sourceCode);
currentLexer.performLex();
gprintln("Collected "~to!(string)(currentLexer.getTokens()));
assert(currentLexer.getTokens() == [new Token("==", 0, 0), new Token(",", 0, 0), new Token("=", 0, 0), new Token("==", 0, 0)]);
@ -788,7 +880,7 @@ unittest
// Test flushing of previous token
import std.algorithm.comparison;
sourceCode = "i==i=\n";
currentLexer = new Lexer(sourceCode);
currentLexer = new BasicLexer(sourceCode);
currentLexer.performLex();
gprintln("Collected "~to!(string)(currentLexer.getTokens()));
assert(currentLexer.getTokens() == [new Token("i", 0, 0), new Token("==", 0, 0), new Token("i", 0, 0), new Token("=", 0, 0)]);
@ -803,18 +895,18 @@ unittest
{
import std.algorithm.comparison;
string sourceCode;
Lexer currentLexer;
BasicLexer currentLexer;
/* 21L (valid) */
sourceCode = "21L";
currentLexer = new Lexer(sourceCode);
currentLexer = new BasicLexer(sourceCode);
currentLexer.performLex();
gprintln("Collected "~to!(string)(currentLexer.getTokens()));
assert(currentLexer.getTokens() == [new Token("21L", 0, 0)]);
/* 21UL (valid) */
sourceCode = "21UL";
currentLexer = new Lexer(sourceCode);
currentLexer = new BasicLexer(sourceCode);
currentLexer.performLex();
gprintln("Collected "~to!(string)(currentLexer.getTokens()));
assert(currentLexer.getTokens() == [new Token("21UL", 0, 0)]);
@ -843,7 +935,7 @@ unittest
{
import std.algorithm.comparison;
string sourceCode = "1.5";
Lexer currentLexer = new Lexer(sourceCode);
BasicLexer currentLexer = new BasicLexer(sourceCode);
currentLexer.performLex();
gprintln("Collected "~to!(string)(currentLexer.getTokens()));
assert(currentLexer.getTokens() == [new Token("1.5", 0, 0)]);
@ -859,7 +951,7 @@ unittest
{
import std.algorithm.comparison;
string sourceCode = "new A().l.p.p;";
Lexer currentLexer = new Lexer(sourceCode);
BasicLexer currentLexer = new BasicLexer(sourceCode);
currentLexer.performLex();
gprintln("Collected "~to!(string)(currentLexer.getTokens()));
assert(currentLexer.getTokens() == [

@ -1,36 +0,0 @@
module tlang.compiler.lexer.tokens;
import std.string : cmp;
import std.conv : to;
public final class Token
{
/* The token */
private string token;
/* Line number information */
private ulong line, column;
this(string token, ulong line, ulong column)
{
this.token = token;
this.line = line;
this.column = column;
}
override bool opEquals(Object other)
{
return cmp(token, (cast(Token)other).getToken()) == 0;
}
override string toString()
{
/* TODO (Column number): Don't adjust here, do it maybe in the lexer itself */
return token~" at ("~to!(string)(line)~", "~to!(string)(column-token.length)~")";
}
public string getToken()
{
return token;
}
}

@ -0,0 +1,17 @@
module tlang.compiler.parsing.cloneable;
import tlang.compiler.symbols.data : Statement;
/**
* A parse-node/AST-node which implements `Cloneable` can
* be safely deeply cloned such that a full copy is returned.
*/
public interface Cloneable
{
/**
* Performs a deep clone of this parse node
*
* Returns: the clone
*/
public Statement clone();
}

File diff suppressed because it is too large Load Diff

@ -4,7 +4,7 @@ import tlang.compiler.parsing.core;
import misc.exceptions;
import tlang.compiler.symbols.check;
import tlang.compiler.symbols.data;
import tlang.compiler.lexer.tokens : Token;
import tlang.compiler.lexer.core.tokens : Token;
import std.conv : to;
public class ParserException : TError

@ -1,76 +1,312 @@
/**
* Token-to-symbol mappings (and vice-versa),
* facilities for performing tests on what sort
* of tokens are of certain classes (operators, etc.)
* and detection of different types of identifiers
*/
module tlang.compiler.symbols.check;
import tlang.compiler.lexer.tokens : Token;
import tlang.compiler.lexer.core.tokens : Token;
import std.conv : to;
import std.string : isNumeric, cmp;
import std.algorithm.searching : canFind;
import misc.utils;
import gogga;
/**
* All allowed symbols
* TODO: There should be a symbol class with sub-types
*/
* All allowed symbols
*/
public enum SymbolType
{
/**
* Default symbol (TODO: idk why this exists)
*/
LE_SYMBOL,
/**
* Any sort of identifier
*
* Must start with a letter,
* can contain numbers and
* may contain periods.
*
* It may also contain underscores.
*/
IDENT_TYPE,
/**
* Any sort of number, this can
* be `8` or `8.5`
*/
NUMBER_LITERAL,
/**
* A character constant like `'a'`
*/
CHARACTER_LITERAL,
/**
* A string constant like `"FELLA"`
*/
STRING_LITERAL,
/**
* Semicolon `;`
*/
SEMICOLON,
/**
* Left smooth brace $(LPAREN)
*/
LBRACE,
/**
* Right smooth brace $(RPAREN)
*/
RBRACE,
/**
* Assigmment symbol `=`
*/
ASSIGN,
/**
* Comma `,`
*/
COMMA,
/**
* Left curly brace `{`
*/
OCURLY,
/**
* Right curly brace `}`
*/
CCURLY,
/**
* Module keyword `module`
*/
MODULE,
/**
* New keyword `new`
*/
NEW,
/**
* If keyword `if`
*/
IF,
/**
* Else keyword `else`
*/
ELSE,
/**
* Discard keyword `discard`
*/
DISCARD,
/**
* While keyword `while`
*/
WHILE,
/**
* Class keyword `class`
*/
CLASS,
/**
* Inherit keyword `:`
*/
INHERIT_OPP,
/**
* Tilde `~`
*/
TILDE,
/**
* For keyword `for`
*/
FOR,
/**
* Super keyword `super`
*/
SUPER,
/**
* This keyword `this`
*/
THIS,
/**
* Switch keyword `switch`
*/
SWITCH,
/**
* Return keyword `return`
*/
RETURN,
/**
* Public keyword `public`
*/
PUBLIC,
/**
* Private keyword `private`
*/
PRIVATE,
/**
* Protected keyword `protected`
*/
PROTECTED,
/**
* Static keyword `static`
*/
STATIC,
/**
* Case keyword `case`
*/
CASE,
/**
* Goto keyword `goto`
*/
GOTO,
/**
* Do keyword `do`
*/
DO,
/**
* Dot operator `.`
*/
DOT,
/**
* Delete keyword `delete`
*/
DELETE,
/**
* Struct keyword `struct`
*/
STRUCT,
/**
* Subtraction operator `-`
*/
SUB,
/**
* Addition operator `+`
*/
ADD,
/**
* Division operator `/`
*/
DIVIDE,
/**
* Star operator `*`
*/
STAR,
/**
* Ampersand (reffer) operator `&`
*/
AMPERSAND,
/**
* Equality operator `==`
*/
EQUALS,
/**
* Greater than operator `>`
*/
GREATER_THAN,
/**
* Smaller than operator `<`
*/
SMALLER_THAN,
/**
* Greater than or equals to operator `>=`
*/
GREATER_THAN_OR_EQUALS,
/**
* Smaller than or equals to operator `<=`
*/
SMALLER_THAN_OR_EQUALS,
/**
* Opening bracket `[`
*/
OBRACKET,
/**
* Closing bracket `]`
*/
CBRACKET,
/**
* Cast keyword `cast`
*/
CAST,
/**
* Extern keyword `extern`
*/
EXTERN,
/**
* Extern-function keyword `efunc`
*/
EXTERN_EFUNC,
EXTERN_EVAR,
UNKNOWN
}
/**
* Extern-variable keyword `evar`
*/
EXTERN_EVAR,
/**
* `generic`
*/
GENERIC_TYPE_DECLARE,
/**
* Unknown symbol
*/
UNKNOWN
}
/* TODO: Later build classes specific to symbol */
/* TODO: Check if below is even used */
/**
* Checks if the given token string is that of
* a built-in type
*
* Params:
* tokenStr = the string to check
* Returns: `true` if one of the built-in types,
* `false` otherwise
*/
public bool isType(string tokenStr)
{
return cmp(tokenStr, "byte") == 0 || cmp(tokenStr, "ubyte") == 0
@ -79,6 +315,18 @@ public bool isType(string tokenStr)
"long") == 0 || cmp(tokenStr, "ulong") == 0 || cmp(tokenStr, "void") == 0;
}
/**
* Checks if the given token string is a path
* identifier. This means that it is something
* which contains dots inbetween it like `a.b`
* but does not appear as a floating point literal
* such as `7.5`. It may also contain udnerscores `_`.
*
* Params:
* token = the token string to check
* Returns: `true` if it is a path identifier,
* `false` otherwise
*/
public bool isPathIdentifier(string token)
{
/* This is used to prevent the first character from not being number */
@ -132,6 +380,17 @@ public bool isPathIdentifier(string token)
return isDot;
}
/**
* Checks if the given token string is an identifier
* which means it can contains letters and umbers
* but MUST start with a letter. It may also
* contain udnerscores `_`.
*
* Params:
* token = the token string to check
* Returns: `true` if an identifier, `flase`
* otherwise
*/
public bool isIdentifier(string token)
{
/* This is used to prevent the first character from not being number */
@ -169,6 +428,13 @@ public bool isIdentifier(string token)
return true;
}
/**
* Checks if the given `Token` is an accessor
*
* Params:
* token = the `Token` to check
* Returns: `true` if so, `false` otherwise
*/
public bool isAccessor(Token token)
{
return getSymbolType(token) == SymbolType.PUBLIC ||
@ -176,11 +442,26 @@ public bool isAccessor(Token token)
getSymbolType(token) == SymbolType.PROTECTED;
}
/**
* Checks if the given `Token` is a modifier
*
* Params:
* token = the `Token` to check
* Returns: `true` if so, `false` otherwise
*/
public bool isModifier(Token token)
{
return getSymbolType(token) == SymbolType.STATIC;
}
/**
* Checks if the given `Token` is a normal
* identifier (with no dots/periods)
*
* Params:
* tokenIn = the `Token` to test
* Returns: `true` if so, `false` otherwise
*/
public bool isIdentifier_NoDot(Token tokenIn)
{
/* Make sure it isn't any other type of symbol */
@ -194,6 +475,15 @@ public bool isIdentifier_NoDot(Token tokenIn)
}
}
/**
* Checks if the given `Token` is a dotted-identifier
* meaning it contains `.`/periods in it - a so-called
* path identifier.
*
* Params:
* tokenIn = the `Token` to test
* Returns: `true` if so, `false` otherwise
*/
public bool isIdentifier_Dot(Token tokenIn)
{
/* Make sure it isn't any other type of symbol */
@ -207,10 +497,19 @@ public bool isIdentifier_Dot(Token tokenIn)
}
}
/**
* Checks if the given token string
* as a numeric literal. It has support
* for checking if it has a size specifier
* as well.
*
* Params:
* token = the string token to check
* Returns: `true` if it is a numeric literal,
* `false` otherwise
*/
private bool isNumericLiteral(string token)
{
import std.algorithm.searching : canFind;
import tlang.compiler.lexer.core :Lexer;
if(canFind(token, "UL") || canFind(token, "UI"))
{
return isNumeric(token[0..$-2]);
@ -229,6 +528,17 @@ private bool isNumericLiteral(string token)
}
}
/**
* Maps a given `Token` to its `SymbolType` such
* that you can determine the type of symbol it
* is.
*
* Params:
* tokenIn = the `Token` to check
* Returns: the `SymbolType` of this token, if
* unrecgnizable then `SymbolType.UNKNOWN` is
* returned
*/
public SymbolType getSymbolType(Token tokenIn)
{
string token = tokenIn.getToken();
@ -380,6 +690,11 @@ public SymbolType getSymbolType(Token tokenIn)
{
return SymbolType.DISCARD;
}
/* generic keyword */
else if(cmp(token, "generic") == 0)
{
return SymbolType.GENERIC_TYPE_DECLARE;
}
/* An identifier/type (of some sorts) - further inspection in parser is needed */
else if(isPathIdentifier(token) || isIdentifier(token))
{
@ -500,6 +815,15 @@ public SymbolType getSymbolType(Token tokenIn)
return SymbolType.UNKNOWN;
}
/**
* Determines whether the given token is
* a mathematical operator
*
* Params:
* token = the `Token` to test
* Returns: `true` if it is a mathematical
* operator, `false` otherwise
*/
public bool isMathOp(Token token)
{
string tokenStr = token.getToken();
@ -508,6 +832,17 @@ public bool isMathOp(Token token)
tokenStr[0] == '*' || tokenStr[0] == '/';
}
/**
* Determines whether the given token is
* a binary operator, meaning one which
* would be infixed/flanked by two operands
* (one to the left and one to the right)
*
* Params:
* token = the `Token` to test
* Returns: `true` if it is a binary
* operator, `false` otherwise
*/
public bool isBinaryOp(Token token)
{
string tokenStr = token.getToken();

@ -4,6 +4,9 @@ import tlang.compiler.symbols.data;
import std.conv : to;
import tlang.compiler.symbols.typing.core;
// AST manipulation interfaces
import tlang.compiler.symbols.mcro : MStatementSearchable, MStatementReplaceable, MCloneable;
/**
* Used so often that we may as well
* declare it once
@ -29,7 +32,9 @@ public Statement[] weightReorder(Statement[] statements)
return stmntsRed;
}
public interface Container
// TODO: Honestly all contains should be a kind-of `MStatementSearchable` and `MStatementReplaceable`
// AND MCloneable
public interface Container : MStatementSearchable, MStatementReplaceable
{
public void addStatement(Statement statement);
@ -38,6 +43,7 @@ public interface Container
public Statement[] getStatements();
}
public class Module : Entity, Container
{
this(string moduleName)
@ -62,6 +68,79 @@ public class Module : Entity, Container
{
return weightReorder(statements);
}
public override Statement[] search(TypeInfo_Class clazzType)
{
/* List of returned matches */
Statement[] matches;
/* Are we (ourselves) of this type? */
if(clazzType.isBaseOf(this.classinfo))
{
matches ~= [this];
}
/* Recurse on each `Statement` making up our body */
// NOTE: Using weight-reordered? Is that fine?
foreach(Statement curStmt; getStatements())
{
MStatementSearchable curStmtCasted = cast(MStatementSearchable)curStmt;
if(curStmtCasted)
{
matches ~= curStmtCasted.search(clazzType);
}
}
return matches;
}
public override bool replace(Statement thiz, Statement that)
{
/* If we (`this`) are `thiz`, then we cannot replace */
if(this == thiz)
{
return false;
}
/* If not ourself, then check the body statements */
else
{
/**
* First check each `Statement` that make sup our
* body and see if we can replace that, else see
* if we can recurse on each of the body statements
* and apply replacement therein
*/
// NOTE: Using weight-reordered? Is that fine?
Statement[] bodyStmts = getStatements();
for(ulong idx = 0; idx < bodyStmts.length; idx++)
{
Statement curBodyStmt = bodyStmts[idx];
/* Should we directly replace the Statement in the body? */
if(curBodyStmt == thiz)
{
// Replace the statement in the body
statements[idx] = that;
// Re-parent `that` to us
that.parentTo(this);
return true;
}
/* If we cannot, then recurse (try) on it */
else if(cast(MStatementReplaceable)curBodyStmt)
{
MStatementReplaceable curBodyStmtRepl = cast(MStatementReplaceable)curBodyStmt;
if(curBodyStmtRepl.replace(thiz, that))
{
return true;
}
}
}
return false;
}
}
}
/**
@ -71,7 +150,7 @@ public class Module : Entity, Container
* that are Variables (TODO: Enforce in parser)
* TODO: Possibly enforce here too
*/
public class Struct : Type, Container
public class Struct : Type, Container, MCloneable
{
private Statement[] statements;
@ -94,6 +173,121 @@ public class Struct : Type, Container
{
super(name);
}
public override Statement[] search(TypeInfo_Class clazzType)
{
/* List of returned matches */
Statement[] matches;
/* Are we (ourselves) of this type? */
if(clazzType.isBaseOf(this.classinfo))
{
matches ~= [this];
}
/* Recurse on each `Statement` making up our body */
// NOTE: Using weight-reordered? Is that fine?
foreach(Statement curStmt; getStatements())
{
MStatementSearchable curStmtCasted = cast(MStatementSearchable)curStmt;
if(curStmtCasted)
{
matches ~= curStmtCasted.search(clazzType);
}
}
return matches;
}
public override bool replace(Statement thiz, Statement that)
{
/* If we (`this`) are `thiz`, then we cannot replace */
if(this == thiz)
{
return false;
}
/* If not ourself, then check the body statements */
else
{
/**
* First check each `Statement` that make sup our
* body and see if we can replace that, else see
* if we can recurse on each of the body statements
* and apply replacement therein
*/
// NOTE: Using weight-reordered? Is that fine?
Statement[] bodyStmts = getStatements();
for(ulong idx = 0; idx < bodyStmts.length; idx++)
{
Statement curBodyStmt = bodyStmts[idx];
/* Should we directly replace the Statement in the body? */
if(curBodyStmt == thiz)
{
// Replace the statement in the body
statements[idx] = that;
// Re-parent `that` to us
that.parentTo(this);
return true;
}
/* If we cannot, then recurse (try) on it */
else if(cast(MStatementReplaceable)curBodyStmt)
{
MStatementReplaceable curBodyStmtRepl = cast(MStatementReplaceable)curBodyStmt;
if(curBodyStmtRepl.replace(thiz, that))
{
return true;
}
}
}
return false;
}
}
/**
* Clones this struct recursively returning a
* fresh copy of all its members and the struct
* itself.
*
* Param:
* newParent = the `Container` to re-parent the
* cloned `Statement`'s self to
*
* Returns: the cloned `Statement`
*/
public override Statement clone(Container newParent = null)
{
Struct clonedStruct = new Struct(this.name);
/**
* Clone all the statements and re-parent them
* to the clone
*/
Statement[] clonedStatements;
foreach(Statement curStmt; this.getStatements())
{
Statement clonedStmt;
if(cast(MCloneable)curStmt)
{
MCloneable cloneableCurStmt = cast(MCloneable)curStmt;
clonedStmt = cloneableCurStmt.clone();
}
// Re-parent to the cloned struct
clonedStmt.parentTo(clonedStruct);
// Add it to the cloned struct's body
clonedStruct.addStatement(clonedStmt);
}
// Parent ourselves to the given parent
clonedStruct.parentTo(newParent);
return clonedStruct;
}
}
public class Clazz : Type, Container
@ -139,5 +333,126 @@ public class Clazz : Type, Container
{
return weightReorder(statements);
}
public override Statement[] search(TypeInfo_Class clazzType)
{
/* List of returned matches */
Statement[] matches;
/* Are we (ourselves) of this type? */
if(clazzType.isBaseOf(this.classinfo))
{
matches ~= [this];
}
/* Recurse on each `Statement` making up our body */
// NOTE: Using weight-reordered? Is that fine?
foreach(Statement curStmt; getStatements())
{
MStatementSearchable curStmtCasted = cast(MStatementSearchable)curStmt;
if(curStmtCasted)
{
matches ~= curStmtCasted.search(clazzType);
}
}
return matches;
}
public override bool replace(Statement thiz, Statement that)
{
/* If we (`this`) are `thiz`, then we cannot replace */
if(this == thiz)
{
return false;
}
/* If not ourself, then check the body statements */
else
{
/**
* First check each `Statement` that make sup our
* body and see if we can replace that, else see
* if we can recurse on each of the body statements
* and apply replacement therein
*/
// NOTE: Using weight-reordered? Is that fine?
Statement[] bodyStmts = getStatements();
for(ulong idx = 0; idx < bodyStmts.length; idx++)
{
Statement curBodyStmt = bodyStmts[idx];
/* Should we directly replace the Statement in the body? */
if(curBodyStmt == thiz)
{
// Replace the statement in the body
statements[idx] = that;
// Re-parent `that` to us
that.parentTo(this);
return true;
}
/* If we cannot, then recurse (try) on it */
else if(cast(MStatementReplaceable)curBodyStmt)
{
MStatementReplaceable curBodyStmtRepl = cast(MStatementReplaceable)curBodyStmt;
if(curBodyStmtRepl.replace(thiz, that))
{
return true;
}
}
}
return false;
}
}
}
/**
* Test the `MCloneable`-ity support of `Struct`
* which has two `Variable` members (therefore
* also testing the `clone()` on `Variable`)
*/
unittest
{
Struct original = new Struct("User");
Variable originalVar_Name = new Variable("byte*", "name");
Variable originalVar_Age = new Variable("int", "age");
originalVar_Name.parentTo(original);
originalVar_Age.parentTo(original);
original.addStatement(originalVar_Name);
original.addStatement(originalVar_Age);
// Now clone it
Struct cloned = cast(Struct)original.clone();
// Cloned version should differ
assert(cloned !is original);
// Cloned statements versus original statements
Statement[] clonedStmts = cloned.getStatements();
Statement[] originalStmts = original.getStatements();
assert(clonedStmts[0] !is originalStmts[0]);
assert(clonedStmts[1] !is originalStmts[1]);
// Compare the variables (members) of both
Variable origStruct_MemberOne = cast(Variable)originalStmts[0];
Variable origStruct_MemberTwo = cast(Variable)originalStmts[1];
Variable clonedStruct_MemberOne = cast(Variable)clonedStmts[0];
Variable clonedStruct_MemberTwo = cast(Variable)clonedStmts[1];
assert(origStruct_MemberOne !is clonedStruct_MemberOne);
assert(origStruct_MemberTwo !is clonedStruct_MemberTwo);
assert(originalVar_Name.getName() == clonedStruct_MemberOne.getName()); // Names should match
assert(origStruct_MemberTwo.getName() == clonedStruct_MemberTwo.getName()); // Names should match
// Ensure re-parenting is correct
assert(origStruct_MemberOne.parentOf() == original);
assert(origStruct_MemberTwo.parentOf() == original);
assert(clonedStruct_MemberOne.parentOf() == cloned);
assert(clonedStruct_MemberTwo.parentOf() == cloned);
// TODO: Make this more deeper this test as a few
// ... more things were left out that can be checked
}

@ -4,6 +4,11 @@ public import tlang.compiler.symbols.check;
import std.conv : to;
import tlang.compiler.typecheck.dependency.core : Context;
// For debug printing
import gogga;
// AST manipulation interfaces
import tlang.compiler.symbols.mcro : MStatementSearchable, MStatementReplaceable, MCloneable;
/**
* TODO: Implement the blow and use them
@ -215,7 +220,8 @@ public class Entity : Statement
}
/* TODO: DO we need intermediary class, TypedEntity */
public class TypedEntity : Entity
import tlang.compiler.symbols.mcro : MTypeRewritable;
public class TypedEntity : Entity, MTypeRewritable
{
private string type;
@ -230,15 +236,14 @@ public class TypedEntity : Entity
{
return type;
}
}
public import tlang.compiler.symbols.containers;
public class ArgumentList
{
public void setType(string type)
{
this.type = type;
}
}
public import tlang.compiler.symbols.containers;
/**
* VariableParameter
@ -352,9 +357,93 @@ public class Function : TypedEntity, Container
return "Function (Name: "~name~", ReturnType: "~type~", Args: "~argTypes~")";
}
public override Statement[] search(TypeInfo_Class clazzType)
{
/* List of returned matches */
Statement[] matches;
/* Are we (ourselves) of this type? */
if(clazzType.isBaseOf(this.classinfo))
{
matches ~= [this];
}
/* Recurse on each `Statement` making up our body */
// NOTE: Using weight-reordered? Is that fine?
foreach(Statement curStmt; getStatements())
{
MStatementSearchable curStmtCasted = cast(MStatementSearchable)curStmt;
if(curStmtCasted)
{
matches ~= curStmtCasted.search(clazzType);
}
}
return matches;
}
public override bool replace(Statement thiz, Statement that)
{
/* If we (`this`) are `thiz`, then we cannot replace */
if(this == thiz)
{
return false;
}
/* If not ourself, then check the body statements */
else
{
/**
* First check each `Statement` that make sup our
* body and see if we can replace that, else see
* if we can recurse on each of the body statements
* and apply replacement therein
*/
// NOTE: Using weight-reordered? Is that fine?
Statement[] bodyStmts = getStatements();
for(ulong idx = 0; idx < bodyStmts.length; idx++)
{
Statement curBodyStmt = bodyStmts[idx];
/* Should we directly replace the Statement in the body? */
if(curBodyStmt == thiz)
{
// Replace the statement in the body
// NOTE: The respective Variable Param must be swapped out too if need be
// (varParams[] subsetOf Statements[])
for(ulong varParamIdx = 0; varParamIdx < params.length; varParamIdx++)
{
VariableParameter curVarParam = params[varParamIdx];
if(curVarParam == thiz)
{
params[varParamIdx] = cast(VariableParameter)that;
break;
}
}
bodyStatements[idx] = that;
// Re-parent `that` to us
that.parentTo(this);
return true;
}
/* If we cannot, then recurse (try) on it */
else if(cast(MStatementReplaceable)curBodyStmt)
{
MStatementReplaceable curBodyStmtRepl = cast(MStatementReplaceable)curBodyStmt;
if(curBodyStmtRepl.replace(thiz, that))
{
return true;
}
}
}
return false;
}
}
}
public class Variable : TypedEntity
public class Variable : TypedEntity, MStatementSearchable, MStatementReplaceable, MCloneable
{
/* TODO: Just make this an Expression */
private VariableAssignment assignment;
@ -382,6 +471,93 @@ public class Variable : TypedEntity
{
return "Variable (Ident: "~name~", Type: "~type~")";
}
public override Statement[] search(TypeInfo_Class clazzType)
{
/* List of returned matches */
Statement[] matches;
/* Are we (ourselves) of this type? */
if(clazzType.isBaseOf(this.classinfo))
{
matches ~= [this];
}
/**
* Recurse on the `VariableAssignment`
*/
MStatementSearchable innerStmt = cast(MStatementSearchable)assignment;
if(innerStmt)
{
matches ~= innerStmt.search(clazzType);
}
return matches;
}
public override bool replace(Statement thiz, Statement that)
{
/* If we, the `Variable`, are the `thiz` then we cannot perform replacement */
if(this == thiz)
{
return false;
}
/* Check if we should replace the `VariableAssignment` */
else if(thiz == assignment)
{
assignment = cast(VariableAssignment)that;
return true;
}
/* Recurse on the variable assignment (if there is one) */
else if(assignment !is null)
{
return assignment.replace(thiz, that);
}
/* Exhausted all possibilities */
else
{
return false;
}
}
/**
* Clones this variable declaration recursively
* including its assigned value (`VariableAssignment`)
* if any.
*
* Param:
* newParent = the `Container` to re-parent the
* cloned `Statement`'s self to
*
* Returns: the cloned `Statement`
*/
public override Statement clone(Container newParent = null)
{
Variable clonedVarDec;
// If there's an assignment, then clone it
VariableAssignment clonedVarAss = null;
if(this.assignment)
{
// Clone the assignment
clonedVarAss = cast(VariableAssignment)this.assignment.clone(); // TODO: If needs be we must re-parent manually
}
// Create new variable with same name and identifier
clonedVarDec = new Variable(this.type, this.name);
// Copy all properties across (TODO: Make sure we didn't miss any)
clonedVarDec.accessorType = this.accessorType;
clonedVarDec.isExternalEntity = this.isExternalEntity;
clonedVarDec.assignment = clonedVarAss;
clonedVarDec.container = this.container;
// Parent outselves to the given parent
clonedVarDec.parentTo(newParent);
return clonedVarDec;
}
}
@ -394,7 +570,7 @@ public import tlang.compiler.symbols.expressions;
/**
* TODO: Rename to `VariableDeclarationAssignment`
*/
public class VariableAssignment : Statement
public class VariableAssignment : Statement, MStatementSearchable, MStatementReplaceable, MCloneable
{
private Expression expression;
private Variable variable;
@ -414,6 +590,8 @@ public class VariableAssignment : Statement
return variable;
}
// NOTE-to-self: Very interesting method we have here, is this just for debugging?
// (15th May 2023, whilst working on Meta)
public void setVariable(Variable variable)
{
this.variable = variable;
@ -423,12 +601,96 @@ public class VariableAssignment : Statement
{
return "[varAssignDec'd: To: "~variable.toString()~"]";
}
public override Statement[] search(TypeInfo_Class clazzType)
{
/* List of returned matches */
Statement[] matches;
/* Are we (ourselves) of this type? */
if(clazzType.isBaseOf(this.classinfo))
{
matches ~= [this];
}
/* Recurse on our `Expression` (if possible) */
MStatementSearchable innerStmt = cast(MStatementSearchable)expression;
if(innerStmt)
{
matches ~= innerStmt.search(clazzType);
}
return matches;
}
public override bool replace(Statement thiz, Statement that)
{
/* We cannot replace ourselves directly */
if(this == thiz)
{
return false;
}
/* Is the `Expression` the `thiz`, then swap out the expression */
else if(expression == thiz)
{
// TODO: Any reparenting needed?
expression = cast(Expression)that;
return true;
}
/* Recurse on the `Expression` being assigned (if possible) */
else if(cast(MStatementReplaceable)expression)
{
MStatementReplaceable replStmt = cast(MStatementReplaceable)expression;
return replStmt.replace(thiz, that);
}
/* If not matched */
else
{
return false;
}
}
/**
* Clones this variable assignment by recursively cloning
* the fields within (TODO: finish description)
*
* Param:
* newParent = the `Container` to re-parent the
* cloned `Statement`'s self to
*
* Returns: the cloned `Statement`
*/
public override Statement clone(Container newParent = null)
{
// FIXME: Investigate if `Variable`? Must be cloned
// ... would cuase infinite recursion and it isn't
// ... reaslly a part of the AST (just a helper)
// ... hence I do not believe it needs to be cloned
// (If for some reason the association eneds to be)
// ... updted then `Variable`'s `clone()' can call
/// ... `setvariable(clonedVarDec)` (with itself)
// Clone the expression (if supported, TODO: throw an error if not)
Expression clonedExpression = null;
if(cast(MCloneable)this.expression)
{
MCloneable cloneableExpression = cast(MCloneable)this.expression;
clonedExpression = cast(Expression)cloneableExpression.clone(); // NOTE: Manually re-parent if
}
VariableAssignment clonedVarAss = new VariableAssignment(clonedExpression);
// Parent outselves to the given parent
clonedVarAss.parentTo(newParent);
return clonedVarAss;
}
}
/**
* TODO: Rename to ``
*/
public class VariableAssignmentStdAlone : Statement
public class VariableAssignmentStdAlone : Statement, MStatementSearchable, MStatementReplaceable
{
private Expression expression;
private string varName;
@ -456,6 +718,55 @@ public class VariableAssignmentStdAlone : Statement
{
return "[varAssignStdAlone: To: "~varName~"]";
}
public override Statement[] search(TypeInfo_Class clazzType)
{
/* List of returned matches */
Statement[] matches;
/* Are we (ourselves) of this type? */
if(clazzType.isBaseOf(this.classinfo))
{
matches ~= [this];
}
/**
* Recurse on the assigned `Expression`
*/
MStatementSearchable assignedStmtCasted = cast(MStatementSearchable)expression;
if(assignedStmtCasted)
{
matches ~= assignedStmtCasted.search(clazzType);
}
return matches;
}
public override bool replace(Statement thiz, Statement that)
{
/* If we, the `VariableAssignmentStdAlone`, are the `thiz` then we cannot perform replacement */
if(this == thiz)
{
return false;
}
/* Check if we should replace the `Expression` being assigned? */
else if(thiz == expression)
{
expression = cast(Expression)that;
return true;
}
/* Recurse on the assigned `Expression` (if possible) */
else if(cast(MStatementReplaceable)expression)
{
MStatementReplaceable expressionCasted = cast(MStatementReplaceable)expression;
return expressionCasted.replace(thiz, that);
}
/* None */
else
{
return false;
}
}
}
// TODO: Add an ArrayAssignment thing here, would be similiar to PointerDeference
@ -538,12 +849,8 @@ public class PointerDereferenceAssignment : Statement
}
}
public class IdentExpression : Expression
public class IdentExpression : Expression, MStatementSearchable, MStatementReplaceable
{
/* name */
private string name;
@ -561,27 +868,37 @@ public class IdentExpression : Expression
{
name = newName;
}
}
public class VariableExpression : IdentExpression
{
this(string identifier)
public override Statement[] search(TypeInfo_Class clazzType)
{
super(identifier);
}
/* List of returned matches */
Statement[] matches;
/* Are we (ourselves) of this type? */
if(clazzType.isBaseOf(this.classinfo))
{
matches ~= [this];
}
return matches;
}
import tlang.compiler.typecheck.core;
public override string evaluateType(TypeChecker typeChecker, Container c)
public override bool replace(Statement thiz, Statement that)
{
string type;
// Nothing to replace within us
return false;
}
}
public class VariableExpression : IdentExpression
{
return null;
this(string identifier)
{
super(identifier);
}
public override string toString()
{
return "[varExp: "~getName()~"]";
@ -596,9 +913,18 @@ public class Call : IdentExpression
}
}
public final class FunctionCall : Call
// FIXME: Finish adding proper `MStatementSearchable` and `MStatementReplaceable` to `FunctionCall`
public final class FunctionCall : Call, MStatementSearchable, MStatementReplaceable
{
/* Whether this is statement-level function call or not */
/**
* Function calls either appear as part of an expression
* (i.e. from `parseExpression()`) or directly as a statement
* in the body of a `Container`. This affects how code generation
* works and hence one needs to disambiguate between the two.
*/
private bool isStatementLevel = false;
/* Argument list */
private Expression[] arguments;
@ -618,6 +944,79 @@ public final class FunctionCall : Call
{
return arguments;
}
/**
* Mark this function call as statement-level
*/
public void makeStatementLevel()
{
this.isStatementLevel = true;
}
/**
* Determines if this function call is statement-level
*
* Returns: true if so, false otherwise
*/
public bool isStatementLevelFuncCall()
{
return isStatementLevel;
}
public override Statement[] search(TypeInfo_Class clazzType)
{
// TODO: Implement me
/* List of returned matches */
Statement[] matches;
/* Are we (ourselves) of this type? */
if(clazzType.isBaseOf(this.classinfo))
{
matches ~= [this];
}
/**
* Recurse on each `Expression` (if possible)
*/
foreach(Expression callExp; arguments)
{
MStatementSearchable innerStmt = cast(MStatementSearchable)callExp;
if(innerStmt)
{
matches ~= innerStmt.search(clazzType);
}
}
return matches;
}
public override bool replace(Statement thiz, Statement that)
{
// TODO: Implement me
// /* Check if our `Expression` matches, then replace */
// if(expression == thiz)
// {
// // NOTE: This legit makes no sense and won't do anything, we could remove this
// // and honestly should probably make this return false
// // FIXME: Make this return `false` (see above)
// expression = cast(Expression)that;
// return true;
// }
// /* If not direct match, then recurse and replace (if possible) */
// else if(cast(MStatementReplaceable)expression)
// {
// MStatementReplaceable replStmt = cast(MStatementReplaceable)expression;
// return replStmt.replace(thiz, that);
// }
// /* If not direct match and not replaceable */
// else
// {
// return false;
// }
return true;
}
}
/**
@ -635,6 +1034,11 @@ public final class ReturnStmt : Statement
{
this.returnExpression = returnExpression;
this();
}
this()
{
/* Statement level weighting is 2 */
weight = 2;
}
@ -643,6 +1047,11 @@ public final class ReturnStmt : Statement
{
return returnExpression;
}
public bool hasReturnExpression()
{
return returnExpression !is null;
}
}
/**
@ -691,6 +1100,70 @@ public final class IfStatement : Entity, Container
{
return "IfStmt";
}
public override Statement[] search(TypeInfo_Class clazzType)
{
/* List of returned matches */
Statement[] matches;
/* Are we (ourselves) of this type? */
if(clazzType.isBaseOf(this.classinfo))
{
matches ~= [this];
}
/* Loop through each `Branch` and recurse on them */
foreach(Branch curBranch; branches)
{
matches ~= curBranch.search(clazzType);
}
return matches;
}
public override bool replace(Statement thiz, Statement that)
{
/* If we (`this`) are `thiz`, then we cannot replace */
if(this == thiz)
{
return false;
}
/* If not ourself, then check each `Branch` or recurse on them */
else
{
/**
* First check each `Branch` that makes up our
* branches array and see if we can replace that,
* else see if we can recurse on each of the branche
* and apply replacement therein
*/
Statement[] bodyStmts = getStatements();
for(ulong idx = 0; idx < bodyStmts.length; idx++)
{
Statement curBodyStmt = bodyStmts[idx];
/* Should we directly replace the Statement in the body? */
if(curBodyStmt == thiz)
{
// Replace the statement in the body
// FIXME: Apply parenting? Yes we should
branches[idx] = cast(Branch)that;
return true;
}
/* If we cannot, then recurse (try) on it */
else if(cast(MStatementReplaceable)curBodyStmt)
{
MStatementReplaceable curBodyStmtRepl = cast(MStatementReplaceable)curBodyStmt;
if(curBodyStmtRepl.replace(thiz, that))
{
return true;
}
}
}
return false;
}
}
}
/**
@ -759,6 +1232,50 @@ public final class WhileLoop : Entity, Container
{
return "WhileLoop";
}
public override Statement[] search(TypeInfo_Class clazzType)
{
/* List of returned matches */
Statement[] matches;
/* Are we (ourselves) of this type? */
if(clazzType.isBaseOf(this.classinfo))
{
matches ~= [this];
}
/* Recurse on the the `Branch` */
if(cast(MStatementSearchable)branch)
{
MStatementSearchable branchCasted = cast(MStatementSearchable)branch;
if(branchCasted)
{
matches ~= branchCasted.search(clazzType);
}
}
return matches;
}
public override bool replace(Statement thiz, Statement that)
{
/* If we (`this`) are `thiz`, then we cannot replace */
if(this == thiz)
{
return false;
}
/* If the `Branch` is to be replaced */
else if(branch == thiz)
{
branch = cast(Branch)that;
return true;
}
/* If not ourself, then recurse on the `Branch` */
else
{
return branch.replace(thiz, that);
}
}
}
public final class ForLoop : Entity, Container
@ -846,6 +1363,66 @@ public final class ForLoop : Entity, Container
{
return "ForLoop";
}
public override Statement[] search(TypeInfo_Class clazzType)
{
/* List of returned matches */
Statement[] matches;
/* Are we (ourselves) of this type? */
if(clazzType.isBaseOf(this.classinfo))
{
matches ~= [this];
}
/* Recurse on the pre-loop `Statement` */
if(cast(MStatementSearchable)preLoopStatement)
{
MStatementSearchable preLoopStatementCasted = cast(MStatementSearchable)preLoopStatement;
if(preLoopStatementCasted)
{
matches ~= preLoopStatementCasted.search(clazzType);
}
}
/* Recurse on the the `Branch` */
if(cast(MStatementSearchable)branch)
{
MStatementSearchable branchCasted = cast(MStatementSearchable)branch;
if(branchCasted)
{
matches ~= branchCasted.search(clazzType);
}
}
return matches;
}
public override bool replace(Statement thiz, Statement that)
{
/* If we (`this`) are `thiz`, then we cannot replace */
if(this == thiz)
{
return false;
}
/* If the `Branch` is to be replaced */
else if(branch == thiz)
{
branch = cast(Branch)that;
return true;
}
/* If the pre-loop `Statement` is to be replaced */
else if(preLoopStatement == thiz)
{
preLoopStatement = cast(Statement)that;
return true;
}
/* If not ourself, then recurse on the `Branch` */
else
{
return branch.replace(thiz, that);
}
}
}
/**
@ -928,9 +1505,95 @@ public final class Branch : Entity, Container
{
return "Branch";
}
public override Statement[] search(TypeInfo_Class clazzType)
{
/* List of returned matches */
Statement[] matches;
/* Are we (ourselves) of this type? */
if(clazzType.isBaseOf(this.classinfo))
{
matches ~= [this];
}
/* Recurse on the branch condition `Expression` */
if(cast(MStatementSearchable)branchCondition)
{
MStatementSearchable branchConditionCasted = cast(MStatementSearchable)branchCondition;
if(branchConditionCasted)
{
matches ~= branchConditionCasted.search(clazzType);
}
}
/* Recurse on each `Statement` making up our body */
// NOTE: Using weight-reordered? Is that fine?
foreach(Statement curStmt; getStatements())
{
MStatementSearchable curStmtCasted = cast(MStatementSearchable)curStmt;
if(curStmtCasted)
{
matches ~= curStmtCasted.search(clazzType);
}
}
return matches;
}
public override bool replace(Statement thiz, Statement that)
{
/* If we (`this`) are `thiz`, then we cannot replace */
if(this == thiz)
{
return false;
}
/* If the branch condition `Expression` is matching */
else if(branchCondition == thiz)
{
branchCondition = cast(Expression)that;
return true;
}
/* If not ourself, then check the body statements */
else
{
/**
* First check each `Statement` that make sup our
* body and see if we can replace that, else see
* if we can recurse on each of the body statements
* and apply replacement therein
*/
// NOTE: Using weight-reordered? Is that fine?
Statement[] bodyStmts = getStatements();
for(ulong idx = 0; idx < bodyStmts.length; idx++)
{
Statement curBodyStmt = bodyStmts[idx];
/* Should we directly replace the Statement in the body? */
if(curBodyStmt == thiz)
{
// Replace the statement in the body
// FIXME: Apply parenting? Yes we should
branchBody[idx] = that;
return true;
}
/* If we cannot, then recurse (try) on it */
else if(cast(MStatementReplaceable)curBodyStmt)
{
MStatementReplaceable curBodyStmtRepl = cast(MStatementReplaceable)curBodyStmt;
if(curBodyStmtRepl.replace(thiz, that))
{
return true;
}
}
}
return false;
}
}
}
public final class DiscardStatement : Statement
public final class DiscardStatement : Statement, MStatementSearchable, MStatementReplaceable
{
private Expression expression;
@ -951,6 +1614,54 @@ public final class DiscardStatement : Statement
{
return "[DiscardStatement: (Exp: "~expression.toString()~")]";
}
public override Statement[] search(TypeInfo_Class clazzType)
{
/* List of returned matches */
Statement[] matches;
/* Are we (ourselves) of this type? */
if(clazzType.isBaseOf(this.classinfo))
{
matches ~= [this];
}
/* Recurse on our `Expression` (if possible) */
MStatementSearchable innerStmt = cast(MStatementSearchable)expression;
if(innerStmt)
{
matches ~= innerStmt.search(clazzType);
}
return matches;
}
public override bool replace(Statement thiz, Statement that)
{
import std.stdio;
writeln("Replace() enter discard");
/* Check if our `Expression` matches, then replace */
if(expression == thiz)
{
// NOTE: This legit makes no sense and won't do anything, we could remove this
// and honestly should probably make this return false
// FIXME: Make this return `false` (see above)
expression = cast(Expression)that;
return true;
}
/* If not direct match, then recurse and replace (if possible) */
else if(cast(MStatementReplaceable)expression)
{
MStatementReplaceable replStmt = cast(MStatementReplaceable)expression;
return replStmt.replace(thiz, that);
}
/* If not direct match and not replaceable */
else
{
return false;
}
}
}
public final class ExternStmt : Statement

@ -3,6 +3,9 @@ module tlang.compiler.symbols.expressions;
import tlang.compiler.symbols.data;
import std.conv : to;
// AST manipulation interfaces
import tlang.compiler.symbols.mcro : MStatementSearchable, MStatementReplaceable, MCloneable;
/* TODO: Look into arrays later */
public class StringExpression : Expression
{
@ -56,7 +59,7 @@ public class UnaryOperatorExpression : OperatorExpression
}
}
public class BinaryOperatorExpression : OperatorExpression
public class BinaryOperatorExpression : OperatorExpression, MStatementSearchable, MStatementReplaceable, MCloneable
{
private Expression lhs, rhs;
@ -83,6 +86,112 @@ public class BinaryOperatorExpression : OperatorExpression
/* TODO: FIll in */
return "[BinOpExp: Op: "~to!(string)(operator)~", Lhs: "~lhs.toString()~", Rhs: "~rhs.toString()~"]";
}
public override Statement[] search(TypeInfo_Class clazzType)
{
/* List of returned matches */
Statement[] matches;
/* Are we (ourselves) of this type? */
if(clazzType.isBaseOf(this.classinfo))
{
matches ~= [this];
}
/* Recurse on our left-hand side `Expression` (if possible) */
MStatementSearchable lhsCasted = cast(MStatementSearchable)lhs;
if(lhsCasted)
{
matches ~= lhsCasted.search(clazzType);
}
/* Recurse on our right-hand side `Expression` (if possible) */
MStatementSearchable rhsCasted = cast(MStatementSearchable)rhs;
if(rhsCasted)
{
matches ~= rhsCasted.search(clazzType);
}
return matches;
}
public override bool replace(Statement thiz, Statement that)
{
/* We cannot directly replace ourselves */
if(this == thiz)
{
return false;
}
/* Is the left-hand side `Expression` to be replaced? */
else if(thiz == lhs)
{
lhs = cast(Expression)that;
return true;
}
/* Is the right-hand side `Expression` to be replaced? */
else if(thiz == rhs)
{
rhs = cast(Expression)that;
return true;
}
/* If not direct match, then recurse and replace on left-hand side `Expression` (if possible) */
else if(cast(MStatementReplaceable)lhs)
{
MStatementReplaceable lhsCasted = cast(MStatementReplaceable)lhs;
return lhsCasted.replace(thiz, that);
}
/* If not direct match, then recurse and replace on right-hand side `Expression` (if possible) */
else if(cast(MStatementReplaceable)rhs)
{
MStatementReplaceable rhsCasted = cast(MStatementReplaceable)rhs;
return rhsCasted.replace(thiz, that);
}
/* If not direct match and not replaceable */
else
{
return false;
}
}
/**
* Clones this binery operator expression recursively
* returning a fresh new copy of itself and its
* left and right operands
*
* Param:
* newParent = the `Container` to re-parent the
* cloned `Statement`'s self to
*
* Returns: the cloned `Statement`
*/
public override Statement clone(Container newParent = null)
{
BinaryOperatorExpression clonedBinaryOp;
// Clone the left-hand operand expression (if supported, TODO: throw an error if not)
Expression clonedLeftOperandExpression = null;
if(cast(MCloneable)this.lhs)
{
MCloneable cloneableExpression = cast(MCloneable)this.lhs;
clonedLeftOperandExpression = cast(Expression)cloneableExpression.clone(); // NOTE: We must parent it if needs be
}
// Clone the left-hand operand expression (if supported, TODO: throw an error if not)
Expression clonedRightOperandExpression = null;
if(cast(MCloneable)this.rhs)
{
MCloneable cloneableExpression = cast(MCloneable)this.rhs;
clonedRightOperandExpression = cast(Expression)cloneableExpression.clone(); // NOTE: We must parent it if needs be
}
// Clone ourselves
clonedBinaryOp = new BinaryOperatorExpression(this.operator, clonedLeftOperandExpression, clonedRightOperandExpression);
// Parent outselves to the given parent
clonedBinaryOp.parentTo(newParent);
return clonedBinaryOp;
}
}
public enum IntegerLiteralEncoding
@ -93,7 +202,7 @@ public enum IntegerLiteralEncoding
UNSIGNED_LONG
}
public final class IntegerLiteral : NumberLiteral
public class IntegerLiteral : NumberLiteral, MCloneable
{
private IntegerLiteralEncoding encoding;
@ -112,6 +221,27 @@ public final class IntegerLiteral : NumberLiteral
{
return "[integerLiteral: "~numberLiteral~" ("~to!(string)(encoding)~")]";
}
/**
* Clones this integer literal
*
* Param:
* newParent = the `Container` to re-parent the
* cloned `Statement`'s self to
*
* Returns: the cloned `Statement`
*/
public override Statement clone(Container newParent = null)
{
IntegerLiteral clonedIntegerLiteral;
clonedIntegerLiteral = new IntegerLiteral(this.numberLiteral, this.encoding);
// Parent outselves to the given parent
clonedIntegerLiteral.parentTo(newParent);
return clonedIntegerLiteral;
}
}
//TODO: Work on floating point literal encodings
@ -143,25 +273,16 @@ public abstract class NumberLiteral : Expression
{
return numberLiteral;
}
}
public class Expression : Statement
{
import tlang.compiler.typecheck.core;
/* TODO: Takes in symbol table? */
public string evaluateType(TypeChecker typechecker, Container c)
{
/* TODO: Go through here evaluating the type */
return null;
}
this()
public final void setNumber(string numberLiteral)
{
this.numberLiteral = numberLiteral;
}
}
/* TODO: Evalute this expression's type */
public abstract class Expression : Statement
{
}
public final class NewExpression : Expression
@ -179,7 +300,7 @@ public final class NewExpression : Expression
}
}
public final class CastedExpression : Expression
public final class CastedExpression : Expression, MCloneable
{
private Expression uncastedExpression;
private string toType;
@ -199,6 +320,36 @@ public final class CastedExpression : Expression
{
return uncastedExpression;
}
/**
* Clones this casted expression recursively
* and returns a fresh copy of it
*
* Param:
* newParent = the `Container` to re-parent the
* cloned `Statement`'s self to
*
* Returns: the cloned `Statement`
*/
public override Statement clone(Container newParent = null)
{
CastedExpression clonedCastedExpression;
// Clone the uncasted expression (if supported, TODO: throw an error if not)
Expression clonedUncastedExpression = null;
if(cast(MCloneable)this.uncastedExpression)
{
MCloneable cloneableExpression = cast(MCloneable)this.uncastedExpression;
clonedUncastedExpression = cast(Expression)cloneableExpression.clone(); // NOTE: We must parent it if needs be
}
clonedCastedExpression = new CastedExpression(this.toType, clonedUncastedExpression);
// Parent outselves to the given parent
clonedCastedExpression.parentTo(newParent);
return clonedCastedExpression;
}
}
public final class ArrayIndex : Expression

@ -0,0 +1,70 @@
module tlang.compiler.symbols.mcro;
import tlang.compiler.symbols.data;
public class Macro : Statement
{
}
public interface MTypeRewritable
{
public string getType();
public void setType(string type);
}
/**
* Anything which implements this has the ability
* to search for objects of the provided type,
* and return a list of them
*/
public interface MStatementSearchable
{
/**
* Searches for all objects of the given type
* and returns an array of them. Only if the given
* type is equal to or sub-of `Statement`
*
* Params:
* clazzType = the type to search for
* Returns: an array of `Statement` (a `Statement[]`)
*/
public Statement[] search(TypeInfo_Class clazzType);
}
/**
* Anything which implements this has the ability
* to, given an object `x`, return a `ref x` to it
* hence allowing us to replace it
*/
public interface MStatementReplaceable
{
/**
* Replace a given `Statement` with another `Statement`
*
* Params:
* thiz = the `Statement` to replace
* that = the `Statement` to insert in-place
* Returns: `true` if the replacement succeeded, `false` otherwise
*/
public bool replace(Statement thiz, Statement that);
}
/**
* Anything which implements this can make a full
* deep clone of itself
*/
public interface MCloneable
{
/**
* Returns a `Statement` which is a clone of this one
* itself
*
* Param:
* newParent = the `Container` to re-parent the
* cloned `Statement`'s self to
*
* Returns: the cloned `Statement`
*/
public Statement clone(Container newParent = null);
}

@ -1,3 +1,8 @@
/**
* Routines for determining, based on an input strung,
* the built-in type that is associated with that
* identifier/name
*/
module tlang.compiler.symbols.typing.builtins;
import tlang.compiler.symbols.typing.core;
@ -11,6 +16,18 @@ import std.conv : to;
* no machine is good if int is not 4, as in imagine short being max addressable unit
* like no, fuck that (and then short=int=long, no , that is shit AND is NOT WHAT TLANG aims for)
*/
/**
* Creates a new instance of the type that is detected via
* the given string. Only for built-in types.
*
* Example, if given `"int"` then you will get an instance
* of `new Integer("int", 4, true)`
*
* Params:
* tc = the associated `TypeChecker` required for lookups
* typeString = the type string to test
* Returns: the `Type` found, if not found then `null`
*/
public Type getBuiltInType(TypeChecker tc, string typeString)
{
gprintln("getBuiltInType("~typeString~")");

@ -1,3 +1,8 @@
/**
* Data structures which represent kind-of `Entity`(s),
* starting with the base-`Entity`, `Type`, which represents
* a name that describes a data type
*/
module tlang.compiler.symbols.typing.core;
import tlang.compiler.symbols.data;
@ -6,6 +11,10 @@ import std.conv : to;
public import tlang.compiler.symbols.typing.builtins;
/**
* The base entity from which all types are derived
* from
*/
public class Type : Entity
{
/* TODO: Add width here */
@ -16,6 +25,14 @@ public class Type : Entity
* Actually yeah, we should, as Number types won't be entities
* Wait lmao they will
*/
/**
* Constructs a new `Type` with the
* given name
*
* Params:
* name = the new type's name
*/
this(string name)
{
super(name);
@ -25,16 +42,33 @@ public class Type : Entity
// ... where referene equality was used, hence I stear clear of that
}
/**
* Represents a void type, a type
* which has no return value for it
*/
public final class Void : Primitive
{
/**
* Constructs a new `Void` type
*/
this()
{
super("void");
}
}
/**
* Represents all primitive data types
*/
public class Primitive : Type
{
/**
* Constructs a new `Primitive`
* type
*
* Params:
* name = the new type's name
*/
this(string name)
{
super(name);
@ -42,63 +76,148 @@ public class Primitive : Type
}
/* TODO: Move width to Type class */
/**
* Represents any kind of number
*
* This means it has a width associated
* with it which is the number of bytes
* wide it is
*/
public class Number : Primitive
{
/* Number of bytes (1,2,4,8) */
/**
* Number of bytes (1,2,4,8)
*/
private ubyte width;
/* TODO: Aligbment details etc. */
/**
* COnstructs a new `Number` type
* with the given name and width
*
* Params:
* name = the new type's name
* width = the bit-width (in bytes)
*/
this(string name, ubyte width)
{
super(name);
this.width = width;
}
/**
* Returns the bit-width of this number
*
* Returns: the width in bytes
*/
public final ubyte getSize()
{
return width;
}
}
/**
* Represents an integer, a kind-of `Number`,
* but with a signedness/unsignedness encoding
* scheme associated with it
*/
public class Integer : Number
{
/* Whether or not signed (if so, then 2's complement) */
/**
* Whether or not signed (if so, then 2's complement)
*/
private bool signed;
/**
* Constructs a new `Integer` type with the given
* parameters
*
* Params:
* name = the name of this type
* width = the bit-width (in bytes)
* signed = whether or not it represents a
* two's complement signed integer or not
*/
this(string name, ubyte width, bool signed = false)
{
super(name, width);
this.signed = signed;
}
/**
* Determines if the type of
* integer being described is signed
* or not
*
* Returns: `true` if signed, `false`
* otherwise
*/
public final bool isSigned()
{
return signed;
}
/* TODO: Remove ig */
/**
* Returns a string representation of
* this `Integer` type
*
* Returns: a `string`
*/
public override string toString()
{
return name;
}
}
/**
* Represents a floating point number
*/
public class Float : Number
{
/**
* Constructs a new floating point
* type with the given name and width
*
* Params:
* name = this type's name
* width = the width (in bytes) of
* the floating point
*/
this(string name, ubyte width)
{
super(name, width);
}
}
/**
* A `Pointer`, is a kind-of `Integer`
* which is unsigned. This represents
* a memory address and is CURRENTLY
* set to `8` bytes (TODO: Change this
* to be dependent on the system used
* basically it should actually take
* in a size)
*
* A pointer is a 64-bit integer
* that point to data in memory of
* another given type
*/
public class Pointer : Integer
{
/* Data type being pointed to */
/**
* Data type being pointed to
*/
private Type dataType;
/**
* Constructs a new `Pointer` to point
* to data of the given type
*
* Params:
* dataType = the `Type` of data being
* pointed to
*/
this(Type dataType)
{
/* The name should be `dataType*` */
@ -109,6 +228,12 @@ public class Pointer : Integer
this.dataType = dataType;
}
/**
* Returns the `Type` of the data this
* pointer refers to
*
* Returns: the `Type`
*/
public Type getReferredType()
{
return dataType;
@ -120,12 +245,25 @@ public class Pointer : Integer
*/
public class StackArray : Type
{
/* Size of the stack array to allocate */
/**
* Size of the stack array to allocate
*/
private ulong arraySize;
/* Component type */
/**
* Component type
*/
private Type elementType;
/**
* Constructs a new `StackArray` type
* with the given type of element and
* the size to allocate on the stack
*
* Params:
* elementType = the component `Type`
* arraySize = the stack allocation size
*/
this(Type elementType, ulong arraySize)
{
/* The name should be `elementType[arraySize]` */
@ -135,11 +273,22 @@ public class StackArray : Type
this.arraySize = arraySize;
}
/**
* Gets the stack array's element type
*
* Returns: the `Type` of the components
*/
public Type getComponentType()
{
return elementType;
}
/**
* Gets the size to be allocated on the stack
* for this array
*
* Returns: the size
*/
public ulong getAllocatedSize()
{
return arraySize;

File diff suppressed because it is too large Load Diff

@ -479,6 +479,19 @@ public class DNodeGenerator
//generate();
}
/**
* Crashes the dependency generator with an
* expectation message by throwing a new
* `DependencyException`.
*
* Params:
* message = the expectation message
*/
public void expect(string message)
{
throw new DependencyException(DependencyError.GENERAL_ERROR, message);
}
public DNode root;
@ -702,14 +715,14 @@ public class DNodeGenerator
}
else
{
Parser.expect("Only class-type may be used with `new`");
expect("Only class-type may be used with `new`");
assert(false);
}
gprintln("Poe naais");
}
else
{
Parser.expect("Invalid ryp");
expect("Invalid ryp");
assert(false);
}
// FunctionCall
@ -817,7 +830,7 @@ public class DNodeGenerator
}
else
{
Parser.expect("Cannot reference variable "~nearestName~" which exists but has not been declared yet");
expect("Cannot reference variable "~nearestName~" which exists but has not been declared yet");
}
@ -865,7 +878,7 @@ public class DNodeGenerator
}
else
{
Parser.expect("No entity by the name "~nearestName~" exists (at all)");
expect("No entity by the name "~nearestName~" exists (at all)");
}
@ -960,7 +973,7 @@ public class DNodeGenerator
}
else
{
Parser.expect("Could not acces \""~remainingSegment~"\" as it is not a container");
expect("Could not acces \""~remainingSegment~"\" as it is not a container");
}
}
@ -971,7 +984,7 @@ public class DNodeGenerator
*/
else
{
Parser.expect("Could not find an entity named "~remainingSegment);
expect("Could not find an entity named "~remainingSegment);
}
}
@ -1391,7 +1404,7 @@ public class DNodeGenerator
}
else
{
Parser.expect("Cannot reference variable "~vAsStdAl.getVariableName()~" which exists but has not been declared yet");
expect("Cannot reference variable "~vAsStdAl.getVariableName()~" which exists but has not been declared yet");
return null;
}
}
@ -1462,12 +1475,16 @@ public class DNodeGenerator
DNode returnStatementDNode = pool(returnStatement);
/* Process the return expression */
Expression returnExpression = returnStatement.getReturnExpression();
DNode returnExpressionDNode = expressionPass(returnExpression, context);
/* Check if this return statement has an expression attached */
if(returnStatement.hasReturnExpression())
{
/* Process the return expression */
Expression returnExpression = returnStatement.getReturnExpression();
DNode returnExpressionDNode = expressionPass(returnExpression, context);
/* Make return depend on the return expression */
returnStatementDNode.needs(returnExpressionDNode);
/* Make return depend on the return expression */
returnStatementDNode.needs(returnExpressionDNode);
}
/* Make this container depend on this return statement */
// node.needs(returnStatementDNode);
@ -1680,6 +1697,23 @@ public class DNodeGenerator
/* NOTE: If anything we ought to remove these ExternSTmt nodes during such a process */
return null;
}
/**
* Function call (statement-level)
*/
else if(cast(FunctionCall)entity)
{
FunctionCall funcCall = cast(FunctionCall)entity;
funcCall.setContext(context);
// It MUST be if we are processing it in `generalPass()`
assert(funcCall.isStatementLevelFuncCall());
gprintln("Function calls (at statement level)", DebugType.INFO);
// The FunctionCall is an expression, so to get a DNode from it `expressionPass()` it
DNode funcCallDNode = expressionPass(funcCall, context);
return funcCallDNode;
}
return null;
}
@ -1770,7 +1804,7 @@ public class DNodeGenerator
/* Sanity check */
if(clazz.getModifierType() != InitScope.STATIC)
{
Parser.expect("SanityCheck: poolClassStatic(): Cannot pool a non-static class");
expect("SanityCheck: poolClassStatic(): Cannot pool a non-static class");
// assert(clazz.getModifierType() == InitScope.STATIC);
}

@ -8,6 +8,7 @@ public enum DependencyError
{
NOT_YET_LINEARIZED,
ALREADY_LINEARIZED,
GENERAL_ERROR
}
public final class DependencyException : TError

@ -19,11 +19,14 @@ public class TypeCheckerException : TError
GENERAL_ERROR
}
private TypecheckError errType;
this(TypeChecker typeChecker, TypecheckError errType, string msg = "")
{
/* We set it after each child class calls this constructor (which sets it to empty) */
super("TypeCheck Error ("~to!(string)(errType)~")"~(msg.length > 0 ? ": "~msg : ""));
this.typeChecker = typeChecker;
this.errType = errType;
}
// TODO: Remove this constructor and make anything that is currently using it
@ -32,10 +35,17 @@ public class TypeCheckerException : TError
{
this(typeChecker, TypecheckError.GENERAL_ERROR);
}
public TypecheckError getError()
{
return errType;
}
}
public final class TypeMismatchException : TypeCheckerException
{
private Type originalType, attemptedType;
this(TypeChecker typeChecker, Type originalType, Type attemptedType, string msgIn = "")
{
super(typeChecker);
@ -43,6 +53,46 @@ public final class TypeMismatchException : TypeCheckerException
msg = "Type mismatch between type "~originalType.getName()~" and "~attemptedType.getName();
msg ~= msgIn.length > 0 ? ": "~msgIn : "";
this.originalType = originalType;
this.attemptedType = attemptedType;
}
public Type getExpectedType()
{
return originalType;
}
public Type getAttemptedType()
{
return attemptedType;
}
}
public final class CoercionException : TypeCheckerException
{
private Type toType, fromType;
this(TypeChecker typeChecker, Type toType, Type fromType, string msgIn = "")
{
super(typeChecker);
msg = "Cannot coerce from type '"~fromType.getName()~"' to type '"~toType.getName()~"'";
msg ~= msgIn.length > 0 ? ": "~msgIn : "";
this.toType = toType;
this.fromType = fromType;
}
public Type getToType()
{
return toType;
}
public Type getFromType()
{
return fromType;
}
}

@ -0,0 +1,413 @@
module tlang.compiler.typecheck.meta;
import tlang.compiler.symbols.data : Statement, TypedEntity, Function, FunctionCall, IdentExpression;
import tlang.compiler.symbols.expressions : Expression, IntegerLiteral, IntegerLiteralEncoding;
import tlang.compiler.symbols.typing.core;
import tlang.compiler.symbols.containers : Container;
import tlang.compiler.symbols.mcro;
import tlang.compiler.typecheck.core;
import gogga;
import std.conv : to;
import tlang.compiler.configuration;
/**
* The `MetaProcessor` is used to do a pass over a `Container`
* to process any macro and macro-like entities
*/
public class MetaProcessor
{
private TypeChecker tc;
private bool isMetaEnabled;
private CompilerConfiguration compilerConfig;
/**
* Constructs a new `MetaProcessor` for the purposes of
* modifying the AST tree before the typechecker traverses
* it
*
* Params:
* tc = the `TypeChecker` instance to process
* isMetaEnabled = `true` if to perform meta processing, otherwise `false`
*/
this(TypeChecker tc, bool isMetaEnabled)
{
this.tc = tc;
this.isMetaEnabled = isMetaEnabled;
this.compilerConfig = tc.getConfig();
}
/**
* Analyzes the provided `Container` and searches for any `Macro`-like
* parse-nodes to process
*/
public void process(Container container)
{
/* Only apply meta-processing if enabled */
if(!isMetaEnabled)
{
return;
}
/* Get all statements */
Statement[] stmts = container.getStatements();
foreach(Statement curStmt; stmts)
{
gprintln("MetaProcessor: Examining AST node '"~curStmt.toString()~"'...");
// Perform replacement of all type alises to concrete types, such as `size_t`
doTypeAlias(container, curStmt);
/**
* Search for any `sizeof(<ident_type>)` expressions
* and replace them with a `NumberLiteral`
*/
if(cast(MStatementSearchable)curStmt && cast(MStatementReplaceable)curStmt)
{
MStatementSearchable searchableStmt = cast(MStatementSearchable)curStmt;
Statement[] foundStmts = searchableStmt.search(FunctionCall.classinfo);
gprintln("Nah fr");
foreach(Statement curFoundStmt; foundStmts)
{
FunctionCall curFuncCall = cast(FunctionCall)curFoundStmt;
if(curFuncCall.getName() == "sizeof")
{
gprintln("Elo");
Expression[] arguments = curFuncCall.getCallArguments();
if(arguments.length == 1)
{
IdentExpression potentialIdentExp = cast(IdentExpression)arguments[0];
if(potentialIdentExp)
{
string typeName = potentialIdentExp.getName();
IntegerLiteral replacementStmt = sizeOf_Literalize(typeName);
gprintln("sizeof: Replace '"~curFoundStmt.toString()~"' with '"~replacementStmt.toString()~"'");
/* Traverse down from the `Container` we are process()'ing and apply the replacement */
MStatementReplaceable containerRepl = cast(MStatementReplaceable)container;
containerRepl.replace(curFoundStmt, replacementStmt);
}
else
{
// TODO: Throw an exception here that an ident_type should be present as the argument
gprintln("The argument to `sizeof` should be an ident", DebugType.ERROR);
}
}
else
{
// TODO: Throw an exception here as only 1 argument is allowed
gprintln("To use the `sizeof` macro you require a single argument to be passed to it", DebugType.ERROR);
}
}
}
}
/**
* If the current statement is a Container then recurse
*
* This will help us do the following:
*
* 1. Type re-writing of
* a. Functions (Parameters and Body as both make up its Statement[])
*/
if(cast(Container)curStmt)
{
process(cast(Container)curStmt);
}
}
}
/**
* Re-writes the types for things such as `size_t`, `ssize_t` and so forth
*
* Params:
* statement = the `MTypeRewritable` to apply re-writing to
*/
private void typeRewrite(MTypeRewritable statement)
{
/* Applies re-write to Variable's declared type and Function's return type */
string type = statement.getType();
/* Only re-write if type alias */
if(isTypeAlias(type))
{
/* Get the concrete type of `type` */
string concreteType = getConcreteType(type);
/* Rewrite the type */
statement.setType(concreteType);
}
}
/**
* Performs the replacement of type alieses such as `size_t`, `ssize_t`
* and so forth with their concrete type
*
* Params:
* container = the current `Container` being processsed
* curStmt = the current `Statement` to consider
*/
private void doTypeAlias(Container container, Statement curStmt)
{
/**
* Apply type-rewriting to any `MTypeRewritable` AST node
* (a.k.a. a node which contains a type and can have it set)
*
* NOTE: This is just for the "type" fields in AST nodes,
* we should have some full recursive re-writer.
*
* An example of why is for supporting something like:
*
* `sizeof(size_t)` <- currently is not supported by this
*/
if(cast(MTypeRewritable)curStmt)
{
typeRewrite(cast(MTypeRewritable)curStmt);
}
/**
* Here we will also search for any `IdentExpression`
* which contains `size_t`, `ssize_t` etc. and replace
* them
*/
if(cast(MStatementSearchable)curStmt && cast(MStatementReplaceable)curStmt)
{
MStatementSearchable searchableStmt = cast(MStatementSearchable)curStmt;
IdentExpression[] foundStmts = cast(IdentExpression[])searchableStmt.search(IdentExpression.classinfo);
// TODO: Implement me
// gprintln("IdentExpressions found: "~to!(string)(foundStmts));
/**
* Loop through all `IdentExpression`s and find any
* occurence of `size_t`/`ssize_t` and replace those
* with the concrete type
*/
foreach(IdentExpression identExp; foundStmts)
{
string identName = identExp.getName();
/* Determine if this is a type alias? */
if(isTypeAlias(identName))
{
// Determine the concrete type
string concereteType = getConcreteType(identName);
gprintln("Found type alias '"~identName~"' which concretely is '"~concereteType~"'");
// Replace with concrete type
container.replace(identExp, new IdentExpression(concereteType));
}
}
}
}
private IntegerLiteral sizeOf_Literalize(string typeName)
{
IntegerLiteral literal = new IntegerLiteral("TODO_LITERAL_GOES_HERESIZEOF_REPLACEMENT", IntegerLiteralEncoding.UNSIGNED_INTEGER);
// TODO: Via typechecker determine size with a lookup
Type type = tc.getType(tc.getModule(), typeName);
/* Calculated type size */
ulong typeSize = 0;
/**
* Calculate stack array size
*
* Algo: `<componentType>.size * stackArraySize`
*/
if(cast(StackArray)type)
{
StackArray stackArrayType = cast(StackArray)type;
ulong arrayLength = stackArrayType.getAllocatedSize();
Type componentType = stackArrayType.getComponentType();
ulong componentTypeSize = 0;
// FIXME: Later, when the Dependency Genrator supports more advanced component types,
// ... we will need to support this - for now assume that `componentType` is primitive
if(cast(Number)componentType)
{
Number numberType = cast(Number)componentType;
componentTypeSize = numberType.getSize();
}
typeSize = componentTypeSize*arrayLength;
}
/**
* Calculate the size of `Number`-based types
*/
else if(cast(Number)type)
{
Number numberType = cast(Number)type;
typeSize = numberType.getSize();
}
// TODO: We may eed toupdate Type so have bitwidth or only do this
// for basic types - in which case I guess we should throw an exception
// here.
// ulong typeSize =
/* Update the `Sizeof` kind-of-`IntegerLiteral` with the new size */
literal.setNumber(to!(string)(typeSize));
return literal;
}
/**
* Transforms the type alias into its concrete type.
*
* This method incorporates defensive programming in
* that it will only apply the transformation IF
* the provided type alias is infact a type alias,
* otherwise it performs an identity transformation
* and returns the "alias" untouched.
*
* Params:
* typeAlias = the potential type alias
* Returns: the concrete type, or `typeAlias` if
* not an alias
*/
private string getConcreteType(string typeAlias)
{
/* Check if this is a system type alias? If so, transform */
if(isSystemType(typeAlias))
{
return getSystemType(typeAlias);
}
// TODO: Add user-defined type alias support here
/* Else, return the "alias" untouched */
else
{
return typeAlias;
}
}
/**
* Determines if the given type is a type alias.
*
* Params:
* typeAlias = the type to check
* Returns: `true` if it is an alias, `false` otherwise
*/
private bool isTypeAlias(string typeAlias)
{
/* If this a system type alias? */
if(isSystemType(typeAlias))
{
return true;
}
// TODO: Support for user-defined type aliases
/* Otherwise, not a type alias */
else
{
return false;
}
}
/**
* Determines if the given type is a system type alias
*
* Params:
* typeAlias = the type to check
* Returns: `true` if system type alias, `false` otherwise
*/
private bool isSystemType(string typeAlias)
{
/* `size_t`/`ssize_t` system type aliases */
if(typeAlias == "size_t" || typeAlias == "ssize_t")
{
return true;
}
/* Else, not a system type alias */
else
{
return false;
}
}
/**
* Given a type alias (think `size_t`/`ssize_t` for example) this will
* look up in the compiler's configuration what that size should be
* resolved to
*
* Params:
* typeAlias = the system type alias to lookup
* Returns: the concrete type
*/
private string getSystemType(string typeAlias)
{
/* Determine machine's width */
ulong maxWidth = compilerConfig.getConfig("types:max_width").getNumber();
string maxType;
if(maxWidth == 1)
{
if(typeAlias == "size_t")
{
return "ubyte";
}
else if(typeAlias == "ssize_t")
{
return "byte";
}
else
{
assert(false);
}
}
else if(maxWidth == 2)
{
if(typeAlias == "size_t")
{
return "ushort";
}
else if(typeAlias == "ssize_t")
{
return "short";
}
else
{
assert(false);
}
}
else if(maxWidth == 4)
{
if(typeAlias == "size_t")
{
return "uint";
}
else if(typeAlias == "ssize_t")
{
return "int";
}
else
{
assert(false);
}
}
else if(maxWidth == 8)
{
if(typeAlias == "size_t")
{
return "ulong";
}
else if(typeAlias == "ssize_t")
{
return "long";
}
else
{
assert(false);
}
}
else
{
assert(false);
}
}
}

@ -396,4 +396,35 @@ public final class Resolver
}
}
/**
* Given a type-of `Container` and a starting `Statement` (AST node) this will
* swim upwards to try and find the first matching parent of which is of the given
* type (exactly, not kind-of).
*
* Params:
* containerType = the type-of `Container` to look for
* startingNode = the starting AST node (as a `Statement`)
* Returns: the found `Container`, or `null` if not found
*/
public Container findContainerOfType(TypeInfo_Class containerType, Statement startingNode)
{
// If the given AST objetc is null, return null
if(startingNode is null)
{
return null;
}
// If the given AST object's type is of the type given
else if(typeid(startingNode) == containerType)
{
// Sanity check: You should not be calling with a TypeInfo_Class referring to a non-`Container`
assert(cast(Container)startingNode);
return cast(Container)startingNode;
}
// If not, swim up to the parent
else
{
return findContainerOfType(containerType, cast(Statement)startingNode.parentOf());
}
}
}

@ -0,0 +1,6 @@
module simple_meta_replace;
void function()
{
discard repr;
}

@ -0,0 +1,7 @@
module meta_sizeof;
size_t myVar1 = sizeof(uint);
size_t myVar2 = sizeof(ubyte);
size_t myVar3 = sizeof(ushort)+1;
myVar3 = sizeof(ulong)+sizeof(size_t);

@ -0,0 +1,3 @@
module simple_template_type_def;
generic T;

@ -0,0 +1,8 @@
module meta_types;
size_t myVar = 1;
size_t function(size_t param1)
{
return 1UL+myVar;
}

@ -0,0 +1,12 @@
module simple_return_bad;
int myFunction()
{
return 1;
}
int myFunction2()
{
return 2;
int j = 1;
}

@ -0,0 +1,11 @@
module simple_return_expressionless;
void expressionless()
{
return;
}
int expressionful()
{
return 2;
}

@ -0,0 +1,12 @@
module simple_return_good;
int myFunction()
{
return 1;
}
int myFunction2()
{
int j = 1;
return 2;
}

@ -0,0 +1,6 @@
module simple_return_type;
byte expressionful()
{
return 1;
}

@ -0,0 +1,13 @@
module simple_direct_func_call;
int myVar = 0;
void otherFunction(int i)
{
myVar = i;
}
void function()
{
otherFunction(69);
}

@ -6,9 +6,13 @@ int k = 22;
int apple(int arg1, int arg2)
{
int h = 69;
return 0;
}
int banana(int arg1)
{
int h = 64;
return 0;
}

@ -0,0 +1,13 @@
module simple_function_recursion_factorial;
ubyte factorial(ubyte i)
{
if(i == 0)
{
return 1;
}
else
{
return i*factorial(i-1);
}
}

@ -0,0 +1,6 @@
module simple_function_return_type_check_bad;
ubyte factorial(ubyte i)
{
return 256;
}

@ -0,0 +1,6 @@
module simple_function_return_type_check_good;
ubyte factorial(ubyte i)
{
return 1;
}

@ -21,4 +21,5 @@ int banana(int arg1)
k=1+h+apple(1, apple(2, 3))+k;
return 0;
}

@ -5,15 +5,15 @@ j = 2+func(j,test());
int func(int x1, byte x2)
{
return 1;
}
byte t2()
{
return 1;
}
byte test()
{
return 1;
}

@ -0,0 +1,7 @@
module simple_coerce_literal_bad;
void function()
{
long i1 = 1;
byte i = i1;
}

@ -0,0 +1,9 @@
module simple_coerce_literal_bad_stdalone_ass;
void function()
{
long i1 = 1;
byte i;
i = i1;
}

@ -0,0 +1,7 @@
module simple_coerce_literal_good;
void function()
{
byte i = 1UL;
long i1 = i;
}

@ -0,0 +1,9 @@
module simple_coerce_literal_good_stdalone_ass;
void function()
{
byte i = 1UL;
long i1;
i1 = i;
}

BIN
tlang

Binary file not shown.
Loading…
Cancel
Save