diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 3b58b380..11555aec 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -1,21 +1,8 @@
-### Description
-
-
+**Is your pull request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+**Describe the solution you've done**
+Key points of your tech and arch decisions.
-### Related Issues
-
-
-
-### References
-
-
-
-
-### Checklist:
-
-
-- [ ] I have followed the [contribution guidelines](../CONTRIBUTING.md) and code style for this project.
-- [ ] I have added tests covering my contributions.
-- [ ] I have updated the documentation accordingly.
-- [ ] I have added corresponding labels with respect to the part of the interpreter i've worked on.
+**Related Issues**
+If it fixes an open issue, please link to the issue here.
diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml
index 5c470007..be08eaf0 100644
--- a/.github/workflows/develop.yml
+++ b/.github/workflows/develop.yml
@@ -53,7 +53,10 @@ jobs:
run: dotnet test -c Debug --no-build -v n --filter-trait Category=Unit
- name: Integration Tests
run: |
- dotnet test --project tests/HydraScript.IntegrationTests -c Debug --no-build -v n --coverage --coverage-output-format cobertura --coverage-output coverage.cobertura.xml
+ dotnet test --project tests/HydraScript.IntegrationTests `
+ -c Debug --no-build -v n `
+ --coverage --coverage-output-format cobertura --coverage-output coverage.cobertura.xml `
+ --coverage-settings tests/coverage-exclude.xml
mkdir coverage-report
- name: Code Coverage Summary Report For Merge Request
if: github.event_name == 'pull_request'
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index cdd4d14d..93f4734f 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -8,6 +8,7 @@ on:
permissions:
contents: write
issues: write
+ actions: write
jobs:
create-release:
@@ -85,6 +86,8 @@ jobs:
name: Publish release
runs-on: ubuntu-latest
needs: upload-release-assets
+ outputs:
+ determined_version: ${{ needs.upload-release-assets.outputs.determined_version }}
steps:
- name: Setup .NET
uses: actions/setup-dotnet@v5
@@ -103,4 +106,24 @@ jobs:
with:
token: ${{ secrets.GITHUB_TOKEN }}
repository: 'Stepami/hydrascript'
- milestone: ${{ needs.upload-release-assets.outputs.determined_version }}
\ No newline at end of file
+ milestone: ${{ needs.upload-release-assets.outputs.determined_version }}
+
+ publish-nuget:
+ name: Publish as dotnet tool to NuGet
+ runs-on: windows-latest
+ needs: publish-release
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v5
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: 10.0.x
+ - name: Build
+ run: dotnet build ./src/HydraScript/HydraScript.csproj -c Release -v n `
+ /p:Version=${{ needs.publish-release.outputs.determined_version }} `
+ /p:PublishAot=false /p:PublishSingleFile=false
+ - name: Publish
+ run: dotnet nuget push ./src/HydraScript/bin/Release/*.nupkg `
+ --api-key ${{ secrets.NUGET_API_KEY }} `
+ --source https://api.nuget.org/v3/index.json
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
index c5a1f364..7676fe4e 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -7,11 +7,16 @@
true
latest
false
+ false
-
+
none
false
+ Size
+ true
+ false
+ true
diff --git a/Directory.Packages.props b/Directory.Packages.props
index c1782766..0e3eb7ec 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -1,17 +1,18 @@
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/ExtendedJavaScriptSubset.slnx b/ExtendedJavaScriptSubset.slnx
index f5a7e5c2..4932071c 100644
--- a/ExtendedJavaScriptSubset.slnx
+++ b/ExtendedJavaScriptSubset.slnx
@@ -1,6 +1,6 @@
-
+
@@ -28,6 +28,7 @@
+
@@ -48,6 +49,7 @@
+
diff --git a/Readme.md b/Readme.md
index 4a2220b5..14b84f5a 100644
--- a/Readme.md
+++ b/Readme.md
@@ -2,63 +2,74 @@

-## "Расширенное подмножество ЯП JavaScript"
+## Installation
-### Скачать
-
-Файл интерпретатора собирается в рамках релиза на три платформы:
+Interpreter executable is built during release for 3 following platforms:
- Windows (x64)
- MacOS (arm64 Apple Silicon)
- Linux (x64)
-Скачать нужную версию можно со страницы соответствующего релиза
+Download hydrascript executable on the corresponding release page.
+
+[The latest relase is available here.](https://github.com/Stepami/hydrascript/releases/latest)
+
+Alternatively you can consume HydraScript as dotnet tool:
+```
+dotnet tool update --global hydrascript
+```
-[Последний релиз доступен по этой ссылке](https://github.com/Stepami/hydrascript/releases/latest)
+## Project History
-### Вводная информация
+It started as a bachelor thesis "Extended JavaScript Subset". Now it's named "HydraScript", because as I fix one bug another occurs.
-За основу был взят стандарт [ECMA-262](https://www.ecma-international.org/publications-and-standards/standards/ecma-262/)
+I took [ECMA-262 standard](https://www.ecma-international.org/publications-and-standards/standards/ecma-262/) as a basis and made:
+- [Lexical structure](src/Domain/HydraScript.Domain.Constants/TokenTypes.cs)
+- [Grammar](src/Domain/HydraScript.Domain.FrontEnd/Parser/grammar.txt)
-[Лексическая структура](src/Domain/HydraScript.Domain.Constants/TokenTypes.cs)
+[Working samples can be found here.](tests/HydraScript.IntegrationTests/Samples) I use them for integration tests.
-[Грамматика](src/Domain/HydraScript.Domain.FrontEnd/Parser/grammar.txt)
+## Project Goals
+1. Partially implement JavaScript with objects and strong static structural typing, getting rid of such things as: `constructor`, `class`, `interface`
+2. Publicly reverse engineer modern static analysis (type inference, forward refs, compile-time detection of the runtime errors, etc.)
+3. Simplifying compilers construction domain with HydraScript source code
+4. Gather clear and concise standard problem solutions and algorithm implementations (Lexer, Parser, CFG, SSA, DCE, etc.)
-[Рабочие примеры](tests/HydraScript.IntegrationTests/Samples)
+## Language docs
-### Цели проекта
-1. Частично реализовать JavaScript с объектами и статической структурной типизацией, избавившись от таких понятий, как `constructor`, `class`, `interface`
-2. Публично реверс-инжинирить современный статический анализ (вывод типов, форвард рефы, ошибки выполнения на стадии компиляции)
-3. Упростить понимание области конструирования компиляторов за счёт исходного кода проекта - собрать понятные реализации алгоритмов и типовых задач в репозитории (Lexer, Parser, CFG, SSA, DCE, etc.)
+### Comments
-### Конструкции языка
+All comments are single-line:
+```
+// double slash comment
+# shebang comment
+```
-#### Типизация
-В языке структурная статическая сильная типизация.
+### Typing
+The HydraScript has strong static structural typing.
-Есть 5 примитивных типов:
+There are 5 primitive types:
1. number
2. boolean
3. string
4. null
5. void
-Остальные типы делятся на группы:
-- NullableType (тип, который допускает значение ```null```)
-- ObjectType (тип объекта, является NullableType)
-- ArrayType (списковый тип)
+The other types can be grouped as such:
+- NullableType (type that allows ```null```)
+- ObjectType (the type of object, obviously, and also is NullableType)
+- ArrayType (the type of array)
-##### Значения по умолчанию
+**The types have the following default values:**
-| Тип | Значение |
-|--------------|----------|
-| number | 0 |
-| boolean | false |
-| string | "" |
-| NullableType | null |
-| ArrayType | [] |
-##### type alias
-Можно создать свой type alias по типу того, как это сделано в С++
+| Type | Value |
+|--------------|-------|
+| number | 0 |
+| boolean | false |
+| string | "" |
+| NullableType | null |
+| ArrayType | [] |
+**You can also create your own type alias as in C++ or TypeScript:**
```
type int = number
type maybeInt = int?
@@ -72,42 +83,108 @@ type composite = {
arr: ints;
}
```
-#### Объявление переменных
+
+#### Strings
+
+Strings support following operations:
+- index access, returns `string`:
+```
+let str = "str"
+>>> str[1] // t
```
-let i = 1 // интерпретатор выведет тип из выражения
-let j: number // запишет значение по умолчанию в переменную
-let k: number = 1 // полностью явное объявление
+- length getter:
```
+let str = "str"
+>>> ~str // 3
+```
+
+### Variables
-#### Объекты
+For declaring mutable variables use `let`:
+```
+let i = 1 // HydraScript infers type from expression
+let j: number // Here it writes to j the default value
+let k: number = 1 // Fully explicit declaration
+```
+For declaring readonly variables use `const`:
+```
+const flag = true
+```
+
+To work with Environment Variables place `$` before identifier:
+```
+$MY_ENV_VAR = "my_env_var_value"
+const flagValue = $FLAG_VALUE
+```
+
+### Input and Output
+
+The HydraScript is able to print any values in console and read strings from it:
+```
+# output
+>>> [1, 2, 3]
+>>> { x: true; }
+>>> "Hello, world!"
+
+// input
+let name: string
+<<< name
+```
+
+### Objects
+
+Object literal declaration is similar to JSON:
```
let v2d = {
x: 3;
y: 4;
}
```
-#### Списки
+
+You can clone objects using `with` expressions like in C#:
+```
+let v = {
+ x: 2;
+ y: 1;
+}
+
+let vToX = v with { y: 0; }
+```
+
+### Arrays
+
+HydraScript arrays are basically lists. Legacy naming, unfortunately.
+
+Array literal declaration is similar to JSON:
```
let array = [1, 2, 3]
-let size = ~array // длина списка
-array::1 // удаление элемента по индексу
-array = array ++ [5, 7] // конкатенация списков
-```
-#### Операторы
-| Оператор | Вид | Типы операндов | Тип операции |
-|------------------|----------|------------------------|----------------|
-| + | бинарный | оба number, оба string | number, string |
-| *, -, /, % | бинарный | number | number |
-| ||, && | бинарный | boolean | boolean |
-| !=, == | бинарный | равный с двух сторон | boolean |
-| <=, >=, >, < | бинарный | number | boolean |
-| ! | унарный | boolean | boolean |
-| - | унарный | number | number |
-| ++ | бинарный | [] | [] |
-| :: | бинарный | [] и number | void |
-| ~ | унарный | [] | number |
-
-#### Ветвление
+```
+
+You have special operators to work with arrays:
+```
+let size = ~array // length
+array::1 // remove at index
+array = array ++ [5, 7] // concatenation
+```
+
+### Operators
+
+| Operator | Binary or Unary | Operand Types | Result Type |
+|------------------|-----------------|----------------------------|------------------|
+| + | binary | both number or both string | number or string |
+| *, -, /, % | binary | number | number |
+| ||, && | binary | boolean | boolean |
+| !=, == | binary | same on both sides | boolean |
+| <=, >=, >, < | binary | number | boolean |
+| ! | unary | boolean | boolean |
+| - | unary | number | number |
+| ++ | binary | [] | [] |
+| :: | binary | [] and number | void |
+| ~ | unary | [] or string | number |
+
+### Conditionals
+
+The language supports classic `if-else`:
```
if (1 == 1) {
// ...
@@ -117,15 +194,18 @@ if (1 == 1) {
else {
// ...
}
-// в общем как в Си подобных языках
-// главное, чтобы выражение условия
-// возвращало boolean
+// basically C-like syntax and semantic
+// test expression must have boolean type
```
-Также есть тернарный оператор
+
+There is also ternary operator:
```
let x = 1 > 0 ? 0 <= 1 ? 1 : 0 : -2 < 0 ? -1 : 0
```
-#### Цикл
+
+### Loops
+
+Now it has only `while`:
```
while (cond) {
// ...
@@ -134,62 +214,114 @@ while (cond) {
break
}
```
-#### Функции
+
+### Functions
+
+Functions are similar to TypeScript:
```
-// объявление
+// declaration
function add(a: number, b: number): number {
return a + b
}
-// вызов
+
+// call
let c = add(1, 2)
```
-#### Методы
+
+The return type can be specified explicitly or inferred:
```
-// сделаны подобно Go - привязка по имени типа
+// this is also valid
+function add(a: number, b: number) {
+ return a + b
+}
+let c = add(1, 2) // c is number
+```
+
+### Methods
-// шаг 1. Объявить type alias
+A function can be method if it's bound to an object using the "Golang-like approach":
+```
+# The binding is done with object type alias
+
+// step 1. Declare type alias
type Point2 = {
x: number;
y: number;
}
-// шаг 2. Объявить переменную этого типа
+// step 2. Declare variable of type defined earlier
+// It's obligatory to explicitly define type after colon to make it work
let v2d: Point2 = {
x: 3;
y: 4;
}
-// шаг 3. Указать первым параметром функции - объект типа
+// step 3. While declaring function place first the paremeter with aliased object type
+// it's basically explicit "this"
function lengthSquared(obj: Point2) {
let x = obj.x
let y = obj.y
return x * x + y * y
}
+
+// step 4. make a call
+let s = v2d.lengthSquared()
```
-#### Операции доступа
+
+### Overloads and default parameters
+
+Both, methods and functions, do support overloading and default parameters:
+```
+function func(x = 0){
+ >>> x
+}
+
+function func(x:number, y:number) {
+ return x + y
+}
+
+func()
+func(1)
+func(func(2, 3))
+```
+
+### Access operators
+
+Access means being able to get element of an array or property of an object:
```
-// объекты
+// objects
let x = v2d.x
-let s = v2d.lengthSquared()
-// массивы
+
+// arrays
let l = array[2]
```
-#### Приведение типов
+
+### Type casting
+
+HydraScript supports explicit casting through `as` keyword:
```
let s = v2d as string
+let one = "1" as number
+let falseValue = 0 as boolean
+
+let nullableNumber = null as number?
```
-#### Печать на экран
-```
-let obj = {}
->>>obj
->>>"Hello, World!"
-```
-### Требования
+One can cast using following rules:
+- `any` -> `string`
+- `string` -> `number`
+- `string` -> `boolean`
+- `boolean` -> `number`
+- `number` -> `boolean`
+- `type?` -> `type`
+- `type` -> `type?`
+- `null` -> `type?`
+
+## Requirements
-До версии **2.3.0** для запуска интерпретатора требовалась [установка .NET Runtime](https://dotnet.microsoft.com/ru-ru/download/dotnet)
+Before **2.3.0** version executable launch needed [.NET Runtime installed.](https://dotnet.microsoft.com/ru-ru/download/dotnet)
-Таблица соответствий hydrascript и dotnet:
+There is match table of hydrascript and dotnet:
| hydrascript | dotnet |
|-------------------------------------------------------------------|--------|
@@ -199,31 +331,31 @@ let obj = {}
| 2.0.0 | .NET 8 |
| | .NET 9 |
-### Сборка
-Необходим .NET SDK 9.0.202 (поддержка SLNX)
+If you use dotnet tool then requirements is specified on NuGet page.
-После клонирования репозитория идём в папку проекта `HydraScript`.
+## Build from source
+Install **latest** .NET SDK. The project uses **SLNX** as solution format.
-Там выполняем команду:
+Do this inside `HydraScript` root after cloning repository:
```dotnet publish ./src/HydraScript/HydraScript.csproj -r -o ```
-Список идентификаторов рантайма лежит [тут](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog#windows-rids)
+[Runtime identifier list can be found here](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog#windows-rids)
-### Запуск
+## How to run
-Простой:
+Default:
```
HydraScript file.js
```
-С выводом дебаг инфы (токены, ast, инструкции):
+Dumping debug info as files (tokens, ast, ir code):
```
HydraScript file.js --dump
```
-### Источники:
+## Sources:
-1. Курсы "Конструирование Компиляторов" и "Генерация Оптимального Кода" кафедры ИУ-9 МГТУ им. Н.Э. Баумана [@bmstu-iu9](https://www.github.com/bmstu-iu9)
+1. "Compilers Construction" and "Optimized Code Generation" courses of [@bmstu-iu9](https://www.github.com/bmstu-iu9)
2. [ECMA-262](https://www.ecma-international.org/publications-and-standards/standards/ecma-262/)
3. [DragonBook](https://suif.stanford.edu/dragonbook/)
4. [Stanford CS143 Lectures](https://web.stanford.edu/class/archive/cs/cs143/cs143.1128/)
diff --git a/icon.png b/icon.png
new file mode 100644
index 00000000..8de02b7f
Binary files /dev/null and b/icon.png differ
diff --git a/src/Application/HydraScript.Application.CodeGeneration/IValueDtoConverter.cs b/src/Application/HydraScript.Application.CodeGeneration/IValueDtoConverter.cs
deleted file mode 100644
index 36d70216..00000000
--- a/src/Application/HydraScript.Application.CodeGeneration/IValueDtoConverter.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using HydraScript.Domain.BackEnd;
-using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions.PrimaryExpressions;
-
-namespace HydraScript.Application.CodeGeneration;
-
-public interface IValueDtoConverter
-{
- public IValue Convert(ValueDto dto);
-}
\ No newline at end of file
diff --git a/src/Application/HydraScript.Application.CodeGeneration/IValueFactory.cs b/src/Application/HydraScript.Application.CodeGeneration/IValueFactory.cs
new file mode 100644
index 00000000..68505783
--- /dev/null
+++ b/src/Application/HydraScript.Application.CodeGeneration/IValueFactory.cs
@@ -0,0 +1,14 @@
+using HydraScript.Domain.BackEnd;
+using HydraScript.Domain.BackEnd.Impl.Values;
+using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions.PrimaryExpressions;
+
+namespace HydraScript.Application.CodeGeneration;
+
+public interface IValueFactory
+{
+ public IValue Create(ValueDto dto);
+
+ public Name Create(IdentifierReference id);
+
+ public Name Create(string id);
+}
\ No newline at end of file
diff --git a/src/Application/HydraScript.Application.CodeGeneration/Impl/ValueDtoConverter.cs b/src/Application/HydraScript.Application.CodeGeneration/Impl/ValueDtoConverter.cs
deleted file mode 100644
index ee3f919b..00000000
--- a/src/Application/HydraScript.Application.CodeGeneration/Impl/ValueDtoConverter.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using HydraScript.Domain.BackEnd;
-using HydraScript.Domain.BackEnd.Impl.Values;
-using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions.PrimaryExpressions;
-
-namespace HydraScript.Application.CodeGeneration.Impl;
-
-internal class ValueDtoConverter : IValueDtoConverter
-{
- public IValue Convert(ValueDto dto) =>
- dto switch
- {
- { Type: ValueDtoType.Constant, Label: not null } =>
- new Constant(dto.Value, dto.Label),
- { Type: ValueDtoType.Name, Name: not null } =>
- new Name(dto.Name),
- _ => throw new ArgumentOutOfRangeException(nameof(dto))
- };
-}
\ No newline at end of file
diff --git a/src/Application/HydraScript.Application.CodeGeneration/Impl/ValueFactory.cs b/src/Application/HydraScript.Application.CodeGeneration/Impl/ValueFactory.cs
new file mode 100644
index 00000000..2aa447fd
--- /dev/null
+++ b/src/Application/HydraScript.Application.CodeGeneration/Impl/ValueFactory.cs
@@ -0,0 +1,42 @@
+using HydraScript.Domain.BackEnd;
+using HydraScript.Domain.BackEnd.Impl.Frames;
+using HydraScript.Domain.BackEnd.Impl.Values;
+using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions.PrimaryExpressions;
+
+namespace HydraScript.Application.CodeGeneration.Impl;
+
+internal class ValueFactory(
+ IFrameContext frameContext,
+ IEnvironmentVariableProvider provider) : IValueFactory
+{
+ public IValue Create(ValueDto dto) =>
+ dto switch
+ {
+ { Type: ValueDtoType.Constant, Label: not null } =>
+ new Constant(dto.Value, dto.Label),
+ { Type: ValueDtoType.Name, Name: not null } =>
+ new Name(dto.Name, CurrentFrame),
+ { Type: ValueDtoType.Env, Name: not null } =>
+ new EnvName(dto.Name, EnvFrame),
+ _ => throw new ArgumentOutOfRangeException(nameof(dto))
+ };
+
+ public Name Create(IdentifierReference id)
+ {
+ var dto = id.ToValueDto();
+ return dto switch
+ {
+ { Type: ValueDtoType.Name, Name: not null } =>
+ new Name(dto.Name, CurrentFrame),
+ { Type: ValueDtoType.Env, Name: not null } =>
+ new EnvName(dto.Name, EnvFrame),
+ _ => throw new ArgumentOutOfRangeException(nameof(dto))
+ };
+ }
+
+ public Name Create(string id) => new(id, CurrentFrame);
+
+ private CurrentFrame CurrentFrame { get; } = new(frameContext);
+
+ private EnvFrame EnvFrame { get; } = new(provider);
+}
\ No newline at end of file
diff --git a/src/Application/HydraScript.Application.CodeGeneration/ServiceCollectionExtensions.cs b/src/Application/HydraScript.Application.CodeGeneration/ServiceCollectionExtensions.cs
index b79c4be5..bd3254d8 100644
--- a/src/Application/HydraScript.Application.CodeGeneration/ServiceCollectionExtensions.cs
+++ b/src/Application/HydraScript.Application.CodeGeneration/ServiceCollectionExtensions.cs
@@ -10,7 +10,7 @@ public static class ServiceCollectionExtensions
{
public static IServiceCollection AddCodeGeneration(this IServiceCollection services)
{
- services.AddSingleton();
+ services.AddSingleton();
services.AddKeyedSingleton<
IVisitor,
InstructionProvider>(CodeGeneratorType.General);
diff --git a/src/Application/HydraScript.Application.CodeGeneration/Visitors/ExpressionInstructionProvider.cs b/src/Application/HydraScript.Application.CodeGeneration/Visitors/ExpressionInstructionProvider.cs
index 82802b41..a34b5ea0 100644
--- a/src/Application/HydraScript.Application.CodeGeneration/Visitors/ExpressionInstructionProvider.cs
+++ b/src/Application/HydraScript.Application.CodeGeneration/Visitors/ExpressionInstructionProvider.cs
@@ -5,6 +5,7 @@
using HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment.ComplexData.Create;
using HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment.ComplexData.Read;
using HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment.ComplexData.Write;
+using HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment.ExplicitCast;
using HydraScript.Domain.BackEnd.Impl.Instructions.WithJump;
using HydraScript.Domain.BackEnd.Impl.Values;
using HydraScript.Domain.FrontEnd.Parser;
@@ -32,21 +33,21 @@ internal class ExpressionInstructionProvider : VisitorBase,
IVisitor
{
- private readonly IValueDtoConverter _valueDtoConverter;
+ private readonly IValueFactory _valueFactory;
- public ExpressionInstructionProvider(IValueDtoConverter valueDtoConverter) =>
- _valueDtoConverter = valueDtoConverter;
+ public ExpressionInstructionProvider(IValueFactory valueFactory) =>
+ _valueFactory = valueFactory;
public override AddressedInstructions Visit(IAbstractSyntaxTreeNode visitable) => [];
public AddressedInstructions Visit(PrimaryExpression visitable) =>
- [new Simple(_valueDtoConverter.Convert(visitable.ToValueDto()))];
+ [new Simple(_valueFactory.Create(visitable.ToValueDto()))];
public AddressedInstructions Visit(ArrayLiteral visitable)
{
var arraySize = visitable.Expressions.Count;
- var arrayName = visitable.Id;
+ var arrayName = _valueFactory.Create(visitable.Id);
var createArray = new CreateArray(arrayName, arraySize);
var result = new AddressedInstructions { createArray };
@@ -60,11 +61,11 @@ public AddressedInstructions Visit(ArrayLiteral visitable)
result.Add(new IndexAssignment(
arrayName,
index,
- _valueDtoConverter.Convert(primary.ToValueDto())));
+ _valueFactory.Create(primary.ToValueDto())));
else
{
result.AddRange(expression.Accept(This));
- var last = new Name(result.OfType().Last().Left!);
+ var last = result.OfType().Last().Left!;
result.Add(new IndexAssignment(arrayName, index, last));
}
}
@@ -74,7 +75,7 @@ public AddressedInstructions Visit(ArrayLiteral visitable)
public AddressedInstructions Visit(ObjectLiteral visitable)
{
- var objectId = visitable.Id;
+ var objectId = _valueFactory.Create(visitable.Id);
var createObject = new CreateObject(objectId);
var result = new AddressedInstructions { createObject };
@@ -89,7 +90,7 @@ public AddressedInstructions Visit(ObjectLiteral visitable)
public AddressedInstructions Visit(Property visitable)
{
- var objectId = visitable.Object.Id;
+ var objectId = _valueFactory.Create(visitable.Object.Id);
var (id, expression) = visitable;
var propertyId = new Constant(id);
@@ -98,10 +99,10 @@ public AddressedInstructions Visit(Property visitable)
return [new DotAssignment(
objectId,
propertyId,
- _valueDtoConverter.Convert(primary.ToValueDto()))];
+ _valueFactory.Create(primary.ToValueDto()))];
var instructions = expression.Accept(This);
- var last = new Name(instructions.OfType().Last().Left!);
+ var last = instructions.OfType().Last().Left!;
instructions.Add(new DotAssignment(objectId, propertyId, last));
return instructions;
@@ -110,10 +111,10 @@ public AddressedInstructions Visit(Property visitable)
public AddressedInstructions Visit(UnaryExpression visitable)
{
if (visitable.Expression is PrimaryExpression primary)
- return [new Simple(visitable.Operator, _valueDtoConverter.Convert(primary.ToValueDto()))];
+ return [new Simple(visitable.Operator, _valueFactory.Create(primary.ToValueDto()))];
var result = visitable.Expression.Accept(This);
- var last = new Name(result.OfType().Last().Left!);
+ var last = result.OfType().Last().Left!;
result.Add(new Simple(visitable.Operator, last));
return result;
@@ -122,25 +123,25 @@ public AddressedInstructions Visit(UnaryExpression visitable)
public AddressedInstructions Visit(BinaryExpression visitable)
{
if (visitable is { Left: IdentifierReference arr, Right: PrimaryExpression primary, Operator: "::" })
- return [new RemoveFromArray(arr.Name, index: _valueDtoConverter.Convert(primary.ToValueDto()))];
+ return [new RemoveFromArray(_valueFactory.Create(arr), index: _valueFactory.Create(primary.ToValueDto()))];
var result = new AddressedInstructions();
IValue left, right;
if (visitable.Left is PrimaryExpression primaryLeft)
- left = _valueDtoConverter.Convert(primaryLeft.ToValueDto());
+ left = _valueFactory.Create(primaryLeft.ToValueDto());
else
{
result.AddRange(visitable.Left.Accept(This));
- left = new Name(result.OfType().Last().Left!);
+ left = result.OfType().Last().Left!;
}
if (visitable.Right is PrimaryExpression primaryRight)
- right = _valueDtoConverter.Convert(primaryRight.ToValueDto());
+ right = _valueFactory.Create(primaryRight.ToValueDto());
else
{
result.AddRange(visitable.Right.Accept(This));
- right = new Name(result.OfType().Last().Left!);
+ right = result.OfType().Last().Left!;
}
result.Add(new Simple(left, visitable.Operator, right));
@@ -150,19 +151,28 @@ public AddressedInstructions Visit(BinaryExpression visitable)
public AddressedInstructions Visit(CastAsExpression visitable)
{
+ Func asFactory = visitable.ToType switch
+ {
+ CastAsExpression.DestinationType.Undefined => throw new NotSupportedException(),
+ CastAsExpression.DestinationType.String => value => new AsString(value),
+ CastAsExpression.DestinationType.Number => value => new AsNumber(value),
+ CastAsExpression.DestinationType.Boolean => value => new AsBool(value),
+ _ => throw new ArgumentOutOfRangeException(nameof(visitable.ToType))
+ };
+
if (visitable.Expression is PrimaryExpression primary)
- return [new AsString(_valueDtoConverter.Convert(primary.ToValueDto()))];
-
+ return [asFactory(_valueFactory.Create(primary.ToValueDto()))];
+
var result = visitable.Expression.Accept(This);
- var last = new Name(result.OfType().Last().Left!);
- result.Add(new AsString(last));
-
+ var last = result.OfType().Last().Left!;
+ result.Add(asFactory(last));
+
return result;
}
public AddressedInstructions Visit(WithExpression visitable)
{
- var objectId = visitable.ObjectLiteral.Id;
+ var objectId = _valueFactory.Create(visitable.ObjectLiteral.Id);
var createObject = new CreateObject(objectId);
var result = new AddressedInstructions { createObject };
@@ -187,15 +197,15 @@ public AddressedInstructions Visit(WithExpression visitable)
result.AddRange(visitable.Expression is PrimaryExpression ? [] : visitable.Expression.Accept(This));
var copyFrom = visitable.Expression is IdentifierReference objectIdent
- ? new Name(objectIdent)
- : new Name(result.OfType().Last().Left!);
+ ? _valueFactory.Create(objectIdent)
+ : result.OfType().Last().Left!;
for (var i = 0; i < visitable.ComputedCopiedProperties.Count; i++)
{
var property = new Constant(visitable.ComputedCopiedProperties[i]);
result.Add(new DotRead(copyFrom, property));
var read = result[result.End].Address.Name;
- result.Add(new DotAssignment(objectId, property, new Name(read)));
+ result.Add(new DotAssignment(objectId, property, _valueFactory.Create(read)));
}
return result;
@@ -210,11 +220,11 @@ public AddressedInstructions Visit(ConditionalExpression visitable)
var result = new AddressedInstructions();
if (visitable.Test is PrimaryExpression primary)
- result.Add(new IfNotGoto(test: _valueDtoConverter.Convert(primary.ToValueDto()), startBlockLabel));
+ result.Add(new IfNotGoto(test: _valueFactory.Create(primary.ToValueDto()), startBlockLabel));
else
{
result.AddRange(visitable.Test.Accept(This));
- var last = new Name(result.OfType().Last().Left!);
+ var last = result.OfType().Last().Left!;
result.Add(new IfNotGoto(last, startBlockLabel));
}
@@ -227,7 +237,7 @@ public AddressedInstructions Visit(ConditionalExpression visitable)
result.OfType().Last().Left = temp;
result.Add(new EndBlock(BlockType.Condition, blockId), endBlockLabel.Name);
- result.Add(new Simple(new Name(temp)));
+ result.Add(new Simple(temp));
return result;
}
@@ -241,14 +251,14 @@ public AddressedInstructions Visit(AssignmentExpression visitable)
if (last is IWriteToComplexData assignment)
result.Add(assignment.ToSimple());
else
- result.Add(new Simple(new Name(last.Left!)));
+ result.Add(new Simple(last.Left!));
}
if (visitable.Destination.Empty())
- result.OfType().Last().Left = visitable.Destination.Id;
+ result.OfType().Last().Left = _valueFactory.Create(visitable.Destination.Id);
else
{
- var last = new Name(result.OfType().Last().Left!);
+ var last = result.OfType().Last().Left!;
result.AddRange(visitable.Destination.Accept(This));
var lastRead = result.OfType().Last();
result.Replace(lastRead.ToInstruction(), lastRead.ToAssignment(last));
@@ -267,10 +277,10 @@ public AddressedInstructions Visit(DotAccess visitable)
var right = new Constant(visitable.Property.Name);
if (!visitable.HasPrev() && visitable.Parent is LeftHandSideExpression lhs)
- return [new DotRead(new Name(lhs.Id), right)];
+ return [new DotRead(_valueFactory.Create(lhs.Id), right)];
var result = visitable.Prev?.Accept(This) ?? [];
- var left = new Name(result.OfType().Last().Left!);
+ var left = result.OfType().Last().Left!;
result.Add(new DotRead(left, right));
return result;
@@ -283,19 +293,19 @@ public AddressedInstructions Visit(IndexAccess visitable)
IValue right;
if (visitable.Index is PrimaryExpression primary)
- right = _valueDtoConverter.Convert(primary.ToValueDto());
+ right = _valueFactory.Create(primary.ToValueDto());
else
{
result.AddRange(visitable.Index.Accept(This));
- right = new Name(result.OfType().Last().Left!);
+ right = result.OfType().Last().Left!;
}
if (!visitable.HasPrev() && visitable.Parent is LeftHandSideExpression lhs)
- result.Add(new IndexRead(new Name(lhs.Id), right));
+ result.Add(new IndexRead(_valueFactory.Create(lhs.Id), right));
else
{
result.AddRange(visitable.Prev?.Accept(This) ?? []);
- var left = new Name(result.OfType().Last().Left!);
+ var left = result.OfType().Last().Left!;
result.Add(new IndexRead(left, right));
}
@@ -320,20 +330,22 @@ public AddressedInstructions Visit(CallExpression visitable)
if (methodCall)
{
- var caller = result.Any() ? result.OfType().Last().Left! : visitable.Id;
- result.Add(new PushParameter(new Name(caller)));
+ var caller = result.Count > 0
+ ? result.OfType().Last().Left!
+ : _valueFactory.Create(visitable.Id);
+ result.Add(new PushParameter(caller));
}
for (var i = 0; i < visitable.Parameters.Count; i++)
{
var expr = visitable.Parameters[i];
if (expr is PrimaryExpression primary)
- result.Add(new PushParameter(_valueDtoConverter.Convert(primary.ToValueDto())));
+ result.Add(new PushParameter(_valueFactory.Create(primary.ToValueDto())));
else
{
result.AddRange(expr.Accept(This));
var id = result.OfType().Last().Left!;
- result.Add(new PushParameter(new Name(id)));
+ result.Add(new PushParameter(id));
}
}
diff --git a/src/Application/HydraScript.Application.CodeGeneration/Visitors/InstructionProvider.cs b/src/Application/HydraScript.Application.CodeGeneration/Visitors/InstructionProvider.cs
index 4fb64d7f..4eab8f98 100644
--- a/src/Application/HydraScript.Application.CodeGeneration/Visitors/InstructionProvider.cs
+++ b/src/Application/HydraScript.Application.CodeGeneration/Visitors/InstructionProvider.cs
@@ -2,8 +2,8 @@
using HydraScript.Domain.BackEnd.Impl.Addresses;
using HydraScript.Domain.BackEnd.Impl.Instructions;
using HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment;
+using HydraScript.Domain.BackEnd.Impl.Instructions.WithAssignment.ExplicitCast;
using HydraScript.Domain.BackEnd.Impl.Instructions.WithJump;
-using HydraScript.Domain.BackEnd.Impl.Values;
using HydraScript.Domain.Constants;
using HydraScript.Domain.FrontEnd.Parser;
using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes;
@@ -24,17 +24,18 @@ internal class InstructionProvider : VisitorBase,
IVisitor,
IVisitor,
- IVisitor
+ IVisitor,
+ IVisitor
{
- private readonly IValueDtoConverter _valueDtoConverter;
+ private readonly IValueFactory _valueFactory;
private readonly IVisitor _expressionVisitor;
public InstructionProvider(
- IValueDtoConverter valueDtoConverter,
+ IValueFactory valueFactory,
[FromKeyedServices(CodeGeneratorType.Expression)]
IVisitor expressionVisitor)
{
- _valueDtoConverter = valueDtoConverter;
+ _valueFactory = valueFactory;
_expressionVisitor = expressionVisitor;
}
@@ -103,11 +104,11 @@ public AddressedInstructions Visit(ReturnStatement visitable)
case null:
return [new Return()];
case PrimaryExpression primary:
- return [new Return(_valueDtoConverter.Convert(primary.ToValueDto()))];
+ return [new Return(_valueFactory.Create(primary.ToValueDto()))];
}
var result = visitable.Expression.Accept(_expressionVisitor);
- var last = new Name(result.OfType().Last().Left!);
+ var last = result.OfType().Last().Left!;
result.Add(new Return(last));
return result;
@@ -115,7 +116,7 @@ public AddressedInstructions Visit(ReturnStatement visitable)
public AddressedInstructions Visit(FunctionDeclaration visitable)
{
- if (!visitable.Statements.Any())
+ if (visitable.IsEmpty)
return [];
var functionInfo = new FunctionInfo(visitable.ComputedFunctionAddress);
@@ -132,10 +133,7 @@ public AddressedInstructions Visit(FunctionDeclaration visitable)
for (var i = 0; i < visitable.Arguments.Count; i++)
{
var arg = visitable.Arguments[i];
- if (arg is DefaultValueArgument @default)
- result.Add(new PopParameter(arg.Name, @default.Info.Value));
- else
- result.Add(new PopParameter(arg.Name));
+ result.Add(new PopParameter(_valueFactory.Create(arg.Name), arg.Info.Value));
}
result.AddRange(visitable.Statements.Accept(This));
@@ -159,11 +157,11 @@ public AddressedInstructions Visit(WhileStatement visitable)
};
if (visitable.Condition is PrimaryExpression primary)
- result.Add(new IfNotGoto(test: _valueDtoConverter.Convert(primary.ToValueDto()), endBlockLabel));
+ result.Add(new IfNotGoto(test: _valueFactory.Create(primary.ToValueDto()), endBlockLabel));
else
{
result.AddRange(visitable.Condition.Accept(_expressionVisitor));
- var last = new Name(result.OfType().Last().Left!);
+ var last = result.OfType().Last().Left!;
result.Add(new IfNotGoto(last, endBlockLabel));
}
@@ -193,7 +191,7 @@ public AddressedInstructions Visit(WhileStatement visitable)
public AddressedInstructions Visit(IfStatement visitable)
{
- if (visitable.Empty())
+ if (visitable.Empty)
return [];
var blockId = $"if_else_{visitable.GetHashCode()}";
@@ -203,11 +201,11 @@ public AddressedInstructions Visit(IfStatement visitable)
var result = new AddressedInstructions();
if (visitable.Test is PrimaryExpression primary)
- result.Add(new IfNotGoto(test: _valueDtoConverter.Convert(primary.ToValueDto()), startBlockLabel));
+ result.Add(new IfNotGoto(test: _valueFactory.Create(primary.ToValueDto()), startBlockLabel));
else
{
result.AddRange(visitable.Test.Accept(_expressionVisitor));
- var last = new Name(result.OfType().Last().Left!);
+ var last = result.OfType().Last().Left!;
result.Add(new IfNotGoto(last,
visitable.HasElseBlock()
? startBlockLabel
@@ -233,21 +231,32 @@ public AddressedInstructions Visit(IfStatement visitable)
return result;
}
- public AddressedInstructions Visit(PrintStatement visitable)
+ public AddressedInstructions Visit(OutputStatement visitable)
{
- AddressedInstructions result = [];
-
if (visitable.Expression is PrimaryExpression prim)
- result.Add(new AsString(_valueDtoConverter.Convert(prim.ToValueDto())));
- else
{
- result.AddRange(visitable.Expression.Accept(_expressionVisitor));
- var name = new Name(result.OfType().Last().Left!);
- result.Add(new AsString(name));
+ var valueDto = prim.ToValueDto();
+ var printedValue = _valueFactory.Create(valueDto);
+ IExecutableInstruction instruction = valueDto is { Type: ValueDtoType.Env } or { Type: ValueDtoType.Constant, Value: string }
+ ? new Output(printedValue)
+ : new AsString(printedValue);
+ AddressedInstructions shortResult = [instruction];
+ if (instruction is AsString asString)
+ shortResult.Add(new Output(asString.Left!));
+ return shortResult;
}
- result.Add(new Print(new Name((result[result.End] as AsString)!.Left!)));
+ AddressedInstructions result = [];
+
+ result.AddRange(visitable.Expression.Accept(_expressionVisitor));
+ var name = result.OfType().Last().Left!;
+ var nameAsString = new AsString(name);
+ result.Add(nameAsString);
+ result.Add(new Output(nameAsString.Left!));
return result;
}
+
+ public AddressedInstructions Visit(InputStatement visitable) =>
+ [new Input(_valueFactory.Create(visitable.Destination))];
}
\ No newline at end of file
diff --git a/src/Application/HydraScript.Application.StaticAnalysis/Exceptions/ExplicitCastNotSupported.cs b/src/Application/HydraScript.Application.StaticAnalysis/Exceptions/ExplicitCastNotSupported.cs
new file mode 100644
index 00000000..09ac2523
--- /dev/null
+++ b/src/Application/HydraScript.Application.StaticAnalysis/Exceptions/ExplicitCastNotSupported.cs
@@ -0,0 +1,8 @@
+using System.Diagnostics.CodeAnalysis;
+using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions;
+
+namespace HydraScript.Application.StaticAnalysis.Exceptions;
+
+[ExcludeFromCodeCoverage]
+public class ExplicitCastNotSupported(CastAsExpression cast, Type from, Type to) :
+ SemanticException(cast.Segment, $"Cast from {from} to {to} is not supported");
\ No newline at end of file
diff --git a/src/Application/HydraScript.Application.StaticAnalysis/IDefaultValueForTypeCalculator.cs b/src/Application/HydraScript.Application.StaticAnalysis/IDefaultValueForTypeCalculator.cs
deleted file mode 100644
index ba34c898..00000000
--- a/src/Application/HydraScript.Application.StaticAnalysis/IDefaultValueForTypeCalculator.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace HydraScript.Application.StaticAnalysis;
-
-public interface IDefaultValueForTypeCalculator
-{
- public object? GetDefaultValueForType(Type type);
-}
\ No newline at end of file
diff --git a/src/Application/HydraScript.Application.StaticAnalysis/IHydraScriptTypesService.cs b/src/Application/HydraScript.Application.StaticAnalysis/IHydraScriptTypesService.cs
new file mode 100644
index 00000000..cabbd2d3
--- /dev/null
+++ b/src/Application/HydraScript.Application.StaticAnalysis/IHydraScriptTypesService.cs
@@ -0,0 +1,22 @@
+namespace HydraScript.Application.StaticAnalysis;
+
+public interface IHydraScriptTypesService
+{
+ public Type Number { get; }
+
+ public Type Boolean { get; }
+
+ public Type String { get; }
+
+ public Type Undefined { get; }
+
+ public Type Void { get; }
+
+ public IEnumerable GetDefaultTypes();
+
+ public bool Contains(Type type);
+
+ public object? GetDefaultValueForType(Type type);
+
+ public bool IsExplicitCastAllowed(Type from, Type to);
+}
\ No newline at end of file
diff --git a/src/Application/HydraScript.Application.StaticAnalysis/IJavaScriptTypesProvider.cs b/src/Application/HydraScript.Application.StaticAnalysis/IJavaScriptTypesProvider.cs
deleted file mode 100644
index 42e79dac..00000000
--- a/src/Application/HydraScript.Application.StaticAnalysis/IJavaScriptTypesProvider.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace HydraScript.Application.StaticAnalysis;
-
-public interface IJavaScriptTypesProvider
-{
- public IEnumerable GetDefaultTypes();
-
- public bool Contains(Type type);
-}
\ No newline at end of file
diff --git a/src/Application/HydraScript.Application.StaticAnalysis/ITypeDeclarationsResolver.cs b/src/Application/HydraScript.Application.StaticAnalysis/ITypeDeclarationsResolver.cs
index d7ac8ff2..99553c85 100644
--- a/src/Application/HydraScript.Application.StaticAnalysis/ITypeDeclarationsResolver.cs
+++ b/src/Application/HydraScript.Application.StaticAnalysis/ITypeDeclarationsResolver.cs
@@ -7,4 +7,6 @@ public interface ITypeDeclarationsResolver
public void Store(TypeDeclaration declaration);
public void Resolve();
+
+ IHydraScriptTypesService TypesService { get; }
}
\ No newline at end of file
diff --git a/src/Application/HydraScript.Application.StaticAnalysis/Impl/DefaultValueForTypeCalculator.cs b/src/Application/HydraScript.Application.StaticAnalysis/Impl/DefaultValueForTypeCalculator.cs
deleted file mode 100644
index d42b560a..00000000
--- a/src/Application/HydraScript.Application.StaticAnalysis/Impl/DefaultValueForTypeCalculator.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using HydraScript.Domain.IR.Types;
-
-namespace HydraScript.Application.StaticAnalysis.Impl;
-
-internal class DefaultValueForTypeCalculator : IDefaultValueForTypeCalculator
-{
- private readonly Type _boolean = "boolean";
- private readonly Type _number = "number";
- private readonly Type _string = "string";
- private readonly Type _void = "void";
- private readonly Type _null = new NullType();
-
- public object? GetDefaultValueForType(Type type)
- {
- if (type is NullableType)
- return null;
- if (type.Equals(_boolean))
- return false;
- if (type.Equals(_number))
- return 0;
- if (type.Equals(_string))
- return string.Empty;
- if (type.Equals(_void))
- return new object();
- if (type.Equals(_null))
- return null;
- if (type is ArrayType)
- return new List();
-
- return new object();
- }
-}
\ No newline at end of file
diff --git a/src/Application/HydraScript.Application.StaticAnalysis/Impl/HydraScriptTypesService.cs b/src/Application/HydraScript.Application.StaticAnalysis/Impl/HydraScriptTypesService.cs
new file mode 100644
index 00000000..013ace91
--- /dev/null
+++ b/src/Application/HydraScript.Application.StaticAnalysis/Impl/HydraScriptTypesService.cs
@@ -0,0 +1,83 @@
+using HydraScript.Domain.IR.Types;
+
+namespace HydraScript.Application.StaticAnalysis.Impl;
+
+internal class HydraScriptTypesService : IHydraScriptTypesService
+{
+ private readonly HashSet _types;
+ private readonly Dictionary> _allowedConversions;
+
+ public HydraScriptTypesService()
+ {
+ _types =
+ [
+ Number,
+ Boolean,
+ String,
+ Null,
+ Undefined,
+ Void
+ ];
+ _allowedConversions = new()
+ {
+ [String] = [new Any()],
+ [Number] = [String, Boolean],
+ [Boolean] = [String, Number],
+ };
+ }
+
+ public Type Number => NumberType.Instance;
+
+ public Type Boolean => BooleanType.Instance;
+
+ public Type String => StringType.Instance;
+
+ private Type Null => NullType.Instance;
+
+ public Type Undefined => "undefined";
+
+ public Type Void => "void";
+
+ public IEnumerable GetDefaultTypes() => _types;
+
+ public bool Contains(Type type) => _types.Contains(type);
+
+ public object? GetDefaultValueForType(Type type)
+ {
+ if (type is NullableType)
+ return null;
+ if (type.Equals(Boolean))
+ return false;
+ if (type.Equals(Number))
+ return 0;
+ if (type.Equals(String))
+ return string.Empty;
+ if (type.Equals(Void))
+ return new object();
+ if (type.Equals(Null))
+ return null;
+ if (type is ArrayType)
+ return new List();
+
+ return new object();
+ }
+
+ public bool IsExplicitCastAllowed(Type from, Type to)
+ {
+ var typeEqualityComparer = default(CommutativeTypeEqualityComparer);
+
+ if (typeEqualityComparer.Equals(from, to))
+ return true;
+
+ if (!_allowedConversions.TryGetValue(to, out var allowedFrom))
+ return false;
+
+ for (var i = 0; i < allowedFrom.Count; i++)
+ {
+ if (typeEqualityComparer.Equals(allowedFrom[i], from))
+ return true;
+ }
+
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/Application/HydraScript.Application.StaticAnalysis/Impl/JavaScriptTypesProvider.cs b/src/Application/HydraScript.Application.StaticAnalysis/Impl/JavaScriptTypesProvider.cs
deleted file mode 100644
index 425a08a5..00000000
--- a/src/Application/HydraScript.Application.StaticAnalysis/Impl/JavaScriptTypesProvider.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using HydraScript.Domain.IR.Types;
-
-namespace HydraScript.Application.StaticAnalysis.Impl;
-
-internal class JavaScriptTypesProvider : IJavaScriptTypesProvider
-{
- private readonly HashSet _types =
- [
- "number",
- "boolean",
- "string",
- new NullType(),
- "undefined",
- "void"
- ];
-
- public IEnumerable GetDefaultTypes() => _types;
-
- public bool Contains(Type type) => _types.Contains(type);
-}
\ No newline at end of file
diff --git a/src/Application/HydraScript.Application.StaticAnalysis/Impl/StandardLibraryProvider.cs b/src/Application/HydraScript.Application.StaticAnalysis/Impl/StandardLibraryProvider.cs
index 19506663..20f174fd 100644
--- a/src/Application/HydraScript.Application.StaticAnalysis/Impl/StandardLibraryProvider.cs
+++ b/src/Application/HydraScript.Application.StaticAnalysis/Impl/StandardLibraryProvider.cs
@@ -4,18 +4,13 @@
namespace HydraScript.Application.StaticAnalysis.Impl;
-internal class StandardLibraryProvider : IStandardLibraryProvider
+internal class StandardLibraryProvider(IHydraScriptTypesService typesService) : IStandardLibraryProvider
{
- private readonly IJavaScriptTypesProvider _provider;
-
- public StandardLibraryProvider(IJavaScriptTypesProvider provider) =>
- _provider = provider;
-
public ISymbolTable GetStandardLibrary()
{
var library = new SymbolTable();
- foreach (var type in _provider.GetDefaultTypes())
+ foreach (var type in typesService.GetDefaultTypes())
library.AddSymbol(new TypeSymbol(type));
var symbolTable = new SymbolTable();
diff --git a/src/Application/HydraScript.Application.StaticAnalysis/Impl/TypeDeclarationsResolver.cs b/src/Application/HydraScript.Application.StaticAnalysis/Impl/TypeDeclarationsResolver.cs
index f75c8b42..6c2e1bd0 100644
--- a/src/Application/HydraScript.Application.StaticAnalysis/Impl/TypeDeclarationsResolver.cs
+++ b/src/Application/HydraScript.Application.StaticAnalysis/Impl/TypeDeclarationsResolver.cs
@@ -5,7 +5,7 @@
namespace HydraScript.Application.StaticAnalysis.Impl;
internal class TypeDeclarationsResolver(
- IJavaScriptTypesProvider provider,
+ IHydraScriptTypesService typesService,
ISymbolTableStorage symbolTables,
IVisitor typeBuilder) : ITypeDeclarationsResolver
{
@@ -16,7 +16,7 @@ public void Store(TypeDeclaration declaration) =>
public void Resolve()
{
- var defaults = provider.GetDefaultTypes()
+ var defaults = TypesService.GetDefaultTypes()
.AsValueEnumerable()
.Select(x => new TypeSymbol(x))
.ToList();
@@ -43,4 +43,6 @@ public void Resolve()
}
}
}
+
+ public IHydraScriptTypesService TypesService { get; } = typesService;
}
\ No newline at end of file
diff --git a/src/Application/HydraScript.Application.StaticAnalysis/ServiceCollectionExtensions.cs b/src/Application/HydraScript.Application.StaticAnalysis/ServiceCollectionExtensions.cs
index d7f9cf40..ae8dd7b3 100644
--- a/src/Application/HydraScript.Application.StaticAnalysis/ServiceCollectionExtensions.cs
+++ b/src/Application/HydraScript.Application.StaticAnalysis/ServiceCollectionExtensions.cs
@@ -19,8 +19,7 @@ public static IServiceCollection AddStaticAnalysis(this IServiceCollection servi
services.AddSingleton();
services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
+ services.AddSingleton();
services.AddSingleton();
diff --git a/src/Application/HydraScript.Application.StaticAnalysis/Visitors/DeclarationVisitor.cs b/src/Application/HydraScript.Application.StaticAnalysis/Visitors/DeclarationVisitor.cs
index be503f12..7bb0f789 100644
--- a/src/Application/HydraScript.Application.StaticAnalysis/Visitors/DeclarationVisitor.cs
+++ b/src/Application/HydraScript.Application.StaticAnalysis/Visitors/DeclarationVisitor.cs
@@ -7,7 +7,6 @@
using HydraScript.Domain.IR.Impl.Symbols;
using HydraScript.Domain.IR.Impl.Symbols.Ids;
using HydraScript.Domain.IR.Types;
-using ZLinq;
namespace HydraScript.Application.StaticAnalysis.Visitors;
@@ -15,6 +14,7 @@ internal class DeclarationVisitor : VisitorNoReturnBase
IVisitor,
IVisitor
{
+ private readonly IHydraScriptTypesService _typesService;
private readonly IFunctionWithUndefinedReturnStorage _functionStorage;
private readonly IMethodStorage _methodStorage;
private readonly ISymbolTableStorage _symbolTables;
@@ -23,6 +23,7 @@ internal class DeclarationVisitor : VisitorNoReturnBase
private readonly IVisitor _returnAnalyzer;
public DeclarationVisitor(
+ IHydraScriptTypesService typesService,
IFunctionWithUndefinedReturnStorage functionStorage,
IMethodStorage methodStorage,
ISymbolTableStorage symbolTables,
@@ -30,6 +31,7 @@ public DeclarationVisitor(
IVisitor typeBuilder,
IVisitor returnAnalyzer)
{
+ _typesService = typesService;
_functionStorage = functionStorage;
_methodStorage = methodStorage;
_symbolTables = symbolTables;
@@ -55,9 +57,9 @@ public VisitUnit Visit(LexicalDeclaration visitable)
throw new DeclarationAlreadyExists(assignment.Destination.Id);
var destinationType = assignment.DestinationType?.Accept(
- _typeBuilder) ?? "undefined";
+ _typeBuilder) ?? _typesService.Undefined;
- if (destinationType == "undefined" &&
+ if (destinationType == _typesService.Undefined &&
assignment.Source is ImplicitLiteral or ArrayLiteral { Expressions.Count: 0 })
throw visitable.ReadOnly
? new ConstWithoutInitializer(assignment.Destination.Id)
@@ -79,16 +81,16 @@ public VisitUnit Visit(FunctionDeclaration visitable)
visitable.AllCodePathsEndedWithReturn = returnAnalyzerResult.CodePathEndedWithReturn;
var parentTable = _symbolTables[visitable.Parent.Scope];
- var indexOfFirstDefaultArgument = visitable.Arguments.AsValueEnumerable()
- .Select((x, i) => new { Argument = x, Index = i })
- .FirstOrDefault(pair => pair.Argument is DefaultValueArgument)?.Index ?? -1;
- var parameters = visitable.Arguments.AsValueEnumerable()
- .Select(x =>
- new VariableSymbol(
- x.Name,
- x.TypeValue.Accept(_typeBuilder))).ToList();
- var functionSymbolId = new FunctionSymbolId(visitable.Name, parameters.Select(x => x.Type));
+ var parameters = new List();
+ for (var i = 0; i < visitable.Arguments.Count; i++)
+ {
+ parameters.Add(visitable.Arguments[i].TypeValue.Accept(_typeBuilder));
+ var arg = new VariableSymbol(visitable.Arguments[i].Name, parameters[i]);
+ arg.Initialize();
+ _symbolTables[visitable.Scope].AddSymbol(arg);
+ }
+ var functionSymbolId = new FunctionSymbolId(visitable.Name, parameters);
_ambiguousInvocations.Clear(functionSymbolId);
visitable.ComputedFunctionAddress = functionSymbolId.ToString();
var functionSymbol = new FunctionSymbol(
@@ -99,46 +101,34 @@ public VisitUnit Visit(FunctionDeclaration visitable)
if (functionSymbolId.Equals(parentTable.FindSymbol(functionSymbolId)?.Id))
throw new OverloadAlreadyExists(visitable.Name, functionSymbolId);
- for (var i = 0; i < parameters.Count; i++)
- {
- var arg = parameters[i];
- arg.Initialize();
- _symbolTables[visitable.Scope].AddSymbol(arg);
- }
-
- var isMethod =
- parameters is [{ Type: ObjectType }, ..] &&
- visitable.Arguments is [{ TypeValue: TypeIdentValue }, ..];
- if (isMethod)
- _methodStorage.BindMethod((parameters[0].Type as ObjectType)!, functionSymbol, functionSymbolId);
+ if (parameters is [ObjectType methodOwner, ..] && visitable.Arguments is [{ TypeValue: TypeIdentValue }, ..])
+ _methodStorage.BindMethod(methodOwner, functionSymbol, functionSymbolId);
- Type undefined = "undefined";
- if (functionSymbol.Type.Equals(undefined))
+ if (functionSymbol.Type.Equals(_typesService.Undefined))
{
if (visitable.HasReturnStatement)
_functionStorage.Save(functionSymbol, visitable);
else
- functionSymbol.DefineReturnType("void");
+ functionSymbol.DefineReturnType(_typesService.Void);
}
parentTable.AddSymbol(functionSymbol);
- for (var i = indexOfFirstDefaultArgument; i < visitable.Arguments.Count; i++)
+ for (var i = visitable.IndexOfFirstDefaultArgument; i < visitable.Arguments.Count; i++)
{
- if (i is -1) break;
- if (visitable.Arguments[i] is not DefaultValueArgument)
+ if (visitable.Arguments[i].Info.Type is ValueDtoType.Name)
throw new NamedArgumentAfterDefaultValueArgument(
visitable.Segment,
function: visitable.Name,
visitable.Arguments[i]);
- var overload = new FunctionSymbolId(visitable.Name, parameters[..i].Select(x => x.Type));
+ var overload = new FunctionSymbolId(visitable.Name, parameters[..i]);
var existing = parentTable.FindSymbol(overload);
var functionToAdd = existing is not null && existing < functionSymbol
? existing
: functionSymbol;
parentTable.AddSymbol(functionToAdd, overload);
- if (isMethod)
- _methodStorage.BindMethod((parameters[0].Type as ObjectType)!, functionToAdd, overload);
+ if (parameters is [ObjectType overloadOwner, ..] && visitable.Arguments is [{ TypeValue: TypeIdentValue }, ..])
+ _methodStorage.BindMethod(overloadOwner, functionToAdd, overload);
if (existing is not null && !existing.Id.Equals(overload))
{
diff --git a/src/Application/HydraScript.Application.StaticAnalysis/Visitors/ReturnAnalyzer.cs b/src/Application/HydraScript.Application.StaticAnalysis/Visitors/ReturnAnalyzer.cs
index 30b4f91c..94f8fa7b 100644
--- a/src/Application/HydraScript.Application.StaticAnalysis/Visitors/ReturnAnalyzer.cs
+++ b/src/Application/HydraScript.Application.StaticAnalysis/Visitors/ReturnAnalyzer.cs
@@ -4,42 +4,53 @@
namespace HydraScript.Application.StaticAnalysis.Visitors;
-internal class ReturnAnalyzer : VisitorBase,
+internal class ReturnAnalyzer : VisitorBase,
IVisitor,
- IVisitor,
- IVisitor
+ IVisitor,
+ IVisitor
{
+ private readonly List _returnStatements = [];
+
public ReturnAnalyzerResult Visit(FunctionDeclaration visitable)
{
IAbstractSyntaxTreeNode astNode = visitable;
- return Visit(astNode);
+ var codePathEndedWithReturn= Visit(astNode);
+ var returnStatements = new List(_returnStatements);
+ ReturnAnalyzerResult result = new(codePathEndedWithReturn, returnStatements);
+ _returnStatements.Clear();
+ return result;
}
- public override ReturnAnalyzerResult Visit(IAbstractSyntaxTreeNode visitable)
+ public override bool Visit(IAbstractSyntaxTreeNode visitable)
{
- var result = ReturnAnalyzerResult.AdditiveIdentity;
for (var i = 0; i < visitable.Count; i++)
{
var visitableResult = visitable[i].Accept(This);
- if (visitableResult.CodePathEndedWithReturn)
- return visitableResult * result;
- result += visitableResult;
+ if (visitableResult)
+ return true;
}
- return result;
+ return false;
}
- public ReturnAnalyzerResult Visit(IfStatement visitable)
+ public bool Visit(IfStatement visitable)
{
var thenReturns = visitable.Then.Accept(This);
if (visitable.Else is null)
- return thenReturns + ReturnAnalyzerResult.AdditiveIdentity;
+ return false;
var elseReturns = visitable.Else.Accept(This);
- return thenReturns + elseReturns;
+ return thenReturns && elseReturns;
+ }
+
+ public bool Visit(ReturnStatement visitable)
+ {
+ _returnStatements.Add(visitable);
+ return true;
}
+}
- public ReturnAnalyzerResult Visit(ReturnStatement visitable) =>
- new(CodePathEndedWithReturn: true, ReturnStatements: [visitable]);
-}
\ No newline at end of file
+public sealed record ReturnAnalyzerResult(
+ bool CodePathEndedWithReturn,
+ IReadOnlyList ReturnStatements);
\ No newline at end of file
diff --git a/src/Application/HydraScript.Application.StaticAnalysis/Visitors/ReturnAnalyzerResult.cs b/src/Application/HydraScript.Application.StaticAnalysis/Visitors/ReturnAnalyzerResult.cs
deleted file mode 100644
index f9c2fb43..00000000
--- a/src/Application/HydraScript.Application.StaticAnalysis/Visitors/ReturnAnalyzerResult.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System.Numerics;
-using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Statements;
-
-namespace HydraScript.Application.StaticAnalysis.Visitors;
-
-public sealed record ReturnAnalyzerResult(bool CodePathEndedWithReturn, IReadOnlyList ReturnStatements) :
- IAdditiveIdentity,
- IAdditionOperators,
- IMultiplyOperators
-{
- public static ReturnAnalyzerResult operator +(ReturnAnalyzerResult left, ReturnAnalyzerResult right) =>
- new(
- left.CodePathEndedWithReturn && right.CodePathEndedWithReturn,
- ReturnStatements: [..left.ReturnStatements, ..right.ReturnStatements]);
-
- public static ReturnAnalyzerResult AdditiveIdentity { get; } = new(CodePathEndedWithReturn: false, ReturnStatements: []);
-
- public static ReturnAnalyzerResult operator *(ReturnAnalyzerResult left, ReturnAnalyzerResult right) =>
- new(
- left.CodePathEndedWithReturn || right.CodePathEndedWithReturn,
- ReturnStatements: [..left.ReturnStatements, ..right.ReturnStatements]);
-}
\ No newline at end of file
diff --git a/src/Application/HydraScript.Application.StaticAnalysis/Visitors/SemanticChecker.cs b/src/Application/HydraScript.Application.StaticAnalysis/Visitors/SemanticChecker.cs
index a0990dcf..fa217df2 100644
--- a/src/Application/HydraScript.Application.StaticAnalysis/Visitors/SemanticChecker.cs
+++ b/src/Application/HydraScript.Application.StaticAnalysis/Visitors/SemanticChecker.cs
@@ -24,6 +24,7 @@ internal class SemanticChecker : VisitorBase,
IVisitor,
IVisitor,
IVisitor,
+ IVisitor,
IVisitor,
IVisitor,
IVisitor,
@@ -41,9 +42,10 @@ internal class SemanticChecker : VisitorBase,
IVisitor,
IVisitor,
IVisitor,
- IVisitor
+ IVisitor,
+ IVisitor
{
- private readonly IDefaultValueForTypeCalculator _calculator;
+ private readonly IHydraScriptTypesService _typesService;
private readonly IFunctionWithUndefinedReturnStorage _functionStorage;
private readonly IMethodStorage _methodStorage;
private readonly ISymbolTableStorage _symbolTables;
@@ -52,7 +54,7 @@ internal class SemanticChecker : VisitorBase,
private readonly IVisitor _typeBuilder;
public SemanticChecker(
- IDefaultValueForTypeCalculator calculator,
+ IHydraScriptTypesService typesService,
IFunctionWithUndefinedReturnStorage functionStorage,
IMethodStorage methodStorage,
ISymbolTableStorage symbolTables,
@@ -60,7 +62,7 @@ public SemanticChecker(
IAmbiguousInvocationStorage ambiguousInvocations,
IVisitor typeBuilder)
{
- _calculator = calculator;
+ _typesService = typesService;
_functionStorage = functionStorage;
_methodStorage = methodStorage;
_symbolTables = symbolTables;
@@ -69,7 +71,7 @@ public SemanticChecker(
_typeBuilder = typeBuilder;
}
- public override Type Visit(IAbstractSyntaxTreeNode visitable) => "undefined";
+ public override Type Visit(IAbstractSyntaxTreeNode visitable) => _typesService.Undefined;
public Type Visit(ScriptBody visitable)
{
@@ -83,33 +85,31 @@ public Type Visit(ScriptBody visitable)
_symbolTables.Clear();
_computedTypes.Clear();
_ambiguousInvocations.Clear();
-
- return "undefined";
+
+ return _typesService.Undefined;
}
public Type Visit(WhileStatement visitable)
{
var condType = visitable.Condition.Accept(This);
- Type boolean = "boolean";
- if (!condType.Equals(boolean))
+ if (!condType.Equals(_typesService.Boolean))
throw new NotBooleanTestExpression(visitable.Segment, condType);
visitable.Statement.Accept(This);
- return "undefined";
+ return _typesService.Undefined;
}
public Type Visit(IfStatement visitable)
{
var testType = visitable.Test.Accept(This);
- Type boolean = "boolean";
- if (!testType.Equals(boolean))
+ if (!testType.Equals(_typesService.Boolean))
throw new NotBooleanTestExpression(visitable.Segment, testType);
visitable.Then.Accept(This);
visitable.Else?.Accept(This);
- return "undefined";
+ return _typesService.Undefined;
}
public Type Visit(InsideStatementJump visitable)
@@ -132,7 +132,7 @@ public Type Visit(InsideStatementJump visitable)
break;
}
- return "undefined";
+ return _typesService.Undefined;
}
public Type Visit(ReturnStatement visitable)
@@ -140,7 +140,7 @@ public Type Visit(ReturnStatement visitable)
if (!visitable.ChildOf())
throw new ReturnOutsideFunction(visitable.Segment);
- return visitable.Expression?.Accept(This) ?? "void";
+ return visitable.Expression?.Accept(This) ?? _typesService.Void;
}
public Type Visit(ExpressionStatement visitable) =>
@@ -154,13 +154,20 @@ public Type Visit(IdentifierReference visitable)
return symbol?.Type ?? throw new UnknownIdentifierReference(visitable);
}
+ public Type Visit(EnvVarReference visitable) => _typesService.String;
+
public Type Visit(Literal visitable) =>
visitable.Type.Accept(_typeBuilder);
public Type Visit(ImplicitLiteral visitable)
{
var type = visitable.Type.Accept(_typeBuilder);
- visitable.ComputedDefaultValue = _calculator.GetDefaultValueForType(type);
+ if (!visitable.IsDefined)
+ {
+ var definedValue = _typesService.GetDefaultValueForType(type);
+ visitable.SetValue(definedValue);
+ }
+
return type;
}
@@ -207,8 +214,7 @@ public ObjectType Visit(ObjectLiteral visitable)
public Type Visit(ConditionalExpression visitable)
{
var tType = visitable.Test.Accept(This);
- Type boolean = "boolean";
- if (!tType.Equals(boolean))
+ if (!tType.Equals(_typesService.Boolean))
throw new NotBooleanTestExpression(visitable.Test.Segment, tType);
var cType = visitable.Consequent.Accept(This);
@@ -229,82 +235,61 @@ public Type Visit(BinaryExpression visitable)
var lType = visitable.Left.Accept(This);
var rType = visitable.Right.Accept(This);
- if (visitable.Operator != "::" && !lType.Equals(rType))
+ if (!lType.TryGetOperator(visitable.Operator, out var @operator))
+ throw new UnsupportedOperation(visitable.Segment, lType, visitable.Operator);
+
+ var operation = new OperationDescriptor(visitable.Operator, OperandTypes: [lType, rType]);
+ if (!@operator.TryGetResultType(operation, out var resultType))
throw new IncompatibleTypesOfOperands(
visitable.Segment,
left: lType,
right: rType);
- Type number = "number";
- Type @string = "string";
- Type boolean = "boolean";
+ if (resultType.Equals(_typesService.Undefined))
+ throw new CannotDefineType(visitable.Segment);
- return visitable.Operator switch
- {
- "+" when lType.Equals(number) => number,
- "+" when lType.Equals(@string) => @string,
- "+" => throw new UnsupportedOperation(visitable.Segment, lType, visitable.Operator),
- "-" or "*" or "/" or "%" => lType.Equals(number)
- ? number
- : throw new UnsupportedOperation(visitable.Segment, lType, visitable.Operator),
- "||" or "&&" => lType.Equals(boolean)
- ? boolean
- : throw new UnsupportedOperation(visitable.Segment, lType, visitable.Operator),
- "==" or "!=" => boolean,
- ">" or ">=" or "<" or "<=" => lType.Equals(number)
- ? boolean
- : throw new UnsupportedOperation(visitable.Segment, lType, visitable.Operator),
- "++" when lType is ArrayType { Type: Any } && rType is ArrayType { Type: Any } =>
- throw new CannotDefineType(visitable.Segment),
- "++" => lType is ArrayType lArrType && rType is ArrayType rArrType
- ? lArrType.Type is not Any ? lArrType : rArrType.Type is not Any ? rArrType : throw new CannotDefineType(visitable.Segment)
- : throw new UnsupportedOperation(visitable.Segment, lType, visitable.Operator),
- "::" when lType is not ArrayType =>
- throw new UnsupportedOperation(visitable.Segment, lType, visitable.Operator),
- "::" => rType.Equals(number) ? "void" : throw new ArrayAccessException(visitable.Segment, rType),
- _ => "undefined"
- };
+ return resultType;
}
public Type Visit(UnaryExpression visitable)
{
var eType = visitable.Expression.Accept(This);
- Type number = "number";
- Type boolean = "boolean";
+ if (!eType.TryGetOperator(visitable.Operator, out var @operator))
+ throw new UnsupportedOperation(visitable.Segment, eType, visitable.Operator);
- return visitable.Operator switch
- {
- "-" when eType.Equals(number) => number,
- "!" when eType.Equals(boolean) => boolean,
- "~" when eType is ArrayType => number,
- _ => throw new UnsupportedOperation(visitable.Segment, eType, visitable.Operator)
- };
+ var operation = new OperationDescriptor(visitable.Operator, OperandTypes: [eType]);
+ if (!@operator.TryGetResultType(operation, out var resultType))
+ throw new UnsupportedOperation(visitable.Segment, eType, visitable.Operator);
+
+ if (resultType.Equals(_typesService.Undefined))
+ throw new CannotDefineType(visitable.Segment);
+
+ return resultType;
}
public Type Visit(LexicalDeclaration visitable)
{
- Type undefined = "undefined", @void = "void";
-
for (var i = 0; i < visitable.Assignments.Count; i++)
{
var assignment = visitable.Assignments[i];
var registeredSymbol = _symbolTables[visitable.Scope].FindSymbol(new VariableSymbolId(assignment.Destination.Id))!;
var sourceType = assignment.Source.Accept(This);
- if (sourceType.Equals(undefined))
+ if (sourceType.Equals(_typesService.Undefined))
throw new CannotDefineType(assignment.Source.Segment);
- if (sourceType.Equals(@void))
+ if (sourceType.Equals(_typesService.Void))
throw new CannotAssignVoid(assignment.Source.Segment);
- if (!registeredSymbol.Type.Equals(undefined) && !registeredSymbol.Type.Equals(sourceType))
+ if (!registeredSymbol.Type.Equals(_typesService.Undefined) &&
+ !default(CommutativeTypeEqualityComparer).Equals(registeredSymbol.Type, sourceType))
throw new IncompatibleTypesOfOperands(
assignment.Segment,
left: registeredSymbol.Type,
right: sourceType);
- if (sourceType is NullType && registeredSymbol.Type.Equals(undefined))
+ if (sourceType is NullType && registeredSymbol.Type.Equals(_typesService.Undefined))
throw new CannotAssignNullWhenUndefined(assignment.Segment);
- var actualType = registeredSymbol.Type.Equals(undefined)
+ var actualType = registeredSymbol.Type.Equals(_typesService.Undefined)
? sourceType
: registeredSymbol.Type;
var actualSymbol = actualType switch
@@ -316,11 +301,13 @@ public Type Visit(LexicalDeclaration visitable)
_symbolTables[visitable.Scope].AddSymbol(actualSymbol);
}
- return undefined;
+ return _typesService.Undefined;
}
public Type Visit(AssignmentExpression visitable)
{
+ var typeComparer = default(CommutativeTypeEqualityComparer);
+
if (visitable.Destination is CallExpression)
throw new WrongAssignmentTarget(visitable.Destination);
@@ -328,7 +315,7 @@ public Type Visit(AssignmentExpression visitable)
if (!visitable.Destination.Empty())
{
var destinationType = visitable.Destination.Accept(This);
- if (!destinationType.Equals(sourceType))
+ if (!typeComparer.Equals(destinationType, sourceType))
throw new IncompatibleTypesOfOperands(
visitable.Segment,
left: destinationType,
@@ -336,14 +323,16 @@ public Type Visit(AssignmentExpression visitable)
return destinationType;
}
- var symbol =
- _symbolTables[visitable.Scope].FindSymbol(new VariableSymbolId(visitable.Destination.Id)) ??
- throw new UnknownIdentifierReference(visitable.Destination.Id);
+ // здесь может быть переменная программы, а может быть переменная среды
+ var symbol = visitable.Destination.Id.ToValueDto().Type is ValueDtoType.Name
+ ? _symbolTables[visitable.Scope].FindSymbol(new VariableSymbolId(visitable.Destination.Id)) ??
+ throw new UnknownIdentifierReference(visitable.Destination.Id)
+ : new VariableSymbol(visitable.Destination.Id, _typesService.String);
if (symbol.ReadOnly)
throw new AssignmentToConst(visitable.Destination.Id);
- if (!sourceType.Equals(symbol.Type))
+ if (!typeComparer.Equals(sourceType, symbol.Type))
throw new IncompatibleTypesOfOperands(
visitable.Segment,
left: symbol.Type,
@@ -354,9 +343,10 @@ public Type Visit(AssignmentExpression visitable)
public Type Visit(MemberExpression visitable)
{
- var idType = visitable.Id.Accept(This);
+ IAbstractSyntaxTreeNode id = visitable.Id;
+ var idType = id.Accept(This);
visitable.ComputedIdTypeGuid = _computedTypes.Save(idType);
- return visitable.Empty() ? idType : visitable.AccessChain?.Accept(This) ?? "undefined";
+ return visitable.Empty() ? idType : visitable.AccessChain?.Accept(This) ?? _typesService.Undefined;
}
public Type Visit(IndexAccess visitable)
@@ -366,17 +356,16 @@ public Type Visit(IndexAccess visitable)
?? (visitable.Parent as MemberExpression)!.ComputedIdTypeGuid;
var prevType = _computedTypes.Get(prevTypeGuid);
- if (prevType is not ArrayType arrayType)
+ if (!prevType.TryGetOperator("[]", out var indexOperator))
throw new NonAccessibleType(prevType);
- Type number = "number";
var indexType = visitable.Index.Accept(This);
- if (!indexType.Equals(number))
+ var indexAccessDescriptor = new OperationDescriptor("[]", [prevType, indexType]);
+ if (!indexOperator.TryGetResultType(indexAccessDescriptor, out var elemType))
throw new ArrayAccessException(visitable.Segment, indexType);
- var elemType = arrayType.Type;
visitable.ComputedTypeGuid = _computedTypes.Save(elemType);
- return visitable.HasNext() ? visitable.Next?.Accept(This) ?? "undefined" : elemType;
+ return visitable.HasNext() ? visitable.Next?.Accept(This) ?? _typesService.Undefined : elemType;
}
public Type Visit(DotAccess visitable)
@@ -396,7 +385,7 @@ public Type Visit(DotAccess visitable)
? objectType
: throw new ObjectAccessException(visitable.Segment, objectType, visitable.Property);
visitable.ComputedTypeGuid = _computedTypes.Save(fieldType);
- return visitable.HasNext() ? visitable.Next?.Accept(This) ?? "undefined" : fieldType;
+ return visitable.HasNext() ? visitable.Next?.Accept(This) ?? _typesService.Undefined : fieldType;
}
public ObjectType Visit(WithExpression visitable)
@@ -422,15 +411,23 @@ public ObjectType Visit(WithExpression visitable)
public Type Visit(CastAsExpression visitable)
{
- Type undefined = "undefined";
- var exprType = visitable.Expression.Accept(This);
+ var from = visitable.Expression.Accept(This);
- if (exprType.Equals(undefined))
+ if (from.Equals(_typesService.Undefined))
throw new CannotDefineType(visitable.Expression.Segment);
- return visitable.Cast.Accept(_typeBuilder) == "string"
- ? "string"
- : throw new NotSupportedException("Other types but 'string' have not been supported for casting yet");
+ var to = visitable.Cast.Accept(_typeBuilder);
+ visitable.ToType = to switch
+ {
+ _ when to.Equals(_typesService.String) => CastAsExpression.DestinationType.String,
+ _ when to.Equals(_typesService.Number) => CastAsExpression.DestinationType.Number,
+ _ when to.Equals(_typesService.Boolean) => CastAsExpression.DestinationType.Boolean,
+ _ => CastAsExpression.DestinationType.Undefined
+ };
+
+ return _typesService.IsExplicitCastAllowed(from, to)
+ ? to
+ : throw new ExplicitCastNotSupported(visitable, from, to);
}
public Type Visit(CallExpression visitable)
@@ -466,51 +463,47 @@ public Type Visit(CallExpression visitable)
.Zip(parameters).Zip(functionSymbol.Parameters.AsValueEnumerable().Skip(methodCall ? 1 : 0))
.ToList().ForEach(pair =>
{
- var ((expr, actualType), expected) = pair;
- if (!actualType.Equals(expected.Type))
- throw new WrongTypeOfArgument(expr.Segment, expected.Type, actualType);
+ var ((expr, actualType), expectedType) = pair;
+ if (!actualType.Equals(expectedType))
+ throw new WrongTypeOfArgument(expr.Segment, expectedType, actualType);
});
- Type undefined = "undefined";
- if (functionSymbol.Type.Equals(undefined))
+ if (functionSymbol.Type.Equals(_typesService.Undefined))
{
var declaration = _functionStorage.Get(functionSymbol);
functionReturnType = declaration.Accept(This);
}
- Type @void = "void";
- if (!functionReturnType.Equals(@void))
+ if (!functionReturnType.Equals(_typesService.Void))
visitable.HasReturnValue = true;
return functionReturnType;
}
public Type Visit(FunctionDeclaration visitable)
{
- var parameters = visitable.Arguments.Select(x => x.TypeValue.Accept(_typeBuilder));
+ var parameters = visitable.Arguments.Select(x => x.TypeValue.Accept(_typeBuilder)).ToList();
var symbol = _symbolTables[visitable.Scope].FindSymbol(new FunctionSymbolId(visitable.Name, parameters))!;
_functionStorage.RemoveIfPresent(symbol);
visitable.Statements.Accept(This);
- Type undefined = "undefined";
HashSet returnTypes = [];
for (var i = 0; i < visitable.ReturnStatements.Count; i++)
{
var returnStatementType = visitable.ReturnStatements[i].Accept(This);
returnTypes.Add(returnStatementType);
- if (returnTypes.Count > 1 && symbol.Type.Equals(undefined))
+ if (returnTypes.Count > 1 && symbol.Type.Equals(_typesService.Undefined))
throw new CannotDefineType(visitable.Segment);
- if (!symbol.Type.Equals(undefined) && !symbol.Type.Equals(returnStatementType))
+ if (!symbol.Type.Equals(_typesService.Undefined) && !symbol.Type.Equals(returnStatementType))
throw new WrongReturnType(
visitable.ReturnStatements[i].Segment,
expected: symbol.Type,
actual: returnStatementType);
}
- if (symbol.Type.Equals(undefined))
+ if (symbol.Type.Equals(_typesService.Undefined))
symbol.DefineReturnType(returnTypes.Single());
- Type @void = "void";
- if (!symbol.Type.Equals(@void) && !visitable.AllCodePathsEndedWithReturn)
+ if (!symbol.Type.Equals(_typesService.Void) && !visitable.AllCodePathsEndedWithReturn)
throw new FunctionWithoutReturnStatement(visitable.Segment);
if (symbol.Type is NullType)
@@ -523,12 +516,21 @@ public Type Visit(BlockStatement visitable)
{
for (var i = 0; i < visitable.Count; i++)
visitable[i].Accept(This);
- return "undefined";
+ return _typesService.Undefined;
}
- public Type Visit(PrintStatement visitable)
+ public Type Visit(OutputStatement visitable)
{
visitable.Expression.Accept(This);
- return "undefined";
+ return _typesService.Undefined;
+ }
+
+ public Type Visit(InputStatement visitable)
+ {
+ IAbstractSyntaxTreeNode id = visitable.Destination;
+ var idType = id.Accept(This);
+ if (!idType.Equals(_typesService.String))
+ throw new UnsupportedOperation(visitable.Segment, idType, "<<<");
+ return _typesService.Undefined;
}
}
\ No newline at end of file
diff --git a/src/Application/HydraScript.Application.StaticAnalysis/Visitors/TypeSystemLoader.cs b/src/Application/HydraScript.Application.StaticAnalysis/Visitors/TypeSystemLoader.cs
index 7f1e10ce..b6c77fe9 100644
--- a/src/Application/HydraScript.Application.StaticAnalysis/Visitors/TypeSystemLoader.cs
+++ b/src/Application/HydraScript.Application.StaticAnalysis/Visitors/TypeSystemLoader.cs
@@ -12,16 +12,13 @@ internal class TypeSystemLoader : VisitorNoReturnBase,
IVisitor
{
private readonly ITypeDeclarationsResolver _resolver;
- private readonly IJavaScriptTypesProvider _provider;
private readonly ISymbolTableStorage _symbolTables;
public TypeSystemLoader(
ITypeDeclarationsResolver resolver,
- IJavaScriptTypesProvider provider,
ISymbolTableStorage symbolTables)
{
_resolver = resolver;
- _provider = provider;
_symbolTables = symbolTables;
}
@@ -45,7 +42,7 @@ public VisitUnit Visit(TypeDeclaration visitable)
{
var symbolTable = _symbolTables[visitable.Scope];
if (symbolTable.ContainsSymbol(new TypeSymbolId(visitable.TypeId)) ||
- _provider.Contains(visitable.TypeId.Name))
+ _resolver.TypesService.Contains(visitable.TypeId.Name))
throw new DeclarationAlreadyExists(visitable.TypeId);
symbolTable.AddSymbol(
diff --git a/src/Domain/HydraScript.Domain.BackEnd/AddressedInstructions.cs b/src/Domain/HydraScript.Domain.BackEnd/AddressedInstructions.cs
index 95620e6a..2a974ddd 100644
--- a/src/Domain/HydraScript.Domain.BackEnd/AddressedInstructions.cs
+++ b/src/Domain/HydraScript.Domain.BackEnd/AddressedInstructions.cs
@@ -4,7 +4,7 @@
namespace HydraScript.Domain.BackEnd;
-public class AddressedInstructions : IEnumerable
+public class AddressedInstructions : IReadOnlyCollection
{
private readonly LinkedList _addresses = new();
private readonly Dictionary> _addressToNode = new();
@@ -85,6 +85,8 @@ public IEnumerator GetEnumerator() =>
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ public int Count => _addresses.Count;
+
public override string ToString() =>
ZString.Join('\n', this);
}
\ No newline at end of file
diff --git a/src/Domain/HydraScript.Domain.BackEnd/Call.cs b/src/Domain/HydraScript.Domain.BackEnd/Call.cs
index c3cc4f61..7dd16697 100644
--- a/src/Domain/HydraScript.Domain.BackEnd/Call.cs
+++ b/src/Domain/HydraScript.Domain.BackEnd/Call.cs
@@ -1,11 +1,12 @@
using HydraScript.Domain.BackEnd.Impl.Addresses;
+using HydraScript.Domain.BackEnd.Impl.Values;
namespace HydraScript.Domain.BackEnd;
public record Call(
IAddress From,
FunctionInfo To,
- string? Where = null)
+ Name? Where = null)
{
public override string ToString() =>
$"{From}: {Where} => {To.Start}: {To.Id}";
diff --git a/src/Domain/HydraScript.Domain.BackEnd/HydraScript.Domain.BackEnd.csproj b/src/Domain/HydraScript.Domain.BackEnd/HydraScript.Domain.BackEnd.csproj
index 77e6f58d..5e816d07 100644
--- a/src/Domain/HydraScript.Domain.BackEnd/HydraScript.Domain.BackEnd.csproj
+++ b/src/Domain/HydraScript.Domain.BackEnd/HydraScript.Domain.BackEnd.csproj
@@ -1,5 +1,6 @@
+
\ No newline at end of file
diff --git a/src/Domain/HydraScript.Domain.BackEnd/IOutputWriter.cs b/src/Domain/HydraScript.Domain.BackEnd/IConsole.cs
similarity index 71%
rename from src/Domain/HydraScript.Domain.BackEnd/IOutputWriter.cs
rename to src/Domain/HydraScript.Domain.BackEnd/IConsole.cs
index 98a3caf4..e68bdf33 100644
--- a/src/Domain/HydraScript.Domain.BackEnd/IOutputWriter.cs
+++ b/src/Domain/HydraScript.Domain.BackEnd/IConsole.cs
@@ -1,8 +1,10 @@
namespace HydraScript.Domain.BackEnd;
-public interface IOutputWriter
+public interface IConsole
{
public void WriteLine(object? obj);
public void WriteError(Exception e, string message);
+
+ public string ReadLine();
}
\ No newline at end of file
diff --git a/src/Domain/HydraScript.Domain.BackEnd/IExecuteParams.cs b/src/Domain/HydraScript.Domain.BackEnd/IExecuteParams.cs
index 8c58b105..5a97ac82 100644
--- a/src/Domain/HydraScript.Domain.BackEnd/IExecuteParams.cs
+++ b/src/Domain/HydraScript.Domain.BackEnd/IExecuteParams.cs
@@ -3,7 +3,10 @@ namespace HydraScript.Domain.BackEnd;
public interface IExecuteParams
{
public Stack CallStack { get; }
- public Stack Frames { get; }
+
public Queue Arguments { get; }
- public IOutputWriter Writer { get; }
+
+ public IConsole Console { get; }
+
+ public IFrameContext FrameContext { get; }
}
\ No newline at end of file
diff --git a/src/Domain/HydraScript.Domain.BackEnd/IFrame.cs b/src/Domain/HydraScript.Domain.BackEnd/IFrame.cs
new file mode 100644
index 00000000..2486bbc1
--- /dev/null
+++ b/src/Domain/HydraScript.Domain.BackEnd/IFrame.cs
@@ -0,0 +1,6 @@
+namespace HydraScript.Domain.BackEnd;
+
+public interface IFrame
+{
+ object? this[string id] { get; set; }
+}
\ No newline at end of file
diff --git a/src/Domain/HydraScript.Domain.BackEnd/IFrameContext.cs b/src/Domain/HydraScript.Domain.BackEnd/IFrameContext.cs
new file mode 100644
index 00000000..b8d42c73
--- /dev/null
+++ b/src/Domain/HydraScript.Domain.BackEnd/IFrameContext.cs
@@ -0,0 +1,10 @@
+namespace HydraScript.Domain.BackEnd;
+
+public interface IFrameContext
+{
+ IFrame Current { get; }
+
+ void StepIn();
+
+ void StepOut();
+}
\ No newline at end of file
diff --git a/src/Domain/HydraScript.Domain.BackEnd/IValue.cs b/src/Domain/HydraScript.Domain.BackEnd/IValue.cs
index 847ebd44..01187554 100644
--- a/src/Domain/HydraScript.Domain.BackEnd/IValue.cs
+++ b/src/Domain/HydraScript.Domain.BackEnd/IValue.cs
@@ -2,5 +2,5 @@ namespace HydraScript.Domain.BackEnd;
public interface IValue : IEquatable
{
- object? Get(Frame? frame);
+ object? Get();
}
\ No newline at end of file
diff --git a/src/Domain/HydraScript.Domain.BackEnd/IVirtualMachine.cs b/src/Domain/HydraScript.Domain.BackEnd/IVirtualMachine.cs
index e8263e32..194d14c1 100644
--- a/src/Domain/HydraScript.Domain.BackEnd/IVirtualMachine.cs
+++ b/src/Domain/HydraScript.Domain.BackEnd/IVirtualMachine.cs
@@ -3,5 +3,6 @@ namespace HydraScript.Domain.BackEnd;
public interface IVirtualMachine
{
public IExecuteParams ExecuteParams { get; }
+
public void Run(AddressedInstructions instructions);
}
\ No newline at end of file
diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/ExecuteParams.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/ExecuteParams.cs
index 19b4eeeb..1425fd88 100644
--- a/src/Domain/HydraScript.Domain.BackEnd/Impl/ExecuteParams.cs
+++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/ExecuteParams.cs
@@ -1,9 +1,14 @@
namespace HydraScript.Domain.BackEnd.Impl;
-public class ExecuteParams(IOutputWriter textWriter) : IExecuteParams
+public class ExecuteParams(
+ IConsole console,
+ IFrameContext frameContext) : IExecuteParams
{
- public Stack CallStack { get; } = new();
- public Stack Frames { get; } = new();
- public Queue Arguments { get; } = new();
- public IOutputWriter Writer { get; } = textWriter;
+ public Stack CallStack { get; } = [];
+
+ public Queue Arguments { get; } = [];
+
+ public IConsole Console { get; } = console;
+
+ public IFrameContext FrameContext { get; } = frameContext;
}
\ No newline at end of file
diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/FrameContext.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/FrameContext.cs
new file mode 100644
index 00000000..0f464c74
--- /dev/null
+++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/FrameContext.cs
@@ -0,0 +1,17 @@
+using HydraScript.Domain.BackEnd.Impl.Frames;
+
+namespace HydraScript.Domain.BackEnd.Impl;
+
+public sealed class FrameContext : IFrameContext
+{
+ private readonly Stack
-
- Size
- true
- false
- true
-
-
+
+ true
+ true
+ HydraScript
+ TypeScript & Go inspired small interpreted programming language written in C#
+ Stepami
+ N/A
+ Copyright © Stepami 2025
+ https://github.com/Stepami/hydrascript
+ git
+ https://github.com/Stepami/hydrascript
+ HydraScript
+ LICENSE
+ interpreter script scripting cgi json backend bash perl typescript go csharp
+ true
+ hydrascript
+ LICENSE
+ README.md
+ icon.png
+
+
+
+
+
+
+
+
diff --git a/src/Infrastructure/HydraScript.Infrastructure/Dumping/DumpingLexer.cs b/src/Infrastructure/HydraScript.Infrastructure/Dumping/DumpingLexer.cs
index e9b53573..e9888240 100644
--- a/src/Infrastructure/HydraScript.Infrastructure/Dumping/DumpingLexer.cs
+++ b/src/Infrastructure/HydraScript.Infrastructure/Dumping/DumpingLexer.cs
@@ -1,4 +1,4 @@
-using System.Diagnostics.CodeAnalysis;
+using Cysharp.Text;
using HydraScript.Domain.FrontEnd.Lexer;
using Microsoft.Extensions.DependencyInjection;
@@ -9,13 +9,12 @@ internal class DumpingLexer(
ILexer lexer,
IDumpingService dumpingService) : ILexer
{
- [ExcludeFromCodeCoverage]
public IStructure Structure => lexer.Structure;
- public List GetTokens(string text)
+ public IEnumerable GetTokens(string text)
{
- var tokens = lexer.GetTokens(text);
- dumpingService.Dump(lexer.ToString(), "tokens");
+ var tokens = lexer.GetTokens(text).ToList();
+ dumpingService.Dump(ZString.Join('\n', tokens), "tokens");
return tokens;
}
}
\ No newline at end of file
diff --git a/src/Infrastructure/HydraScript.Infrastructure/Executor.cs b/src/Infrastructure/HydraScript.Infrastructure/Executor.cs
index 80bef01c..aedbc491 100644
--- a/src/Infrastructure/HydraScript.Infrastructure/Executor.cs
+++ b/src/Infrastructure/HydraScript.Infrastructure/Executor.cs
@@ -14,7 +14,7 @@ public class Executor(
{
public int Invoke()
{
- var writer = virtualMachine.ExecuteParams.Writer;
+ var writer = virtualMachine.ExecuteParams.Console;
try
{
var sourceCode = sourceCodeProvider.GetText();
diff --git a/src/Infrastructure/HydraScript.Infrastructure/LoggingWriter.cs b/src/Infrastructure/HydraScript.Infrastructure/HydraScriptConsole.cs
similarity index 57%
rename from src/Infrastructure/HydraScript.Infrastructure/LoggingWriter.cs
rename to src/Infrastructure/HydraScript.Infrastructure/HydraScriptConsole.cs
index 54fed74c..7f4f7776 100644
--- a/src/Infrastructure/HydraScript.Infrastructure/LoggingWriter.cs
+++ b/src/Infrastructure/HydraScript.Infrastructure/HydraScriptConsole.cs
@@ -6,15 +6,17 @@
namespace HydraScript.Infrastructure;
[ExcludeFromCodeCoverage]
-internal partial class LoggingWriter(ILogger logger) : IOutputWriter
+internal partial class HydraScriptConsole(ILogger logger) : IConsole
{
[ZLoggerMessage(Level = LogLevel.Information, Message = "{obj}")]
- private static partial void WriteLine(ILogger logger, object? obj);
+ private static partial void WriteLine(ILogger logger, object? obj);
public void WriteLine(object? obj) => WriteLine(logger, obj);
[ZLoggerMessage(Level = LogLevel.Error, Message = "{message}")]
- private static partial void WriteError(ILogger logger, Exception e, string message);
+ private static partial void WriteError(ILogger logger, Exception e, string message);
public void WriteError(Exception e, string message) => WriteError(logger, e, message);
+
+ public string ReadLine() => Console.ReadLine() ?? string.Empty;
}
\ No newline at end of file
diff --git a/src/Infrastructure/HydraScript.Infrastructure/ServiceCollectionExtensions.cs b/src/Infrastructure/HydraScript.Infrastructure/ServiceCollectionExtensions.cs
index 81562386..78c93256 100644
--- a/src/Infrastructure/HydraScript.Infrastructure/ServiceCollectionExtensions.cs
+++ b/src/Infrastructure/HydraScript.Infrastructure/ServiceCollectionExtensions.cs
@@ -24,7 +24,9 @@ public static IServiceCollection AddDomain(this IServi
services.AddSingleton();
services.AddSingleton();
- services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton(SystemEnvironmentVariableProvider.Instance);
+ services.AddSingleton();
services.AddSingleton();
return services;
diff --git a/tests/Directory.Packages.props b/tests/Directory.Packages.props
index c539153a..07de0211 100644
--- a/tests/Directory.Packages.props
+++ b/tests/Directory.Packages.props
@@ -6,13 +6,13 @@
-
+
-
+
-
+
\ No newline at end of file
diff --git a/tests/HydraScript.IntegrationTests/DumpOptionTests.cs b/tests/HydraScript.IntegrationTests/DumpOptionTests.cs
index 16adbccf..29bee0be 100644
--- a/tests/HydraScript.IntegrationTests/DumpOptionTests.cs
+++ b/tests/HydraScript.IntegrationTests/DumpOptionTests.cs
@@ -11,7 +11,7 @@ public class DumpOptionTests(TestHostFixture fixture) : IClassFixture>>[]"));
- var outputWriter = runner.ServiceProvider.GetRequiredService();
+ var outputWriter = runner.ServiceProvider.GetRequiredService();
var fileSystemMock = runner.ServiceProvider.GetRequiredService();
fileSystemMock.File
.WhenForAnyArgs(x => x.WriteAllText(Arg.Any(), Arg.Any()))
diff --git a/tests/HydraScript.IntegrationTests/HydraScript.IntegrationTests.csproj b/tests/HydraScript.IntegrationTests/HydraScript.IntegrationTests.csproj
index a10b5036..5ca102d9 100644
--- a/tests/HydraScript.IntegrationTests/HydraScript.IntegrationTests.csproj
+++ b/tests/HydraScript.IntegrationTests/HydraScript.IntegrationTests.csproj
@@ -17,116 +17,8 @@
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
+
+ PreserveNewest
diff --git a/tests/HydraScript.IntegrationTests/InputTests.cs b/tests/HydraScript.IntegrationTests/InputTests.cs
new file mode 100644
index 00000000..5722ab83
--- /dev/null
+++ b/tests/HydraScript.IntegrationTests/InputTests.cs
@@ -0,0 +1,29 @@
+using HydraScript.Domain.BackEnd;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using NSubstitute;
+
+namespace HydraScript.IntegrationTests;
+
+public class InputTests(TestHostFixture fixture) : IClassFixture
+{
+ [Fact]
+ public void Invoke_Input_Success()
+ {
+ var console = Substitute.For();
+ console.ReadLine().ReturnsForAnyArgs("1");
+
+ using var runner = fixture.GetRunner(
+ new TestHostFixture.Options(
+ InMemoryScript: "<<< $SOME_NUMBER >>> $SOME_NUMBER"),
+ services => services.Replace(ServiceDescriptor.Singleton(console)));
+
+ var env = runner.ServiceProvider.GetRequiredService();
+ env.When(x => x.SetEnvironmentVariable("SOME_NUMBER", "1"))
+ .Do(_ => env.GetEnvironmentVariable("SOME_NUMBER").Returns("1"));
+
+ runner.Invoke();
+
+ console.Received(1).WriteLine("1");
+ }
+}
\ No newline at end of file
diff --git a/tests/HydraScript.IntegrationTests/Samples/cast.js b/tests/HydraScript.IntegrationTests/Samples/cast.js
new file mode 100644
index 00000000..7610d064
--- /dev/null
+++ b/tests/HydraScript.IntegrationTests/Samples/cast.js
@@ -0,0 +1,4 @@
+let one = 1 as string
+let i = one as number
+if (i as boolean)
+ >>> "Success"
\ No newline at end of file
diff --git a/tests/HydraScript.IntegrationTests/Samples/defaultarray.js b/tests/HydraScript.IntegrationTests/Samples/defaultarray.js
index 9ba49e63..6b784f74 100644
--- a/tests/HydraScript.IntegrationTests/Samples/defaultarray.js
+++ b/tests/HydraScript.IntegrationTests/Samples/defaultarray.js
@@ -2,4 +2,5 @@ type numbers = number[]
let a: numbers
->>>a
\ No newline at end of file
+>>>a
+>>>~a
\ No newline at end of file
diff --git a/tests/HydraScript.IntegrationTests/Samples/env.js b/tests/HydraScript.IntegrationTests/Samples/env.js
new file mode 100644
index 00000000..1eab0da7
--- /dev/null
+++ b/tests/HydraScript.IntegrationTests/Samples/env.js
@@ -0,0 +1,2 @@
+$MY_ENV_VAR = "my_env_var_value"
+>>> $MY_ENV_VAR
\ No newline at end of file
diff --git a/tests/HydraScript.IntegrationTests/Samples/jump.js b/tests/HydraScript.IntegrationTests/Samples/jump.js
index 070230f6..0c7a99a4 100644
--- a/tests/HydraScript.IntegrationTests/Samples/jump.js
+++ b/tests/HydraScript.IntegrationTests/Samples/jump.js
@@ -1,9 +1,11 @@
let i = 0
+
while (i < 10) {
+ i = i + 1
if (i % 2 == 0)
continue
if (i == 7)
break
- i = i + 1
}
+
>>> i
\ No newline at end of file
diff --git a/tests/HydraScript.IntegrationTests/Samples/string_reversed.js b/tests/HydraScript.IntegrationTests/Samples/string_reversed.js
new file mode 100644
index 00000000..d6ccd61a
--- /dev/null
+++ b/tests/HydraScript.IntegrationTests/Samples/string_reversed.js
@@ -0,0 +1,8 @@
+let str = "string"
+let i = ~str - 1
+let revStr: string
+while (i >= 0) {
+ revStr = revStr + str[i]
+ i = i - 1
+}
+>>> revStr
\ No newline at end of file
diff --git a/tests/HydraScript.IntegrationTests/Samples/this.js b/tests/HydraScript.IntegrationTests/Samples/this.js
index f53f2106..58508c5d 100644
--- a/tests/HydraScript.IntegrationTests/Samples/this.js
+++ b/tests/HydraScript.IntegrationTests/Samples/this.js
@@ -2,6 +2,8 @@ type ObjType = {
num: number;
flag: boolean;
str: string;
+ arr: number[];
+ nullableFlag: boolean?;
}
function toString(obj: ObjType): string {
@@ -13,6 +15,8 @@ let obj: ObjType = {
num: 1;
flag: true;
str: "field";
+ arr: [1, 2, 3];
+ nullableFlag: null;
}
>>> obj.toString()
\ No newline at end of file
diff --git a/tests/HydraScript.IntegrationTests/Samples/xxx.js b/tests/HydraScript.IntegrationTests/Samples/xxx.js
index cc850991..aba9666f 100644
--- a/tests/HydraScript.IntegrationTests/Samples/xxx.js
+++ b/tests/HydraScript.IntegrationTests/Samples/xxx.js
@@ -1,7 +1,8 @@
type x = number
let x:x
function x(x:x) {
->>>x
+ >>>x
+ x = x + 1
}
x(x)
\ No newline at end of file
diff --git a/tests/HydraScript.IntegrationTests/SuccessfulProgramsTests.cs b/tests/HydraScript.IntegrationTests/SuccessfulProgramsTests.cs
index 36ea7628..ffd0df6d 100644
--- a/tests/HydraScript.IntegrationTests/SuccessfulProgramsTests.cs
+++ b/tests/HydraScript.IntegrationTests/SuccessfulProgramsTests.cs
@@ -11,7 +11,8 @@ public void Invoke_NoError_ReturnCodeIsZero(string relativePathToFile)
using var runner = fixture.GetRunner(
new TestHostFixture.Options(
FileName: relativePathToFile,
- MockFileSystem: false));
+ MockFileSystem: false,
+ MockEnv: false));
var code = runner.Invoke();
code.Should().Be(Executor.ExitCodes.Success);
}
diff --git a/tests/HydraScript.IntegrationTests/TestHostFixture.cs b/tests/HydraScript.IntegrationTests/TestHostFixture.cs
index 5cda0308..b2fbbf4f 100644
--- a/tests/HydraScript.IntegrationTests/TestHostFixture.cs
+++ b/tests/HydraScript.IntegrationTests/TestHostFixture.cs
@@ -1,6 +1,7 @@
using System.IO.Abstractions;
using HydraScript.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Serilog;
@@ -15,6 +16,7 @@ public record Options(
string FileName = ScriptFileName + ".js",
bool Dump = false,
bool MockFileSystem = true,
+ bool MockEnv = true,
string InMemoryScript = "");
public class Runner(ServiceProvider serviceProvider, Executor executor): IDisposable
@@ -54,7 +56,13 @@ public Runner GetRunner(Options options, Action? configureTe
if (options.MockFileSystem)
{
var fileSystem = Substitute.For();
- services.AddSingleton(fileSystem);
+ services.Replace(ServiceDescriptor.Singleton(fileSystem));
+ }
+
+ if (options.MockEnv)
+ {
+ var env = Substitute.For();
+ services.Replace(ServiceDescriptor.Singleton(env));
}
if (!string.IsNullOrWhiteSpace(options.InMemoryScript))
diff --git a/tests/HydraScript.UnitTests/Application/ExpressionInstructionProviderTests.cs b/tests/HydraScript.UnitTests/Application/ExpressionInstructionProviderTests.cs
index ba4fde83..de88f66c 100644
--- a/tests/HydraScript.UnitTests/Application/ExpressionInstructionProviderTests.cs
+++ b/tests/HydraScript.UnitTests/Application/ExpressionInstructionProviderTests.cs
@@ -1,6 +1,7 @@
using HydraScript.Application.CodeGeneration.Impl;
using HydraScript.Application.CodeGeneration.Visitors;
using HydraScript.Domain.BackEnd;
+using HydraScript.Domain.BackEnd.Impl;
using HydraScript.Domain.FrontEnd.Parser;
using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions;
@@ -8,7 +9,8 @@ namespace HydraScript.UnitTests.Application;
public class ExpressionInstructionProviderTests(ITestOutputHelper testOutputHelper)
{
- private readonly ExpressionInstructionProvider _expressionInstructionProvider = new(new ValueDtoConverter());
+ private readonly ExpressionInstructionProvider _expressionInstructionProvider =
+ new(new ValueFactory(new FrameContext(), new SystemEnvironmentProvider()));
[Theory, ClassData(typeof(WithExpressionData))]
public void Visit_WithExpression_ExpectedInstructions(
diff --git a/tests/HydraScript.UnitTests/Application/FunctionWithUndefinedReturnStorageTests.cs b/tests/HydraScript.UnitTests/Application/FunctionWithUndefinedReturnStorageTests.cs
index 9115da6f..c7f573e5 100644
--- a/tests/HydraScript.UnitTests/Application/FunctionWithUndefinedReturnStorageTests.cs
+++ b/tests/HydraScript.UnitTests/Application/FunctionWithUndefinedReturnStorageTests.cs
@@ -27,7 +27,8 @@ public void StorageIsEmptyAfterFlushTest()
name: new IdentifierReference(FunctionName),
returnTypeValue: Substitute.For(),
arguments: [],
- new BlockStatement([]));
+ new BlockStatement([]),
+ indexOfFirstDefaultArgument: int.MaxValue);
storage.Save(symbol, decl);
@@ -44,26 +45,30 @@ public void StorageIsCorrectOrderTest()
name: new IdentifierReference(FunctionName + "1"),
returnTypeValue: Substitute.For(),
arguments: [],
- new BlockStatement([])),
+ new BlockStatement([]),
+ indexOfFirstDefaultArgument: int.MaxValue),
new(
name: new IdentifierReference(FunctionName + "2"),
returnTypeValue: Substitute.For(),
arguments: [],
- new BlockStatement([])),
+ new BlockStatement([]),
+ indexOfFirstDefaultArgument: int.MaxValue),
new(
name: new IdentifierReference(FunctionName + "3"),
returnTypeValue: Substitute.For(),
arguments: [],
- new BlockStatement([])),
+ new BlockStatement([]),
+ indexOfFirstDefaultArgument: int.MaxValue),
new(
name: new IdentifierReference(FunctionName + "4"),
returnTypeValue: Substitute.For(),
arguments: [],
- new BlockStatement([]))];
-
+ new BlockStatement([]),
+ indexOfFirstDefaultArgument: int.MaxValue)];
+
IFunctionWithUndefinedReturnStorage storage = new FunctionWithUndefinedReturnStorage();
var removable = new FunctionSymbol(
diff --git a/tests/HydraScript.UnitTests/Application/HydraScriptTypesServiceTests.cs b/tests/HydraScript.UnitTests/Application/HydraScriptTypesServiceTests.cs
new file mode 100644
index 00000000..fac4678f
--- /dev/null
+++ b/tests/HydraScript.UnitTests/Application/HydraScriptTypesServiceTests.cs
@@ -0,0 +1,37 @@
+using HydraScript.Application.StaticAnalysis.Impl;
+using HydraScript.Domain.IR.Types;
+
+namespace HydraScript.UnitTests.Application;
+
+public class HydraScriptTypesServiceTests
+{
+ private readonly HydraScriptTypesService _typesService = new();
+
+ [Theory, MemberData(nameof(ConversionsData))]
+ public void IsExplicitCastAllowed_Always_Success(Type from, Type to, bool expected) =>
+ _typesService.IsExplicitCastAllowed(from, to).Should().Be(expected);
+
+ public static TheoryData ConversionsData =>
+ new()
+ {
+ { new ObjectType([]), "string", true },
+ { "number", new NullableType("number"), true },
+ { new NullableType("number"), "number", true },
+ { "string", "number", true },
+ { "string", "boolean", true },
+ { "boolean", "number", true },
+ { "number", "boolean", true },
+ { new ObjectType([]), "boolean", false },
+ { new ArrayType("number"), "number", false },
+ { new ArrayType("number"), new NullableType("boolean"), false },
+ };
+
+ [Fact]
+ public void GetDefaultValueForType_NullableTypes_ReturnsNull()
+ {
+ var calculator = new HydraScriptTypesService();
+ Assert.Null(calculator.GetDefaultValueForType(new NullableType(new Any())));
+ Assert.Null(calculator.GetDefaultValueForType(new NullType()));
+ Assert.Null(calculator.GetDefaultValueForType(new ObjectType([])));
+ }
+}
\ No newline at end of file
diff --git a/tests/HydraScript.UnitTests/Application/ReturnAnalyzerTests.cs b/tests/HydraScript.UnitTests/Application/ReturnAnalyzerTests.cs
index c88175d8..88f2a916 100644
--- a/tests/HydraScript.UnitTests/Application/ReturnAnalyzerTests.cs
+++ b/tests/HydraScript.UnitTests/Application/ReturnAnalyzerTests.cs
@@ -22,14 +22,14 @@ public void Visit_FunctionWithMissingReturn_CodePathEndedWithReturnIsFalse()
// Arrange
var functionDeclaration = new FunctionDeclaration(
new IdentifierReference("f"),
- new TypeIdentValue(new IdentifierReference("undefined")),
- [new NamedArgument("b", new TypeIdentValue(new IdentifierReference("boolean")))],
+ TypeIdentValue.Undefined,
+ [new NamedArgument("b", TypeIdentValue.Boolean)],
new BlockStatement([
new IfStatement(
new IdentifierReference("b"),
- new ReturnStatement(
- new Literal(new TypeIdentValue(new IdentifierReference("number")), 1, "segment")))
- ]));
+ new ReturnStatement(Literal.Number(1)))
+ ]),
+ indexOfFirstDefaultArgument: int.MaxValue);
// Act
var result = new ReturnAnalyzer().Visit(functionDeclaration);
diff --git a/tests/HydraScript.UnitTests/Application/WithExpressionData.cs b/tests/HydraScript.UnitTests/Application/WithExpressionData.cs
index 9a1aa247..478d6e02 100644
--- a/tests/HydraScript.UnitTests/Application/WithExpressionData.cs
+++ b/tests/HydraScript.UnitTests/Application/WithExpressionData.cs
@@ -4,7 +4,6 @@
using HydraScript.Domain.BackEnd.Impl.Values;
using HydraScript.Domain.FrontEnd.Parser;
using HydraScript.Domain.FrontEnd.Parser.Impl.Ast;
-using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Declarations;
using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions;
using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions.ComplexLiterals;
using HydraScript.Domain.FrontEnd.Parser.Impl.Ast.Nodes.Expressions.PrimaryExpressions;
@@ -24,13 +23,12 @@ public WithExpressionData()
new ObjectLiteral([
new Property(
new IdentifierReference("x"),
- new Literal(new TypeIdentValue(new IdentifierReference("number")), 1,
- "(1, 15)-(1, 16)"))
+ Literal.Number(1, "(1, 15)-(1, 16)"))
]),
new ObjectLiteral([])))),
[
- new CreateObject("obj"),
- new DotAssignment("obj", new Constant("x"), new Constant(1))
+ new CreateObject(new Name("obj", Substitute.For