├── .github └── workflows │ ├── ci.yaml │ └── master_smallbasic-publicwebsite-code.yml ├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── LICENSE ├── README.md ├── SmallBasic.sln ├── Source ├── Directory.Build.props ├── SmallBasic.Analyzers │ ├── DoNotUseAsserts.cs │ ├── SmallBasic.Analyzers.csproj │ └── UseIsDefaultHelper.cs ├── SmallBasic.Bridge │ ├── Bridge.Generated.cs │ ├── FileBridge.cs │ ├── NetworkBridge.cs │ ├── ProcessBridge.cs │ ├── Program.cs │ └── SmallBasic.Bridge.csproj ├── SmallBasic.Client │ ├── Bridge │ │ └── Bridge.Generated.ts │ ├── Entries │ │ ├── Electron.ts │ │ ├── Renderer.ts │ │ ├── Styles.scss │ │ ├── Template.html │ │ └── Web.ts │ ├── Images │ │ ├── Actions │ │ │ ├── Back.svg │ │ │ ├── Continue.svg │ │ │ ├── Copy.svg │ │ │ ├── Cut.svg │ │ │ ├── Debug.svg │ │ │ ├── Import.svg │ │ │ ├── New.svg │ │ │ ├── NextLine.svg │ │ │ ├── Open.svg │ │ │ ├── Paste.svg │ │ │ ├── Pause.svg │ │ │ ├── Publish.svg │ │ │ ├── Redo.svg │ │ │ ├── Run.svg │ │ │ ├── Save.svg │ │ │ └── Undo.svg │ │ ├── Caret.svg │ │ ├── EditPage │ │ │ ├── Error.svg │ │ │ └── LibraryExplorer.svg │ │ ├── MemoryExplorer │ │ │ ├── ArrayType.svg │ │ │ ├── CallStack.svg │ │ │ ├── MemoryExplorer.svg │ │ │ ├── NumberType.svg │ │ │ ├── ProgramEnded.svg │ │ │ ├── ProgramRunning.svg │ │ │ ├── StringType.svg │ │ │ └── Variables.svg │ │ ├── Turtle.svg │ │ ├── favicon.ico │ │ └── logo.svg │ ├── Interop │ │ ├── CSInterop.Generated.ts │ │ ├── JSInterop.Generated.ts │ │ ├── LayoutInterop.ts │ │ └── MonacoInterop.ts │ ├── SmallBasic.Client.csproj │ ├── Utility │ │ ├── GraphicsDisplay.ts │ │ ├── SetupAppInsights.ts │ │ └── SetupMonacoEnvironment.ts │ ├── package.json │ ├── tsconfig.json │ ├── tslint.json │ ├── webpack.config.ts │ └── yarn.lock ├── SmallBasic.Compiler │ ├── Binding │ │ ├── BaseBoundNode.cs │ │ ├── Binder.cs │ │ ├── BoundNodes.Generated.cs │ │ └── Visitors │ │ │ ├── GoToUndefinedLabelChecker.cs │ │ │ ├── LabelDefinitionsCollector.cs │ │ │ ├── RuntimeAnalysis.cs │ │ │ └── VariablesAndSubModulesCollector.cs │ ├── Diagnostics │ │ ├── Diagnostic.cs │ │ ├── DiagnosticBag.Generated.cs │ │ └── DiagnosticCode.Generated.cs │ ├── Parsing │ │ ├── BaseSyntaxNode.cs │ │ ├── Parser.cs │ │ ├── SyntaxNodes.Generated.cs │ │ └── Visitors │ │ │ └── SubModuleNamesCollector.cs │ ├── Runtime │ │ ├── DebuggerSnapshot.cs │ │ ├── Frame.cs │ │ ├── Instructions │ │ │ ├── BaseInstructions.cs │ │ │ ├── ChangingStateInstructions.cs │ │ │ ├── JumpInstructions.cs │ │ │ ├── MemoryInstructions.cs │ │ │ ├── OperatorInstructions.cs │ │ │ └── OtherInstructions.cs │ │ ├── Libraries │ │ │ ├── Libraries.Generated.cs │ │ │ └── Libraries.Interfaces.Generated.cs │ │ ├── ModuleEmitter.cs │ │ ├── RuntimeModule.cs │ │ └── Values │ │ │ ├── ArrayValue.cs │ │ │ ├── BaseValue.cs │ │ │ ├── BooleanValue.cs │ │ │ ├── NumberValue.cs │ │ │ └── StringValue.cs │ ├── Scanning │ │ ├── Scanner.cs │ │ ├── TextPosition.cs │ │ ├── TextRange.cs │ │ ├── Token.cs │ │ └── TokenKind.Generated.cs │ ├── Services │ │ ├── CompletionItemProvider.cs │ │ ├── HoverProvider.cs │ │ └── MonacoTypes.cs │ ├── SmallBasic.Compiler.csproj │ ├── SmallBasicCompilation.cs │ └── SmallBasicEngine.cs ├── SmallBasic.Editor │ ├── Bridge │ │ └── Bridge.Generated.cs │ ├── Components │ │ ├── Display │ │ │ ├── EngineDisplay.cs │ │ │ ├── EngineDisplay.scss │ │ │ ├── GraphicsDisplay.cs │ │ │ ├── GraphicsDisplay.scss │ │ │ ├── TextDisplay.cs │ │ │ └── TextDisplay.scss │ │ ├── Layout │ │ │ ├── App.cs │ │ │ ├── MainLayout.cs │ │ │ ├── MainLayout.scss │ │ │ └── SmallBasicComponent.cs │ │ ├── Pages │ │ │ ├── Debug │ │ │ │ ├── DebugPage.cs │ │ │ │ ├── DebugPage.scss │ │ │ │ ├── MemoryExplorer.cs │ │ │ │ └── MemoryExplorer.scss │ │ │ ├── Edit │ │ │ │ ├── EditPage.cs │ │ │ │ ├── EditPage.scss │ │ │ │ ├── ErrorsSpace.cs │ │ │ │ ├── ErrorsSpace.scss │ │ │ │ ├── LibraryBody.cs │ │ │ │ ├── LibraryBody.scss │ │ │ │ ├── LibraryExplorer.cs │ │ │ │ └── LibraryExplorer.scss │ │ │ ├── IndexPage.cs │ │ │ └── Run │ │ │ │ ├── RunPage.cs │ │ │ │ └── RunPage.scss │ │ ├── Toolbox │ │ │ ├── ActionsRow.cs │ │ │ ├── ActionsRow.scss │ │ │ ├── Micro.cs │ │ │ ├── MonacoEditor.cs │ │ │ └── MonacoEditor.scss │ │ └── TreeComposer.cs │ ├── Interop │ │ ├── CSInterop.Generated.cs │ │ ├── GraphicsDisplayInterop.cs │ │ ├── JSInterop.Generated.cs │ │ └── MonacoInterop.cs │ ├── Libraries │ │ ├── ArrayLibrary.cs │ │ ├── ClockLibrary.cs │ │ ├── Controls │ │ │ ├── BaseControl.cs │ │ │ ├── ButtonControl.cs │ │ │ ├── MultilineTextBoxControl.cs │ │ │ └── TextBoxControl.cs │ │ ├── ControlsLibrary.cs │ │ ├── DesktopLibrary.cs │ │ ├── DictionaryLibrary.cs │ │ ├── FileLibrary.cs │ │ ├── FlickrLibrary.cs │ │ ├── Graphics │ │ │ ├── BaseGraphicsObject.cs │ │ │ ├── EllipseGraphicsObject.cs │ │ │ ├── ImageGraphicsObject.cs │ │ │ ├── LineGraphicsObject.cs │ │ │ ├── RectangleGraphicsObject.cs │ │ │ ├── TextGraphicsObject.cs │ │ │ ├── TriangleGraphicsObject.cs │ │ │ └── TurtleGraphicsObject.cs │ │ ├── GraphicsWindowLibrary.cs │ │ ├── ImageListLibrary.cs │ │ ├── LibrariesCollection.cs │ │ ├── MathLibrary.cs │ │ ├── MouseLibrary.cs │ │ ├── NetworkLibrary.cs │ │ ├── ProgramLibrary.cs │ │ ├── Shapes │ │ │ ├── BaseShape.cs │ │ │ ├── EllipseShape.cs │ │ │ ├── ImageShape.cs │ │ │ ├── LineShape.cs │ │ │ ├── RectangleShape.cs │ │ │ ├── TextShape.cs │ │ │ ├── TriangleShape.cs │ │ │ └── TurtleShape.cs │ │ ├── ShapesLibrary.cs │ │ ├── SoundLibrary.cs │ │ ├── StackLibrary.cs │ │ ├── TextLibrary.cs │ │ ├── TextWindowLibrary.cs │ │ ├── TimerLibrary.cs │ │ ├── TurtleLibrary.cs │ │ └── Utilities │ │ │ ├── AsyncEngine.cs │ │ │ ├── GraphicsWindowStyles.cs │ │ │ ├── NamedCounter.cs │ │ │ └── PredefinedColors.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── SmallBasic.Editor.csproj │ └── Store │ │ ├── CompilationStore.cs │ │ ├── GraphicsDisplayStore.cs │ │ ├── NavigationStore.cs │ │ └── TextDisplayStore.cs ├── SmallBasic.Generators │ ├── BaseConverterTask.cs │ ├── BaseTask.cs │ ├── Binding │ │ ├── BoundNodes.xml │ │ ├── GenerateBoundNodes.cs │ │ └── Models.cs │ ├── Bridge │ │ ├── BridgeTypes.xml │ │ ├── GenerateBridgeExecution.cs │ │ ├── GenerateClientBridge.cs │ │ ├── GenerateEditorBridge.cs │ │ └── Models.cs │ ├── Diagnostics │ │ ├── Diagnostics.xml │ │ ├── GenerateDiagnosticBag.cs │ │ ├── GenerateDiagnosticCode.cs │ │ └── Models.cs │ ├── Interop │ │ ├── CSInteropTypes.xml │ │ ├── GenerateCSClientInterop.cs │ │ ├── GenerateCSEditorInterop.cs │ │ ├── GenerateJSClientInterop.cs │ │ ├── GenerateJSEditorInterop.cs │ │ ├── JSInteropTypes.xml │ │ └── Models.cs │ ├── Libraries │ │ ├── GenerateLibraries.cs │ │ ├── GenerateLibraryInterfaces.cs │ │ ├── GenerateLoggingTestLibraries.cs │ │ ├── Libraries.xml │ │ └── Models.cs │ ├── Parsing │ │ ├── GenerateSyntaxNodes.cs │ │ ├── Models.cs │ │ └── SyntaxNodes.xml │ ├── Program.cs │ ├── Scanning │ │ ├── GenerateTokenKinds.cs │ │ ├── Models.cs │ │ └── TokenKinds.xml │ └── SmallBasic.Generators.csproj ├── SmallBasic.Server │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ └── SmallBasic.Server.csproj ├── SmallBasic.Tests │ ├── Compiler │ │ ├── BindingTests.cs │ │ ├── ParsingTests.cs │ │ └── ScanningTests.cs │ ├── CultureFixture.cs │ ├── LoggingTestLibraries.Generated.cs │ ├── Runtime │ │ ├── ExpressionTests.cs │ │ ├── LibrariesTests.cs │ │ ├── OperatorTests.cs │ │ ├── RuntimeAnalysisTests.cs │ │ └── StatementsTests.cs │ ├── Services │ │ ├── CompletionItemProviderTests.cs │ │ └── HoverProviderTests.cs │ ├── SmallBasic.Tests.csproj │ └── TestExtensions.cs ├── SmallBasic.Utilities │ ├── Bridge │ │ ├── FileBridgeModels.cs │ │ └── ImageListBridgeModels.cs │ ├── ExceptionUtilities.cs │ ├── Extensions │ │ ├── ObjectExtensions.cs │ │ └── StringExtensions.cs │ ├── Resources │ │ ├── DiagnosticsResources.Designer.cs │ │ ├── DiagnosticsResources.resx │ │ ├── EditorResources.Designer.cs │ │ ├── EditorResources.resx │ │ ├── LibrariesResources.Designer.cs │ │ ├── LibrariesResources.resx │ │ ├── TokenKindsResources.Designer.cs │ │ └── TokenKindsResources.resx │ └── SmallBasic.Utilities.csproj └── stylecop.json └── global.json /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | pull_request: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | configuration: ["Debug", "Release"] 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions/setup-node@v1 15 | with: 16 | node-version: 16.11.1 17 | - uses: actions/setup-dotnet@v1.7.2 18 | with: 19 | dotnet-version: 2.1.816 20 | - run: yarn --ci 21 | working-directory: ./Source/SmallBasic.Client 22 | - run: dotnet restore 23 | - run: dotnet build ./SmallBasic.sln /p:TreatWarningsAsErrors=True /p:Configuration=${{matrix.configuration}} 24 | - run: dotnet test ./Source/SmallBasic.Tests /p:Configuration=${{matrix.configuration}} 25 | -------------------------------------------------------------------------------- /.github/workflows/master_smallbasic-publicwebsite-code.yml: -------------------------------------------------------------------------------- 1 | # Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy 2 | # More GitHub Actions for Azure: https://github.com/Azure/actions 3 | 4 | name: Build and deploy to Caseysc`s Azure Web App - smallbasic-publicwebsite-code 5 | 6 | on: 7 | push: 8 | branches: 9 | - master 10 | workflow_dispatch: 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - uses: actions/setup-node@v1 18 | with: 19 | node-version: 16.11.1 20 | - uses: actions/setup-dotnet@v1.7.2 21 | with: 22 | dotnet-version: 2.1.816 23 | - run: yarn --ci 24 | working-directory: ./Source/SmallBasic.Client 25 | - run: dotnet restore 26 | - run: dotnet build ./SmallBasic.sln /p:TreatWarningsAsErrors=True /p:Configuration=Release 27 | - run: dotnet publish ./Source/SmallBasic.Editor/SmallBasic.Editor.csproj --no-build /p:TreatWarningsAsErrors=True /p:Configuration=Release 28 | - uses: actions/upload-artifact@v3.1.0 29 | with: 30 | path: /home/runner/work/smallbasic-editor/smallbasic-editor/Source/SmallBasic.Editor/bin/Release/netstandard2.0/publish/ 31 | if-no-files-found: error 32 | 33 | deploy: 34 | runs-on: windows-latest 35 | needs: build 36 | environment: 37 | name: 'Production' 38 | url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} 39 | 40 | steps: 41 | - name: Download artifact from build job 42 | uses: actions/download-artifact@v2 43 | with: 44 | name: artifact 45 | 46 | - name: Deploy to Azure Web App 47 | id: deploy-to-webapp 48 | uses: azure/webapps-deploy@v2 49 | with: 50 | app-name: 'smallbasic-publicwebsite-code' 51 | slot-name: 'Production' 52 | publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_CC78D407C64043A1978FBF8A5FEDA312 }} 53 | package: . 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # VS files 2 | .vs/ 3 | *.csproj.user 4 | 5 | # Output files 6 | bin 7 | obj 8 | node_modules 9 | wwwroot 10 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | ] 8 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dotnet-test-explorer.testProjectPath": "./Source/Tests" 3 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | ] 5 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SmallBasic 2 | 3 | [![](https://github.com/sb/smallbasic-editor/actions/workflows/ci.yaml/badge.svg)](https://github.com/sb/smallbasic-editor/actions/workflows/ci.yaml) 4 | 5 | This is home to the new SmallBasic editor. It has the following projects: 6 | * **SmallBasic.Analyzers**: a set of C# analyzers to help maintain the quality of the codebase. 7 | * **SmallBasic.Client**: a webpack project to bundle client typescript, sass, and image resources. 8 | * **SmallBasic.Compiler**: the compiler/main engine used to run programs. 9 | * **SmallBasic.Editor**: a blazor application that renders editor UI components. 10 | * **SmallBasic.Generators**: a collection of scripts to automatically generate thousands of C#/typescript lines used throughout the codebase. 11 | * **SmallBasic.Server**: an ASP server used to serve/deploy the editor. 12 | * **SmallBasic.Tests**: C# unit tests to test the compiler/engine. 13 | * **SmallBasic.Utilities**: helper utilities shared between different projects. 14 | 15 | #### Developer Workflow 16 | 17 | 1. Open **SmallBasic.sln** in Visual Studio 2017. 18 | 2. Choose **SmallBasic.Editor** and press F5 to run in the browser. This will also listen to C# changes and recompile when needed. 19 | 3. If you're editing **SmallBasic.Client** typescript files, you have to recompile that project, or run `dotnet build Source/SmallBasic.Client /p:Watch=True` from a command line to also listen/recompile these changes. 20 | 4. If changing any auto-generated files, rebuild **SmallBasic.Generators** to regenerate the files. 21 | 22 | NOTE: [this issue](https://github.com/sb/smallbasic-editor/issues/2) is currently tracking improving this workflow. 23 | 24 | ### Contributing to this project 25 | 26 | Please open an issue or comment on an existing one before you start working on a change, to make sure the effort is not duplicated/merged appropriately. 27 | -------------------------------------------------------------------------------- /Source/SmallBasic.Analyzers/DoNotUseAsserts.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Analyzers 6 | { 7 | using System; 8 | using System.Collections.Immutable; 9 | using System.Composition; 10 | using System.Linq; 11 | using System.Threading; 12 | using System.Threading.Tasks; 13 | using Microsoft.CodeAnalysis; 14 | using Microsoft.CodeAnalysis.CodeActions; 15 | using Microsoft.CodeAnalysis.CodeFixes; 16 | using Microsoft.CodeAnalysis.CSharp; 17 | using Microsoft.CodeAnalysis.CSharp.Syntax; 18 | using Microsoft.CodeAnalysis.Diagnostics; 19 | using Microsoft.CodeAnalysis.Text; 20 | 21 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 22 | public class DoNotUseAssertsAnalyzer : DiagnosticAnalyzer 23 | { 24 | public const string Title = "Do not use Assert()"; 25 | public const string MessageFormat = "Use 'Should()' extensions instead of 'Assert()' ones."; 26 | public const string Category = "SmallBasic.Analyzers"; 27 | 28 | public const string DiagnosticId = "SB1008"; 29 | 30 | private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true); 31 | 32 | public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); 33 | 34 | public override void Initialize(AnalysisContext context) 35 | { 36 | context.RegisterSyntaxNodeAction(AnalyzeInvocationExpression, SyntaxKind.InvocationExpression); 37 | } 38 | 39 | private static void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext context) 40 | { 41 | var invocation = (InvocationExpressionSyntax)context.Node; 42 | if (invocation.Expression is MemberAccessExpressionSyntax memberAccess && 43 | memberAccess.Expression is IdentifierNameSyntax identifier && 44 | identifier.Identifier.Text == "Assert") 45 | { 46 | context.ReportDiagnostic(Diagnostic.Create(Rule, invocation.GetLocation())); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Source/SmallBasic.Analyzers/SmallBasic.Analyzers.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Source/SmallBasic.Bridge/NetworkBridge.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Bridge 6 | { 7 | using System; 8 | using System.Drawing; 9 | using System.IO; 10 | using System.Net; 11 | using SmallBasic.Utilities.Bridge; 12 | 13 | internal class NetworkBridge : INetworkBridge 14 | { 15 | public ImageListBridgeModels.ImageData LoadImage(string fileNameOrUrl) 16 | { 17 | if (new Uri(fileNameOrUrl).IsFile) 18 | { 19 | return CreateImage(() => File.ReadAllBytes(fileNameOrUrl)); 20 | } 21 | else 22 | { 23 | using (var client = new WebClient()) 24 | { 25 | return CreateImage(() => client.DownloadData(fileNameOrUrl)); 26 | } 27 | } 28 | } 29 | 30 | public string DownloadFile(string url) 31 | { 32 | try 33 | { 34 | using (var client = new WebClient()) 35 | { 36 | string path = Path.GetTempFileName(); 37 | File.WriteAllText(path, client.DownloadString(url)); 38 | return path; 39 | } 40 | } 41 | catch 42 | { 43 | return string.Empty; 44 | } 45 | } 46 | 47 | public string GetWebPageContents(string url) 48 | { 49 | try 50 | { 51 | using (var client = new WebClient()) 52 | { 53 | return client.DownloadString(url); 54 | } 55 | } 56 | catch 57 | { 58 | return string.Empty; 59 | } 60 | } 61 | 62 | private static ImageListBridgeModels.ImageData CreateImage(Func factory) 63 | { 64 | try 65 | { 66 | byte[] bytes = factory(); 67 | using (var stream = new MemoryStream(bytes)) 68 | { 69 | using (var image = new Bitmap(Image.FromStream(stream))) 70 | { 71 | string base64Contents = Convert.ToBase64String(bytes, Base64FormattingOptions.None); 72 | return new ImageListBridgeModels.ImageData(image.Width, image.Height, base64Contents); 73 | } 74 | } 75 | } 76 | catch 77 | { 78 | return new ImageListBridgeModels.ImageData(width: 0, height: 0, base64Contents: string.Empty); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Source/SmallBasic.Bridge/ProcessBridge.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Bridge 6 | { 7 | using System.Diagnostics; 8 | 9 | internal class ProcessBridge : IProcessBridge 10 | { 11 | public void OpenExternalLink(string url) 12 | { 13 | Process.Start(url); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Source/SmallBasic.Bridge/Program.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Bridge 6 | { 7 | public static class Program 8 | { 9 | public static void Main(string[] args) 10 | { 11 | BridgeExecution.Run(args); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Source/SmallBasic.Bridge/SmallBasic.Bridge.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | Exe 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Entries/Electron.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | */ 4 | 5 | import { app, BrowserWindow } from "electron"; 6 | 7 | let window: BrowserWindow | null = null; 8 | 9 | function createWindow(): void { 10 | window = new BrowserWindow({ 11 | width: 1200, 12 | height: 1100 13 | }); 14 | 15 | // window.setMenu(null); 16 | window.loadFile("index.html"); 17 | 18 | window.on("closed", () => { 19 | window = null; 20 | }); 21 | } 22 | 23 | app.on("ready", createWindow); 24 | 25 | app.on("window-all-closed", () => { 26 | if (process.platform !== "darwin") { 27 | app.quit(); 28 | } 29 | }); 30 | 31 | app.on("activate", () => { 32 | if (window === null) { 33 | createWindow(); 34 | } 35 | }); 36 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Entries/Renderer.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | */ 4 | 5 | import "../Bridge/Bridge.Generated"; 6 | import "./Web"; 7 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Entries/Styles.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | */ 4 | 5 | @mixin set-background-image($relative-path, $width, $height) { 6 | width: $width; 7 | height: $height; 8 | background-size: cover; 9 | background-image: url("../Images/" + $relative-path); 10 | } 11 | 12 | $fa-font-path: "~@fortawesome/fontawesome-free/webfonts"; 13 | @import "@fortawesome/fontawesome-free/scss/fontawesome.scss"; 14 | @import "@fortawesome/fontawesome-free/scss/solid.scss"; 15 | 16 | @import "../../SmallBasic.Editor/Components/**/*.scss"; 17 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Entries/Web.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | */ 4 | 5 | import "typeface-roboto"; 6 | 7 | import "./Styles"; 8 | 9 | import "../Utility/GraphicsDisplay"; 10 | import "../Utility/SetupAppInsights"; 11 | import "../Utility/SetupMonacoEnvironment"; 12 | import "../Interop/JSInterop.Generated"; 13 | import "../Interop/CSInterop.Generated"; 14 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Images/Actions/Back.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Images/Actions/Continue.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Images/Actions/Copy.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Images/Actions/New.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Images/Actions/NextLine.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Images/Actions/Open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Images/Actions/Paste.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Images/Actions/Pause.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Images/Actions/Redo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Images/Actions/Run.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Images/Actions/Save.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Images/Actions/Undo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Images/Caret.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Images/EditPage/Error.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Images/MemoryExplorer/CallStack.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Images/MemoryExplorer/MemoryExplorer.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Images/MemoryExplorer/ProgramEnded.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Images/MemoryExplorer/Variables.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sb/smallbasic-editor/05fc82b7197d35d883118c85f19bd8cf51931615/Source/SmallBasic.Client/Images/favicon.ico -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Interop/CSInterop.Generated.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | */ 4 | 5 | /// 6 | /// This file is auto-generated by a build task. It shouldn't be edited by hand. 7 | /// 8 | 9 | /// 10 | 11 | export module CSIntrop { 12 | export module Monaco { 13 | export function updateDiagnostics(code: string): Promise { 14 | return DotNet.invokeMethodAsync("SmallBasic.Editor", "CSIntrop.Monaco.UpdateDiagnostics", code); 15 | } 16 | 17 | export function provideCompletionItems(code: string, position: monaco.IPosition): Promise { 18 | return DotNet.invokeMethodAsync("SmallBasic.Editor", "CSIntrop.Monaco.ProvideCompletionItems", code, position); 19 | } 20 | 21 | export function provideHover(code: string, position: monaco.IPosition): Promise { 22 | return DotNet.invokeMethodAsync("SmallBasic.Editor", "CSIntrop.Monaco.ProvideHover", code, position); 23 | } 24 | } 25 | 26 | export module GraphicsDisplay { 27 | export function updateDisplayLocation(x: number, y: number): Promise { 28 | return DotNet.invokeMethodAsync("SmallBasic.Editor", "CSIntrop.GraphicsDisplay.UpdateDisplayLocation", x, y).then(() => { 29 | Promise.resolve(); 30 | }); 31 | } 32 | 33 | export function onKeyUp(key: string): Promise { 34 | return DotNet.invokeMethodAsync("SmallBasic.Editor", "CSIntrop.GraphicsDisplay.OnKeyUp", key).then(() => { 35 | Promise.resolve(); 36 | }); 37 | } 38 | 39 | export function onKeyDown(key: string): Promise { 40 | return DotNet.invokeMethodAsync("SmallBasic.Editor", "CSIntrop.GraphicsDisplay.OnKeyDown", key).then(() => { 41 | Promise.resolve(); 42 | }); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Interop/LayoutInterop.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | */ 4 | 5 | import { ILayoutInterop } from "./JSInterop.Generated"; 6 | 7 | export class LayoutInterop implements ILayoutInterop { 8 | public async initializeWebView(locale: string, title: string): Promise { 9 | document.documentElement.setAttribute("lang", locale); 10 | document.title = title; 11 | } 12 | 13 | public async openExternalLink(url: string): Promise { 14 | window.open(url, "_blank"); 15 | } 16 | 17 | public async getElementHeight(element: HTMLElement | null): Promise { 18 | if (element) { 19 | return element.getBoundingClientRect().height; 20 | } 21 | return 0; 22 | } 23 | 24 | public async getElementWidth(element: HTMLElement | null): Promise { 25 | if (element) { 26 | return element.getBoundingClientRect().width; 27 | } 28 | return 0; 29 | } 30 | 31 | public async scrollIntoView(element: HTMLElement | null): Promise { 32 | if (element) { 33 | element.scrollIntoView(); 34 | } 35 | } 36 | 37 | public async focus(element: HTMLElement | null): Promise { 38 | if (element) { 39 | element.focus(); 40 | } 41 | } 42 | 43 | public async showMessage(text: string, title: string): Promise { 44 | // second parameter will do nothing in web, but will display the title in electron 45 | (alert)(text, title); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/SmallBasic.Client.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | $(RootDirectory)\Source\SmallBasic.Editor\wwwroot 6 | 7 | 8 | 9 | 3.0 10 | true 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Utility/GraphicsDisplay.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | */ 4 | 5 | import * as $ from "jquery"; 6 | import { CSIntrop } from "../Interop/CSInterop.Generated"; 7 | 8 | let lastUpdatedX = 0; 9 | let lastUpdatedY = 0; 10 | 11 | $(window).resize(() => { 12 | const display = getGraphicsDisplay(); 13 | if (display === null) { 14 | return; 15 | } 16 | 17 | const rect = display.getBoundingClientRect(); 18 | if (rect.left !== lastUpdatedX || rect.top !== lastUpdatedY) { 19 | lastUpdatedX = rect.left; 20 | lastUpdatedY = rect.top; 21 | CSIntrop.GraphicsDisplay.updateDisplayLocation(lastUpdatedX, lastUpdatedY); 22 | } 23 | }); 24 | 25 | const CHECK_INTERVAL = 250; 26 | 27 | setInterval(() => { 28 | const display = getGraphicsDisplay(); 29 | if (display === null) { 30 | return; 31 | } 32 | 33 | const stamp = "x-display-key-events-done"; 34 | if (display.hasAttribute(stamp)) { 35 | return; 36 | } 37 | 38 | $(display).keyup(e => { 39 | CSIntrop.GraphicsDisplay.onKeyUp(e.key); 40 | }).keydown(e => { 41 | CSIntrop.GraphicsDisplay.onKeyDown(e.key); 42 | }); 43 | 44 | display.setAttribute(stamp, "true"); 45 | }, CHECK_INTERVAL); 46 | 47 | function getGraphicsDisplay(): Element | null { 48 | return document.getElementsByTagName("graphics-display").item(0); 49 | } 50 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Utility/SetupAppInsights.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | */ 4 | 5 | import { AppInsights } from "applicationinsights-js"; 6 | 7 | AppInsights.downloadAndSetup!({ 8 | instrumentationKey: "4558db03-9e28-4a5a-9212-8a76bd8f6d1a" 9 | }); 10 | 11 | AppInsights.trackPageView("PageLoad", window.location.toString()); 12 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/Utility/SetupMonacoEnvironment.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | */ 4 | 5 | /// Do not actually import in TypeScript. This is bundled by webpack separately: 6 | /// 7 | 8 | import { CSIntrop } from "../Interop/CSInterop.Generated"; 9 | 10 | (window).MonacoEnvironment = { 11 | getWorkerUrl: function (): string { 12 | return "./monaco/editor.worker.js"; 13 | } 14 | }; 15 | 16 | function createRange(start: string, stop: string): string[] { 17 | const result: string[] = []; 18 | for (let idx = start.charCodeAt(0), end = stop.charCodeAt(0); idx <= end; ++idx) { 19 | result.push(String.fromCharCode(idx)); 20 | } 21 | return result; 22 | } 23 | 24 | monaco.languages.registerCompletionItemProvider("sb", { 25 | triggerCharacters: [ 26 | ".", 27 | ...createRange("a", "z"), 28 | ...createRange("A", "Z") 29 | ], 30 | provideCompletionItems: (model: monaco.editor.IReadOnlyModel, position: monaco.IPosition): monaco.Thenable => { 31 | return CSIntrop.Monaco.provideCompletionItems(model.getValue(), position); 32 | } 33 | }); 34 | 35 | monaco.languages.registerHoverProvider("sb", { 36 | provideHover: (model: monaco.editor.IReadOnlyModel, position: monaco.IPosition): monaco.Thenable => { 37 | return CSIntrop.Monaco.provideHover(model.getValue(), position).then(lines => { 38 | return { 39 | range: null, 40 | contents: lines.map(line => { 41 | return { 42 | language: null, 43 | value: line 44 | }; 45 | }) 46 | }; 47 | }); 48 | } 49 | }); 50 | 51 | monaco.languages.setLanguageConfiguration("sb", { 52 | indentationRules: { 53 | increaseIndentPattern: /^\s*(If|ElseIf|Else|While|For|Sub)/i, 54 | decreaseIndentPattern: /(ElseIf|Else|EndIf|EndWhile|EndFor|EndSub)\s*$/i 55 | } 56 | }); 57 | 58 | // Reference: https://github.com/Microsoft/monaco-editor/blob/master/test/playground.generated/customizing-the-appearence-exposed-colors.html 59 | monaco.editor.defineTheme("small-basic", { 60 | base: "vs", 61 | inherit: true, 62 | rules: [], 63 | colors: { 64 | "editorWidget.background": "#E7E8EA", 65 | "editorWidget.border": "#656A72" 66 | } 67 | }); 68 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "main": "SmallBasic.Electron.js", 4 | "devDependencies": { 5 | "@dotnet/jsinterop": "0.1.1", 6 | "@fortawesome/fontawesome-free": "5.15.4", 7 | "@timkendrick/monaco-editor": "0.0.9", 8 | "@types/applicationinsights-js": "1.0.9", 9 | "@types/copy-webpack-plugin": "8.0.1", 10 | "@types/element-resize-event": "2.0.0", 11 | "@types/file-saver": "2.0.0", 12 | "@types/html-webpack-plugin": "3.2.6", 13 | "@types/jquery": "3.5.0", 14 | "@types/mini-css-extract-plugin": "2.4.0", 15 | "@types/webpack": "5.28.0", 16 | "applicationinsights-js": "1.0.20", 17 | "autoprefixer": "9.3.1", 18 | "base64-inline-loader": "2.0.1", 19 | "copy-webpack-plugin": "9.0.1", 20 | "css-loader": "1.0.1", 21 | "element-resize-event": "3.0.3", 22 | "file-loader": "2.0.0", 23 | "file-saver": "2.0.0", 24 | "html-webpack-plugin": "5.4.0", 25 | "import-glob-loader": "1.1.0", 26 | "jquery": "3.5.0", 27 | "mini-css-extract-plugin": "2.4.2", 28 | "monaco-editor": "0.10.1", 29 | "popper.js": "1.14.4", 30 | "postcss-loader": "3.0.0", 31 | "precss": "3.1.2", 32 | "sass": "1.43.2", 33 | "sass-loader": "12.2.0", 34 | "source-map-loader": "0.2.4", 35 | "ts-loader": "9.2.6", 36 | "ts-node": "7.0.1", 37 | "tslint": "5.11.0", 38 | "tslint-loader": "3.6.0", 39 | "typeface-roboto": "0.0.54", 40 | "typescript": "4.4.4", 41 | "webpack": "5.58.2", 42 | "webpack-cli": "4.9.0" 43 | }, 44 | "dependencies": { 45 | "dotnet-2.0.0": "1.4.4", 46 | "electron": "15.2.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Source/SmallBasic.Client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "pretty": true, 4 | "diagnostics": true, 5 | "noEmitOnError": true, 6 | 7 | "strict": true, 8 | "allowJs": false, 9 | "alwaysStrict": true, 10 | "noImplicitAny": true, 11 | "noImplicitThis": true, 12 | "noUnusedLocals": true, 13 | "strictNullChecks": true, 14 | "noImplicitReturns": true, 15 | "noUnusedParameters": true, 16 | "allowUnusedLabels": false, 17 | "allowUnreachableCode": false, 18 | "noFallthroughCasesInSwitch": true, 19 | 20 | "target": "es5", 21 | "lib": [ "es6", "dom" ], 22 | "module": "commonjs", 23 | "moduleResolution": "node", 24 | 25 | "sourceMap": true, 26 | "inlineSourceMap": false, 27 | "inlineSources": false 28 | }, 29 | "exclude": [ 30 | "**/node_modules/**" 31 | ] 32 | } -------------------------------------------------------------------------------- /Source/SmallBasic.Client/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-consecutive-blank-lines": true, 4 | "eofline": true, 5 | "no-var-keyword": true, 6 | "triple-equals": true, 7 | "typedef": [ 8 | true, 9 | "call-signature", 10 | "parameter", 11 | "property-declaration", 12 | "member-variable-declaration", 13 | "object-destructuring", 14 | "array-destructuring" 15 | ], 16 | "member-access": [ 17 | true, 18 | "check-constructor", 19 | "check-accessor" 20 | ], 21 | "semicolon": [ 22 | true, 23 | "always" 24 | ], 25 | "quotemark": [ 26 | true, 27 | "double" 28 | ], 29 | "indent": [ 30 | true, 31 | "spaces", 32 | 4 33 | ], 34 | "trailing-comma": [ 35 | true, 36 | { 37 | "multiline": "never", 38 | "singleline": "never" 39 | } 40 | ], 41 | "file-header": [ 42 | true, 43 | "Licensed under the MIT License\\. See LICENSE file in the project root for license information\\.", 44 | "Licensed under the MIT License. See LICENSE file in the project root for license information." 45 | ] 46 | } 47 | } -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/Binding/BaseBoundNode.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Compiler.Binding 6 | { 7 | using System.Collections.Generic; 8 | using System.Diagnostics; 9 | using SmallBasic.Utilities; 10 | 11 | internal abstract class BaseBoundNode 12 | { 13 | private BaseBoundNode parent; 14 | 15 | public BaseBoundNode Parent 16 | { 17 | get => this.parent; 18 | set 19 | { 20 | Debug.Assert(this.parent.IsDefault(), "Parent node is already set."); 21 | this.parent = value; 22 | } 23 | } 24 | 25 | public abstract IEnumerable Children { get; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/Binding/Visitors/GoToUndefinedLabelChecker.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Compiler.Binding 6 | { 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using SmallBasic.Compiler.Diagnostics; 10 | 11 | internal sealed class GoToUndefinedLabelChecker : BaseBoundNodeVisitor 12 | { 13 | private readonly DiagnosticBag diagnostics; 14 | private readonly IReadOnlyCollection labels = new HashSet(); 15 | 16 | public GoToUndefinedLabelChecker(DiagnosticBag diagnostics, IReadOnlyCollection labels, BoundStatementBlock module) 17 | { 18 | this.diagnostics = diagnostics; 19 | this.labels = labels; 20 | this.Visit(module); 21 | } 22 | 23 | private protected override void VisitGoToStatement(BoundGoToStatement node) 24 | { 25 | if (!this.labels.Contains(node.Label)) 26 | { 27 | this.diagnostics.ReportGoToUndefinedLabel(node.Syntax.LabelToken.Range, node.Label); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/Binding/Visitors/LabelDefinitionsCollector.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Compiler.Binding 6 | { 7 | using System.Collections.Generic; 8 | using SmallBasic.Compiler.Diagnostics; 9 | using SmallBasic.Compiler.Parsing; 10 | using SmallBasic.Utilities; 11 | 12 | internal sealed class LabelDefinitionsCollector : BaseBoundNodeVisitor 13 | { 14 | private readonly DiagnosticBag diagnostics; 15 | private readonly HashSet labels = new HashSet(); 16 | 17 | public LabelDefinitionsCollector(DiagnosticBag diagnostics, BoundStatementBlock module) 18 | { 19 | this.diagnostics = diagnostics; 20 | this.Visit(module); 21 | } 22 | 23 | public IReadOnlyCollection Labels => this.labels; 24 | 25 | private protected override void VisitLabelStatement(BoundLabelStatement node) 26 | { 27 | if (!this.labels.Add(node.Label)) 28 | { 29 | this.diagnostics.ReportTwoLabelsWithTheSameName(node.Syntax.LabelToken.Range, node.Label); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/Binding/Visitors/RuntimeAnalysis.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Compiler.Binding 6 | { 7 | using System.Diagnostics; 8 | using System.Linq; 9 | using SmallBasic.Compiler.Runtime; 10 | 11 | public sealed class RuntimeAnalysis : BaseBoundNodeVisitor 12 | { 13 | internal RuntimeAnalysis(SmallBasicCompilation compilation) 14 | { 15 | Debug.Assert(!compilation.Diagnostics.Any(), "Cannot analyze a compilation with errors."); 16 | this.Compilation = compilation; 17 | 18 | this.Visit(this.Compilation.MainModule); 19 | foreach (var subModule in this.Compilation.SubModules.Values) 20 | { 21 | this.Visit(subModule); 22 | } 23 | 24 | if (!this.UsesGraphicsWindow) 25 | { 26 | this.UsesTextWindow = true; 27 | } 28 | } 29 | 30 | public SmallBasicCompilation Compilation { get; private set; } 31 | 32 | public bool UsesTextWindow { get; private set; } 33 | 34 | public bool UsesGraphicsWindow { get; private set; } 35 | 36 | public bool ListensToEvents { get; private set; } 37 | 38 | private protected override void VisitLibraryTypeExpression(BoundLibraryTypeExpression node) 39 | { 40 | base.VisitLibraryTypeExpression(node); 41 | 42 | var library = Libraries.Types[node.Name]; 43 | this.UsesTextWindow |= library.UsesTextWindow; 44 | this.UsesGraphicsWindow |= library.UsesGraphicsWindow; 45 | } 46 | 47 | private protected override void VisitEventAssignmentStatement(BoundEventAssignmentStatement node) 48 | { 49 | base.VisitEventAssignmentStatement(node); 50 | 51 | this.ListensToEvents = true; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/Binding/Visitors/VariablesAndSubModulesCollector.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Compiler.Binding 6 | { 7 | using System.Collections.Generic; 8 | 9 | internal sealed class VariablesAndSubModulesCollector : BaseBoundNodeVisitor 10 | { 11 | private readonly HashSet names = new HashSet(); 12 | 13 | public VariablesAndSubModulesCollector(Binder binder) 14 | { 15 | this.Visit(binder.MainModule); 16 | foreach (var subModule in binder.SubModules.Values) 17 | { 18 | this.Visit(subModule); 19 | } 20 | } 21 | 22 | public IReadOnlyCollection Names => this.names; 23 | 24 | private protected override void VisitArrayAssignmentStatement(BoundArrayAssignmentStatement node) 25 | { 26 | this.names.Add(node.Array.Name); 27 | base.VisitArrayAssignmentStatement(node); 28 | } 29 | 30 | private protected override void VisitVariableAssignmentStatement(BoundVariableAssignmentStatement node) 31 | { 32 | this.names.Add(node.Variable.Name); 33 | base.VisitVariableAssignmentStatement(node); 34 | } 35 | 36 | private protected override void VisitSubModule(BoundSubModule node) 37 | { 38 | this.names.Add(node.Name); 39 | base.VisitSubModule(node); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/Diagnostics/Diagnostic.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Compiler.Diagnostics 6 | { 7 | using System.Collections.Generic; 8 | using System.Diagnostics; 9 | using System.Globalization; 10 | using SmallBasic.Compiler.Scanning; 11 | 12 | [DebuggerDisplay("{ToDisplayString()}")] 13 | public sealed class Diagnostic 14 | { 15 | private string[] args; 16 | 17 | public Diagnostic(DiagnosticCode code, TextRange range, params string[] args) 18 | { 19 | this.Code = code; 20 | this.Range = range; 21 | this.args = args; 22 | } 23 | 24 | public DiagnosticCode Code { get; private set; } 25 | 26 | public TextRange Range { get; private set; } 27 | 28 | public IReadOnlyList Args => this.args; 29 | 30 | public string ToDisplayString() => string.Format(CultureInfo.CurrentCulture, this.Code.ToDisplayString(), this.args); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/Parsing/BaseSyntaxNode.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Compiler.Parsing 6 | { 7 | using System.Collections.Generic; 8 | using System.Diagnostics; 9 | using SmallBasic.Compiler.Scanning; 10 | using SmallBasic.Utilities; 11 | 12 | internal abstract class BaseSyntaxNode 13 | { 14 | private BaseSyntaxNode parent; 15 | 16 | public BaseSyntaxNode Parent 17 | { 18 | get => this.parent; 19 | set 20 | { 21 | Debug.Assert(this.parent.IsDefault(), "Parent node is already set."); 22 | this.parent = value; 23 | } 24 | } 25 | 26 | public abstract IEnumerable Children { get; } 27 | 28 | public abstract TextRange Range { get; } 29 | 30 | public BaseSyntaxNode FindNodeAt(TextPosition position) 31 | { 32 | if (!this.Range.Contains(position)) 33 | { 34 | return null; 35 | } 36 | 37 | foreach (var child in this.Children) 38 | { 39 | var result = child.FindNodeAt(position); 40 | if (!result.IsDefault()) 41 | { 42 | return result; 43 | } 44 | } 45 | 46 | return this; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/Parsing/Visitors/SubModuleNamesCollector.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Compiler.Parsing 6 | { 7 | using System.Collections.Generic; 8 | using SmallBasic.Compiler.Diagnostics; 9 | 10 | internal sealed class SubModuleNamesCollector : BaseSyntaxNodeVisitor 11 | { 12 | private readonly HashSet names = new HashSet(); 13 | 14 | public SubModuleNamesCollector(StatementBlockSyntax syntaxTree) 15 | { 16 | this.Visit(syntaxTree); 17 | } 18 | 19 | public IReadOnlyCollection Names => this.names; 20 | 21 | private protected override void VisitSubModuleStatement(SubModuleStatementSyntax node) 22 | { 23 | this.names.Add(node.NameToken.Text); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/Runtime/DebuggerSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Compiler.Runtime 6 | { 7 | using System.Collections.Generic; 8 | 9 | public sealed class DebuggerSnapshot 10 | { 11 | internal DebuggerSnapshot(int currentSourceLine, IReadOnlyCollection executionStack, IReadOnlyDictionary memory) 12 | { 13 | this.CurrentSourceLine = currentSourceLine; 14 | this.ExecutionStack = executionStack; 15 | this.Memory = memory; 16 | } 17 | 18 | public int CurrentSourceLine { get; set; } 19 | 20 | public IReadOnlyCollection ExecutionStack { get; private set; } 21 | 22 | public IReadOnlyDictionary Memory { get; private set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/Runtime/Frame.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Compiler.Runtime 6 | { 7 | using System.Diagnostics; 8 | 9 | public sealed class Frame 10 | { 11 | private int index = 0; 12 | 13 | internal Frame(RuntimeModule module) 14 | { 15 | this.Module = module; 16 | } 17 | 18 | public RuntimeModule Module { get; private set; } 19 | 20 | public int InstructionIndex 21 | { 22 | get 23 | { 24 | return this.index; 25 | } 26 | 27 | internal set 28 | { 29 | Debug.Assert(value >= 0 && value <= this.Module.Instructions.Count, "Value should be within the module length"); 30 | this.index = value; 31 | } 32 | } 33 | 34 | public int CurrentSourceLine 35 | { 36 | get 37 | { 38 | if (this.index < this.Module.Instructions.Count) 39 | { 40 | return this.Module.Instructions[this.index].Range.Start.Line; 41 | } 42 | else 43 | { 44 | return this.Module.Syntax.Range.End.Line; 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/Runtime/Instructions/ChangingStateInstructions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Compiler.Runtime 6 | { 7 | using SmallBasic.Compiler.Scanning; 8 | 9 | internal sealed class PauseInstruction : BaseNonJumpInstruction 10 | { 11 | public PauseInstruction(TextRange range) 12 | : base(range) 13 | { 14 | } 15 | 16 | protected override void Execute(SmallBasicEngine engine) => engine.Pause(); 17 | } 18 | 19 | internal sealed class TerminateInstruction : BaseNonJumpInstruction 20 | { 21 | public TerminateInstruction(TextRange range) 22 | : base(range) 23 | { 24 | } 25 | 26 | protected override void Execute(SmallBasicEngine engine) => engine.Terminate(); 27 | } 28 | 29 | internal sealed class BlockOnStringInputInstruction : BaseNonJumpInstruction 30 | { 31 | public BlockOnStringInputInstruction(TextRange range) 32 | : base(range) 33 | { 34 | } 35 | 36 | protected override void Execute(SmallBasicEngine engine) => engine.BlockOnStringInput(); 37 | } 38 | 39 | internal sealed class BlockOnNumberInputInstruction : BaseNonJumpInstruction 40 | { 41 | public BlockOnNumberInputInstruction(TextRange range) 42 | : base(range) 43 | { 44 | } 45 | 46 | protected override void Execute(SmallBasicEngine engine) => engine.BlockOnNumberInput(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/Runtime/RuntimeModule.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Compiler.Runtime 6 | { 7 | using System.Collections.Generic; 8 | using SmallBasic.Compiler.Parsing; 9 | 10 | public sealed class RuntimeModule 11 | { 12 | internal RuntimeModule(string name, IReadOnlyList instructions, StatementBlockSyntax syntax) 13 | { 14 | this.Name = name; 15 | this.Instructions = instructions; 16 | this.Syntax = syntax; 17 | } 18 | 19 | public string Name { get; private set; } 20 | 21 | internal IReadOnlyList Instructions { get; private set; } 22 | 23 | internal StatementBlockSyntax Syntax { get; private set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/Runtime/Values/BaseValue.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Compiler.Runtime 6 | { 7 | using System.Diagnostics; 8 | 9 | [DebuggerDisplay("{ToDisplayString()}")] 10 | public abstract class BaseValue 11 | { 12 | public abstract string ToDisplayString(); 13 | 14 | public sealed override string ToString() => this.ToDisplayString(); 15 | 16 | internal abstract bool ToBoolean(); 17 | 18 | internal abstract decimal ToNumber(); 19 | 20 | internal abstract ArrayValue ToArray(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/Runtime/Values/BooleanValue.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Compiler.Runtime 6 | { 7 | public sealed class BooleanValue : BaseValue 8 | { 9 | public BooleanValue(bool value) 10 | { 11 | this.Value = value; 12 | } 13 | 14 | public bool Value { get; private set; } 15 | 16 | public override string ToDisplayString() => this.Value ? "True" : "False"; 17 | 18 | internal override bool ToBoolean() => this.Value; 19 | 20 | internal override decimal ToNumber() => 0; 21 | 22 | internal override ArrayValue ToArray() => new ArrayValue(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/Runtime/Values/NumberValue.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Compiler.Runtime 6 | { 7 | using System.Globalization; 8 | 9 | public sealed class NumberValue : BaseValue 10 | { 11 | public NumberValue(decimal value) 12 | { 13 | this.Value = value; 14 | } 15 | 16 | public decimal Value { get; private set; } 17 | 18 | public override string ToDisplayString() => this.Value.ToString(CultureInfo.CurrentCulture); 19 | 20 | internal override bool ToBoolean() => false; 21 | 22 | internal override decimal ToNumber() => this.Value; 23 | 24 | internal override ArrayValue ToArray() => new ArrayValue(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/Runtime/Values/StringValue.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Compiler.Runtime 6 | { 7 | using System.Diagnostics; 8 | using System.Globalization; 9 | using SmallBasic.Utilities; 10 | 11 | public sealed class StringValue : BaseValue 12 | { 13 | /* 14 | * Note: We use a a number style slightly different from the default 'NumberStyles.Number' 15 | * because for parity with SBD, we don't allow thousands separators. 16 | */ 17 | private const NumberStyles NumberStyle = NumberStyles.Integer | NumberStyles.AllowTrailingSign | NumberStyles.AllowDecimalPoint; 18 | 19 | private StringValue(string value) 20 | { 21 | Debug.Assert(!value.IsDefault(), "Value should never be null."); 22 | this.Value = value; 23 | } 24 | 25 | public string Value { get; private set; } 26 | 27 | internal static StringValue Empty => new StringValue(string.Empty); 28 | 29 | public static BaseValue Create(string value) 30 | { 31 | switch (value.Trim().ToLower(CultureInfo.CurrentCulture)) 32 | { 33 | case "true": 34 | return new BooleanValue(true); 35 | case "false": 36 | return new BooleanValue(false); 37 | case string other when decimal.TryParse(other, NumberStyle, NumberFormatInfo.CurrentInfo, out decimal decimalResult): 38 | return new NumberValue(decimalResult); 39 | default: 40 | return new StringValue(value); 41 | } 42 | } 43 | 44 | public override string ToDisplayString() => this.Value; 45 | 46 | internal override bool ToBoolean() => false; 47 | 48 | internal override decimal ToNumber() => 0; 49 | 50 | internal override ArrayValue ToArray() => new ArrayValue(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/Scanning/TextPosition.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Compiler.Scanning 6 | { 7 | using System; 8 | using System.Diagnostics; 9 | using SmallBasic.Compiler.Services; 10 | 11 | [DebuggerDisplay("{ToDisplayString()}")] 12 | public readonly struct TextPosition : IEquatable 13 | { 14 | internal TextPosition(int line, int column) 15 | { 16 | this.Line = line; 17 | this.Column = column; 18 | } 19 | 20 | public int Line { get; } 21 | 22 | public int Column { get; } 23 | 24 | public static implicit operator TextPosition(in (int Line, int Column) tuple) 25 | { 26 | return new TextPosition(tuple.Line, tuple.Column); 27 | } 28 | 29 | public static bool operator ==(TextPosition left, TextPosition right) => left.Line == right.Line && left.Column == right.Column; 30 | 31 | public static bool operator !=(TextPosition left, TextPosition right) => !(left == right); 32 | 33 | public static bool operator <(TextPosition left, TextPosition right) => left.Line < right.Line || (left.Line == right.Line && left.Column < right.Column); 34 | 35 | public static bool operator >(TextPosition left, TextPosition right) => left.Line > right.Line || (left.Line == right.Line && left.Column > right.Column); 36 | 37 | public static bool operator <=(TextPosition left, TextPosition right) => left < right || left == right; 38 | 39 | public static bool operator >=(TextPosition left, TextPosition right) => left > right || left == right; 40 | 41 | public override bool Equals(object obj) => obj is TextPosition other && this == other; 42 | 43 | public override int GetHashCode() => this.Line ^ this.Column; 44 | 45 | public bool Equals(TextPosition other) => this == other; 46 | 47 | public string ToDisplayString() => $"({this.Line}, {this.Column})"; 48 | 49 | public MonacoPosition ToMonacoPosition() => new MonacoPosition 50 | { 51 | lineNumber = this.Line + 1, 52 | column = this.Column + 1 53 | }; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/Scanning/TextRange.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Compiler.Scanning 6 | { 7 | using System; 8 | using System.Diagnostics; 9 | using SmallBasic.Compiler.Services; 10 | 11 | [DebuggerDisplay("{ToDisplayString()}")] 12 | public readonly struct TextRange : IEquatable 13 | { 14 | internal TextRange(TextPosition start, TextPosition end) 15 | { 16 | this.Start = start; 17 | this.End = end; 18 | } 19 | 20 | public TextPosition Start { get; } 21 | 22 | public TextPosition End { get; } 23 | 24 | public static implicit operator TextRange(in (TextPosition Start, TextPosition End) tuple) 25 | { 26 | return new TextRange(tuple.Start, tuple.End); 27 | } 28 | 29 | public static bool operator ==(TextRange left, TextRange right) => left.Start == right.Start && left.End == right.End; 30 | 31 | public static bool operator !=(TextRange left, TextRange right) => !(left == right); 32 | 33 | public override bool Equals(object obj) => obj is TextRange other && this == other; 34 | 35 | public override int GetHashCode() => this.Start.GetHashCode() ^ this.End.GetHashCode(); 36 | 37 | public bool Equals(TextRange other) => this == other; 38 | 39 | public string ToDisplayString() => $"({this.Start.ToDisplayString()}, {this.End.ToDisplayString()})"; 40 | 41 | public bool Contains(in TextPosition position) => this.Start <= position && position <= this.End; 42 | 43 | public MonacoRange ToMonacoRange() => new MonacoRange 44 | { 45 | startLineNumber = this.Start.Line + 1, 46 | startColumn = this.Start.Column + 1, 47 | endLineNumber = this.End.Line + 1, 48 | endColumn = this.End.Column + 2 49 | }; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/Scanning/Token.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Compiler.Scanning 6 | { 7 | using System.Diagnostics; 8 | 9 | [DebuggerDisplay("{ToDisplayString()}")] 10 | internal sealed class Token 11 | { 12 | public Token(TokenKind kind, string text, TextRange range) 13 | { 14 | Debug.Assert(range.Start.Line == range.End.Line, "Tokens should never span multiple lines"); 15 | 16 | this.Kind = kind; 17 | this.Text = text; 18 | this.Range = range; 19 | } 20 | 21 | public TokenKind Kind { get; private set; } 22 | 23 | public string Text { get; private set; } 24 | 25 | public TextRange Range { get; private set; } 26 | 27 | public string ToDisplayString() => $"{nameof(TokenKind)}.{this.Kind}: '{this.Text}' at {this.Range.ToDisplayString()}"; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/SmallBasic.Compiler.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Source/SmallBasic.Compiler/SmallBasicCompilation.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Compiler 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using SmallBasic.Compiler.Binding; 11 | using SmallBasic.Compiler.Diagnostics; 12 | using SmallBasic.Compiler.Parsing; 13 | using SmallBasic.Compiler.Scanning; 14 | using SmallBasic.Compiler.Services; 15 | using SmallBasic.Utilities; 16 | 17 | public sealed class SmallBasicCompilation 18 | { 19 | private readonly DiagnosticBag diagnostics; 20 | private readonly bool isRunningOnDesktop; 21 | 22 | private readonly Scanner scanner; 23 | private readonly Parser parser; 24 | private readonly Binder binder; 25 | 26 | private readonly Lazy lazyAnalysis; 27 | 28 | public SmallBasicCompilation(string text) 29 | #if IsBuildingForDesktop 30 | : this(text, isRunningOnDesktop: true) 31 | #else 32 | : this(text, isRunningOnDesktop: false) 33 | #endif 34 | { 35 | } 36 | 37 | public SmallBasicCompilation(string text, bool isRunningOnDesktop) 38 | { 39 | this.diagnostics = new DiagnosticBag(); 40 | this.isRunningOnDesktop = isRunningOnDesktop; 41 | 42 | this.Text = text; 43 | 44 | this.scanner = new Scanner(this.Text, this.diagnostics); 45 | this.parser = new Parser(this.scanner.Tokens, this.diagnostics); 46 | this.binder = new Binder(this.parser.SyntaxTree, this.diagnostics, isRunningOnDesktop); 47 | 48 | this.lazyAnalysis = new Lazy(() => new RuntimeAnalysis(this)); 49 | } 50 | 51 | public string Text { get; private set; } 52 | 53 | public RuntimeAnalysis Analysis => this.lazyAnalysis.Value; 54 | 55 | public IReadOnlyList Diagnostics => this.diagnostics.Contents; 56 | 57 | internal BoundStatementBlock MainModule => this.binder.MainModule; 58 | 59 | internal IReadOnlyDictionary SubModules => this.binder.SubModules; 60 | 61 | public MonacoCompletionItem[] ProvideCompletionItems(TextPosition position) => CompletionItemProvider.Provide(this.parser, this.binder, position); 62 | 63 | public string[] ProvideHover(TextPosition position) => HoverProvider.Provide(this.diagnostics, this.parser, position); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Components/Display/EngineDisplay.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Components.Display 6 | { 7 | using System.Collections.Generic; 8 | using Microsoft.AspNetCore.Blazor.Components; 9 | using SmallBasic.Editor.Components.Layout; 10 | using SmallBasic.Editor.Libraries.Utilities; 11 | using SmallBasic.Editor.Store; 12 | 13 | public sealed class EngineDisplay : SmallBasicComponent 14 | { 15 | [Parameter] 16 | private AsyncEngine Engine { get; set; } 17 | 18 | internal static void Inject(TreeComposer composer, AsyncEngine engine) 19 | { 20 | composer.Inject(new Dictionary 21 | { 22 | { nameof(EngineDisplay.Engine), engine } 23 | }); 24 | } 25 | 26 | protected override void ComposeTree(TreeComposer composer) 27 | { 28 | composer.Element( 29 | name: "engine-display", 30 | attributes: new Dictionary 31 | { 32 | // Important to prevent the right-click menu 33 | { "oncontextmenu", "return false;" } 34 | }, 35 | body: () => 36 | { 37 | if (CompilationStore.Compilation.Analysis.UsesTextWindow) 38 | { 39 | TextDisplay.Inject(composer); 40 | } 41 | 42 | if (CompilationStore.Compilation.Analysis.UsesGraphicsWindow) 43 | { 44 | GraphicsDisplay.Inject(composer, this.Engine.Libraries); 45 | } 46 | }); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Components/Display/EngineDisplay.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | */ 4 | 5 | engine-display { 6 | display: flex; 7 | flex-direction: row; 8 | justify-content: flex-start; 9 | align-items: stretch; 10 | align-content: stretch; 11 | height: 100%; 12 | width: 100%; 13 | } 14 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Components/Display/GraphicsDisplay.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | */ 4 | 5 | graphics-display { 6 | flex: 1 0 50%; 7 | background-color: white; 8 | overflow-x: hidden; 9 | overflow-y: hidden; 10 | border: 1px solid #656A72; 11 | // Required to have "absolute" positioned children 12 | position: relative; 13 | 14 | title { 15 | display: block; 16 | padding: 4px 8px; 17 | background: #F3F3F3; 18 | border-bottom: 1px solid #656A72; 19 | font-family: Roboto, sans-serif; 20 | font-size: 16px; 21 | } 22 | 23 | svg { 24 | // Needs to have a "relative" parent 25 | position: absolute; 26 | } 27 | 28 | button { 29 | // Needs to have a "relative" parent 30 | position: absolute; 31 | font-family: Consolas, monospace, Hack; 32 | font-size: 14px; 33 | } 34 | 35 | input { 36 | // Needs to have a "relative" parent 37 | position: absolute; 38 | font-family: Consolas, monospace, Hack; 39 | font-size: 14px; 40 | } 41 | 42 | textarea { 43 | // Needs to have a "relative" parent 44 | position: absolute; 45 | font-family: Consolas, monospace, Hack; 46 | font-size: 14px; 47 | resize: none; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Components/Display/TextDisplay.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | */ 4 | 5 | text-display { 6 | flex: 1 0 50%; 7 | padding: 5px; 8 | box-sizing: border-box; 9 | overflow-x: hidden; 10 | overflow-y: auto; 11 | word-break: break-all; 12 | font-size: 18px; 13 | font-family: Consolas, monospace, Hack; 14 | border: 1px solid #656A72; 15 | white-space: pre; 16 | 17 | title { 18 | display: block; 19 | margin: -5px -5px 5px -5px; 20 | padding: 4px 8px; 21 | background: #F3F3F3; 22 | border-bottom: 1px solid #656A72; 23 | font-family: Roboto, sans-serif; 24 | font-size: 16px; 25 | } 26 | 27 | input-field { 28 | color: gray; 29 | display: inline; 30 | 31 | cursor { 32 | animation: blink 0.7s infinite; 33 | } 34 | 35 | @keyframes blink { 36 | 0% { 37 | opacity: 0; 38 | } 39 | 40 | 100% { 41 | opacity: 1; 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Components/Layout/App.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Components.Layout 6 | { 7 | using System.Collections.Generic; 8 | using Microsoft.AspNetCore.Blazor.Components; 9 | using Microsoft.AspNetCore.Blazor.Routing; 10 | 11 | // Configuring this here is temporary. Later we'll move the app config 12 | // into Program.cs, and it won't be necessary to specify AppAssembly. 13 | public sealed class App : SmallBasicComponent 14 | { 15 | protected override void ComposeTree(TreeComposer composer) 16 | { 17 | composer.Inject(new Dictionary 18 | { 19 | { "AppAssembly", RuntimeHelpers.TypeCheck(typeof(App).Assembly) } 20 | }); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Components/Layout/MainLayout.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | */ 4 | 5 | main-layout { 6 | display: flex; 7 | flex-direction: column; 8 | color: #232528; 9 | font-family: Roboto, sans-serif; 10 | height: 100%; 11 | 12 | $header-row-height: 100px; 13 | $actions-row-height: 98px; 14 | 15 | header-row { 16 | display: flex; 17 | flex-shrink: 0; 18 | flex-direction: row; 19 | justify-content: space-between; 20 | align-items: center; 21 | height: $header-row-height; 22 | background: #F3F3F3; 23 | 24 | logo-area { 25 | display: flex; 26 | align-items: center; 27 | 28 | logo { 29 | @include set-background-image("logo.svg", 224px, 80px); 30 | margin-left: 40px; 31 | } 32 | } 33 | 34 | header-links { 35 | header-link { 36 | font-size: 20px; 37 | margin-right: 40px; 38 | cursor: pointer; 39 | } 40 | } 41 | } 42 | 43 | actions-row { 44 | height: $actions-row-height; 45 | flex-shrink: 0; 46 | } 47 | 48 | page-contents { 49 | height: calc(100% - #{$header-row-height} - #{$actions-row-height}); 50 | min-height: 600px; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Components/Layout/SmallBasicComponent.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Components.Layout 6 | { 7 | using Microsoft.AspNetCore.Blazor.Components; 8 | using Microsoft.AspNetCore.Blazor.RenderTree; 9 | 10 | public abstract class SmallBasicComponent : BlazorComponent 11 | { 12 | protected abstract void ComposeTree(TreeComposer composer); 13 | 14 | protected override sealed void BuildRenderTree(RenderTreeBuilder builder) 15 | { 16 | base.BuildRenderTree(builder); 17 | this.ComposeTree(new TreeComposer(builder)); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Components/Pages/Debug/DebugPage.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | */ 4 | 5 | debug-page { 6 | display: flex; 7 | flex-direction: row; 8 | justify-content: flex-start; 9 | align-items: stretch; 10 | height: 100%; 11 | 12 | main-space { 13 | width: 100%; 14 | height: 100%; 15 | display: flex; 16 | flex-direction: column; 17 | align-items: stretch; 18 | justify-content: flex-start; 19 | flex-grow: 1; 20 | 21 | editor-space { 22 | width: 100%; 23 | height: 300px; 24 | flex-grow: 1; 25 | display: flex; 26 | flex-direction: column; 27 | align-items: stretch; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Components/Pages/Edit/EditPage.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | */ 4 | 5 | edit-page { 6 | display: flex; 7 | flex-direction: row; 8 | justify-content: flex-start; 9 | align-items: stretch; 10 | height: 100%; 11 | 12 | main-space { 13 | width: 100%; 14 | height: 100%; 15 | display: flex; 16 | flex-direction: column; 17 | align-items: stretch; 18 | justify-content: flex-start; 19 | flex-grow: 1; 20 | 21 | editor-space { 22 | width: 100%; 23 | flex-grow: 1; 24 | display: flex; 25 | flex-direction: column; 26 | align-items: stretch; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Components/Pages/IndexPage.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Components.Pages.Edit 6 | { 7 | using System; 8 | using Microsoft.AspNetCore.Blazor.Components; 9 | using Microsoft.AspNetCore.Blazor.Services; 10 | using SmallBasic.Editor.Components.Layout; 11 | using SmallBasic.Editor.Components.Pages.Debug; 12 | using SmallBasic.Editor.Components.Pages.Run; 13 | using SmallBasic.Editor.Store; 14 | using SmallBasic.Utilities; 15 | 16 | [Route("/")] // For browser entry 17 | [Route("/index.html")] // For electron entry 18 | public sealed class IndexPage : SmallBasicComponent, IDisposable 19 | { 20 | private IUriHelper UriHelper { get; set; } 21 | 22 | public void Dispose() 23 | { 24 | NavigationStore.PageChanged -= this.StateHasChanged; 25 | } 26 | 27 | protected override void OnInit() 28 | { 29 | NavigationStore.PageChanged += this.StateHasChanged; 30 | } 31 | 32 | protected override void ComposeTree(TreeComposer composer) 33 | { 34 | switch (NavigationStore.CurrentPage) 35 | { 36 | case NavigationStore.PageId.Edit: 37 | EditPage.Inject(composer); 38 | break; 39 | case NavigationStore.PageId.Run: 40 | RunPage.Inject(composer); 41 | break; 42 | case NavigationStore.PageId.Debug: 43 | DebugPage.Inject(composer); 44 | break; 45 | default: 46 | throw ExceptionUtilities.UnexpectedValue(NavigationStore.CurrentPage); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Components/Pages/Run/RunPage.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Components.Pages.Run 6 | { 7 | using System; 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | using SmallBasic.Compiler; 11 | using SmallBasic.Editor.Components.Display; 12 | using SmallBasic.Editor.Components.Layout; 13 | using SmallBasic.Editor.Components.Toolbox; 14 | using SmallBasic.Editor.Libraries; 15 | using SmallBasic.Editor.Libraries.Utilities; 16 | using SmallBasic.Editor.Store; 17 | using SmallBasic.Utilities; 18 | using SmallBasic.Utilities.Resources; 19 | 20 | public sealed class RunPage : MainLayout, IDisposable 21 | { 22 | private readonly AsyncEngine engine = new AsyncEngine(isDebugging: false); 23 | 24 | private bool isInitialized; 25 | 26 | public static void Inject(TreeComposer composer) 27 | { 28 | composer.Inject(); 29 | } 30 | 31 | public void Dispose() 32 | { 33 | this.engine.Dispose(); 34 | } 35 | 36 | protected override void OnInit() 37 | { 38 | if (CompilationStore.Compilation.Diagnostics.Any()) 39 | { 40 | NavigationStore.NagivateTo(NavigationStore.PageId.Edit); 41 | return; 42 | } 43 | } 44 | 45 | protected override void ComposeBody(TreeComposer composer) 46 | { 47 | composer.Element("run-page", body: () => 48 | { 49 | EngineDisplay.Inject(composer, this.engine); 50 | }); 51 | } 52 | 53 | protected override void ComposeLeftActions(TreeComposer composer) 54 | { 55 | Actions.Action(composer, "back", EditorResources.Actions_Back, () => 56 | { 57 | NavigationStore.NagivateTo(NavigationStore.PageId.Edit); 58 | return Task.CompletedTask; 59 | }); 60 | } 61 | 62 | protected override async Task OnAfterRenderAsync() 63 | { 64 | if (!this.isInitialized) 65 | { 66 | this.isInitialized = true; 67 | await Task.Run(() => this.engine.StartLoop()).ConfigureAwait(false); 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Components/Pages/Run/RunPage.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | */ 4 | 5 | run-page { 6 | display: flex; 7 | flex-direction: row; 8 | justify-content: flex-start; 9 | align-items: stretch; 10 | height: 100%; 11 | } -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Components/Toolbox/ActionsRow.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Components.Toolbox 6 | { 7 | using System; 8 | using System.Threading.Tasks; 9 | 10 | internal static class Actions 11 | { 12 | public static void Row(TreeComposer composer, Action left = null, Action right = null) 13 | { 14 | composer.Element("actions-row", body: () => 15 | { 16 | composer.Element("left-actions", body: left); 17 | composer.Element("right-actions", body: right); 18 | }); 19 | } 20 | 21 | public static void Action(TreeComposer composer, string name, string title, Func onClick) 22 | { 23 | composer.Element( 24 | name: "action", 25 | events: new TreeComposer.Events 26 | { 27 | OnClickAsync = args => onClick() 28 | }, 29 | body: () => 30 | { 31 | composer.Element("icon-" + name); 32 | composer.Text(title); 33 | }); 34 | } 35 | 36 | public static void DisabledAction(TreeComposer composer, string name, string title, string message) 37 | { 38 | composer.Element("disabled-action-container", body: () => 39 | { 40 | composer.Element("action", body: () => 41 | { 42 | composer.Element("icon-" + name); 43 | composer.Text(title); 44 | }); 45 | 46 | composer.Element("disabled-message", body: () => composer.Text(message)); 47 | }); 48 | } 49 | 50 | public static void Separator(TreeComposer composer) 51 | { 52 | composer.Element("separator"); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Components/Toolbox/Micro.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Components.Toolbox 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Threading.Tasks; 10 | using Microsoft.AspNetCore.Blazor; 11 | 12 | internal static class Micro 13 | { 14 | public static void FontAwesome(TreeComposer composer, string iconName) 15 | { 16 | composer.Element(name: "font-awesome-icon", attributes: new Dictionary 17 | { 18 | { "class", $"fas fa-{iconName}" } 19 | }); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Components/Toolbox/MonacoEditor.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Components.Toolbox 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Threading.Tasks; 10 | using Microsoft.AspNetCore.Blazor; 11 | using Microsoft.AspNetCore.Blazor.Components; 12 | using SmallBasic.Editor.Components.Layout; 13 | using SmallBasic.Editor.Interop; 14 | using SmallBasic.Editor.Store; 15 | 16 | public sealed class MonacoEditor : SmallBasicComponent, IDisposable 17 | { 18 | private ElementRef editorElement = default; 19 | 20 | [Parameter] 21 | private bool IsReadOnly { get; set; } 22 | 23 | public static void Inject(TreeComposer composer, bool isReadOnly) 24 | { 25 | composer.Inject(new Dictionary 26 | { 27 | { nameof(MonacoEditor.IsReadOnly), isReadOnly }, 28 | }); 29 | } 30 | 31 | public void Dispose() 32 | { 33 | JSInterop.Monaco.Dispose().ConfigureAwait(false); 34 | } 35 | 36 | protected override Task OnAfterRenderAsync() 37 | { 38 | return JSInterop.Monaco.Initialize(this.editorElement, CompilationStore.Compilation.Text, this.IsReadOnly); 39 | } 40 | 41 | protected override void ComposeTree(TreeComposer composer) 42 | { 43 | composer.Element("editor-container", body: () => 44 | { 45 | composer.Element("editor", capture: element => this.editorElement = element); 46 | }); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Components/Toolbox/MonacoEditor.scss: -------------------------------------------------------------------------------- 1 | editor-container { 2 | padding-top: 20px; 3 | padding-bottom: 20px; 4 | box-sizing: border-box; 5 | flex-grow: 1; 6 | // absolute position is required to layout the editor correctly. 7 | // This container needs to not be attached to it's parent width, 8 | // So that its width can shrink/grow accourding to the page. 9 | position: absolute; 10 | 11 | editor { 12 | 13 | .monaco-editor-hover .monaco-tokenized-source { 14 | word-break: normal; 15 | } 16 | 17 | .wavy-line { 18 | display: inline-block; 19 | position: relative; 20 | background: url(data:image/gif;base64,R0lGODdhBAADAPEAANv///8AAP///wAAACwAAAAABAADAEACBZQjmIAFADs=) bottom repeat-x; 21 | } 22 | 23 | .error-line-glyph { 24 | margin-left: 14px; 25 | @include set-background-image("EditPage/Error.svg", 20px, 20px); 26 | } 27 | 28 | .debugger-line-highlight { 29 | background: #FEF849; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Interop/GraphicsDisplayInterop.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Interop 6 | { 7 | using System.Threading.Tasks; 8 | using SmallBasic.Editor.Store; 9 | 10 | public class GraphicsDisplayInterop : IGraphicsDisplayInterop 11 | { 12 | public Task UpdateDisplayLocation(decimal x, decimal y) 13 | { 14 | GraphicsDisplayStore.UpdateDisplayLocation(x, y); 15 | return Task.CompletedTask; 16 | } 17 | 18 | public Task OnKeyUp(string key) 19 | { 20 | GraphicsDisplayStore.NotifyKeyUp(key); 21 | return Task.CompletedTask; 22 | } 23 | 24 | public Task OnKeyDown(string key) 25 | { 26 | GraphicsDisplayStore.NotifyKeyDown(key); 27 | return Task.CompletedTask; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Interop/MonacoInterop.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Interop 6 | { 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using SmallBasic.Compiler.Services; 10 | using SmallBasic.Editor.Components; 11 | using SmallBasic.Editor.Store; 12 | 13 | public class MonacoInterop : IMonacoInterop 14 | { 15 | public Task UpdateDiagnostics(string code) 16 | { 17 | CompilationStore.NotifyCodeChanged(code); 18 | return Task.FromResult(CompilationStore.Compilation.Diagnostics.Select(d => d.Range.ToMonacoRange()).ToArray()); 19 | } 20 | 21 | public Task ProvideCompletionItems(string code, MonacoPosition position) 22 | { 23 | return Task.FromResult(CompilationStore.Compilation.ProvideCompletionItems(position.ToCompilerPosition())); 24 | } 25 | 26 | public Task ProvideHover(string code, MonacoPosition position) 27 | { 28 | return Task.FromResult(CompilationStore.Compilation.ProvideHover(position.ToCompilerPosition())); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/ArrayLibrary.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries 6 | { 7 | using System.Collections.Generic; 8 | using System.Globalization; 9 | using System.Linq; 10 | using SmallBasic.Compiler.Runtime; 11 | 12 | internal sealed class ArrayLibrary : IArrayLibrary 13 | { 14 | private readonly Dictionary arrays = new Dictionary(); 15 | 16 | public bool ContainsIndex(ArrayValue array, string index) => array.ContainsKey(index); 17 | 18 | public bool ContainsValue(ArrayValue array, string value) => array.Values.Any(existing => existing.ToString() == value); 19 | 20 | public ArrayValue GetAllIndices(ArrayValue array) 21 | { 22 | int i = 1; 23 | return new ArrayValue(array.Keys.Select(k => StringValue.Create(k)).ToDictionary(value => (i++).ToString(CultureInfo.CurrentCulture))); 24 | } 25 | 26 | public decimal GetItemCount(ArrayValue array) => array.Count; 27 | 28 | public BaseValue GetValue(string arrayName, string index) 29 | { 30 | if (this.arrays.TryGetValue(arrayName, out ArrayValue array) && array.TryGetValue(index, out BaseValue value)) 31 | { 32 | return value; 33 | } 34 | else 35 | { 36 | return StringValue.Create(string.Empty); 37 | } 38 | } 39 | 40 | public bool IsArray(BaseValue array) => array is ArrayValue; 41 | 42 | public void RemoveValue(string arrayName, string index) 43 | { 44 | if (this.arrays.TryGetValue(arrayName, out ArrayValue array) && array.ContainsKey(index)) 45 | { 46 | array.RemoveIndex(index); 47 | this.arrays[arrayName] = array; 48 | } 49 | } 50 | 51 | public void SetValue(string arrayName, string index, BaseValue value) 52 | { 53 | if (this.arrays.TryGetValue(arrayName, out ArrayValue array)) 54 | { 55 | array.SetIndex(index, value); 56 | this.arrays[arrayName] = array; 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/ClockLibrary.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries 6 | { 7 | using System; 8 | using System.Globalization; 9 | using SmallBasic.Compiler.Runtime; 10 | 11 | internal sealed class ClockLibrary : IClockLibrary 12 | { 13 | public string Get_Date() => DateTime.Now.ToString(DateTimeFormatInfo.GetInstance(CultureInfo.CurrentCulture).ShortDatePattern, CultureInfo.CurrentCulture); 14 | 15 | public decimal Get_Day() => DateTime.Now.Day; 16 | 17 | public decimal Get_ElapsedMilliseconds() => (decimal)(DateTime.Now - new DateTime(1900, 1, 1)).TotalMilliseconds; 18 | 19 | public decimal Get_Hour() => DateTime.Now.Hour; 20 | 21 | public decimal Get_Millisecond() => DateTime.Now.Millisecond; 22 | 23 | public decimal Get_Minute() => DateTime.Now.Minute; 24 | 25 | public decimal Get_Month() => DateTime.Now.Month; 26 | 27 | public decimal Get_Second() => DateTime.Now.Second; 28 | 29 | public string Get_Time() => DateTime.Now.ToString(DateTimeFormatInfo.GetInstance(CultureInfo.CurrentCulture).LongTimePattern, CultureInfo.CurrentCulture); 30 | 31 | public string Get_WeekDay() => DateTime.Now.ToString("dddd", CultureInfo.CurrentCulture); 32 | 33 | public decimal Get_Year() => DateTime.Now.Year; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/Controls/BaseControl.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries.Controls 6 | { 7 | using System.Collections.Generic; 8 | using SmallBasic.Editor.Components; 9 | 10 | internal abstract class BaseControl 11 | { 12 | protected BaseControl(string name, decimal left, decimal top, decimal width, decimal height) 13 | { 14 | this.Name = name; 15 | this.Left = left; 16 | this.Top = top; 17 | this.Width = width; 18 | this.Height = height; 19 | this.Visible = true; 20 | } 21 | 22 | public string Name { get; set; } 23 | 24 | public decimal Left { get; set; } 25 | 26 | public decimal Top { get; set; } 27 | 28 | public decimal Width { get; set; } 29 | 30 | public decimal Height { get; set; } 31 | 32 | public bool Visible { get; set; } 33 | 34 | protected IReadOnlyDictionary Styles => new Dictionary 35 | { 36 | { "left", $"{this.Left}px" }, 37 | { "top", $"{this.Top}px" }, 38 | { "width", $"{this.Width}px" }, 39 | { "height", $"{this.Height}px" }, 40 | { "visibility", this.Visible ? "visible" : "hidden" } 41 | }; 42 | 43 | public abstract void ComposeTree(ControlsLibrary library, TreeComposer composer); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/Controls/ButtonControl.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries.Controls 6 | { 7 | using SmallBasic.Editor.Components; 8 | 9 | internal sealed class ButtonControl : BaseControl 10 | { 11 | public ButtonControl(string name, string caption, decimal left, decimal top, decimal width, decimal height) 12 | : base(name, left, top, width, height) 13 | { 14 | this.Caption = caption; 15 | } 16 | 17 | public string Caption { get; set; } 18 | 19 | public override void ComposeTree(ControlsLibrary library, TreeComposer composer) 20 | { 21 | composer.Element( 22 | name: "button", 23 | body: () => composer.Text(this.Caption), 24 | events: new TreeComposer.Events 25 | { 26 | OnClick = args => library.NotifyButtonClicked(this.Name) 27 | }, 28 | styles: this.Styles); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/Controls/MultilineTextBoxControl.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries.Controls 6 | { 7 | using System.Collections.Generic; 8 | using SmallBasic.Editor.Components; 9 | 10 | internal sealed class MultilineTextBoxControl : BaseControl 11 | { 12 | public MultilineTextBoxControl(string name, decimal left, decimal top, decimal width, decimal height) 13 | : base(name, left, top, width, height) 14 | { 15 | this.Text = string.Empty; 16 | } 17 | 18 | public string Text { get; set; } 19 | 20 | public override void ComposeTree(ControlsLibrary library, TreeComposer composer) 21 | { 22 | composer.Element( 23 | name: "textarea", 24 | styles: this.Styles, 25 | events: new TreeComposer.Events 26 | { 27 | OnInput = args => 28 | { 29 | this.Text = args.Value.ToString(); 30 | library.NotifyTextTyped(this.Name); 31 | } 32 | }, 33 | attributes: new Dictionary() 34 | { 35 | { "value", this.Text }, 36 | }); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/Controls/TextBoxControl.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries.Controls 6 | { 7 | using System.Collections.Generic; 8 | using SmallBasic.Editor.Components; 9 | 10 | internal sealed class TextBoxControl : BaseControl 11 | { 12 | public TextBoxControl(string name, decimal left, decimal top, decimal width, decimal height) 13 | : base(name, left, top, width, height) 14 | { 15 | this.Text = string.Empty; 16 | } 17 | 18 | public string Text { get; set; } 19 | 20 | public override void ComposeTree(ControlsLibrary library, TreeComposer composer) 21 | { 22 | composer.Element( 23 | name: "input", 24 | styles: this.Styles, 25 | events: new TreeComposer.Events 26 | { 27 | OnInput = args => 28 | { 29 | this.Text = args.Value.ToString(); 30 | library.NotifyTextTyped(this.Name); 31 | } 32 | }, 33 | attributes: new Dictionary() 34 | { 35 | { "type", "text" }, 36 | { "value", this.Text }, 37 | }); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/DesktopLibrary.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries 6 | { 7 | using SmallBasic.Compiler.Runtime; 8 | 9 | internal sealed class DesktopLibrary : IDesktopLibrary 10 | { 11 | // All members are deprecated 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/DictionaryLibrary.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries 6 | { 7 | using SmallBasic.Compiler.Runtime; 8 | 9 | internal sealed class DictionaryLibrary : IDictionaryLibrary 10 | { 11 | // All members are deprecated 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/FlickrLibrary.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries 6 | { 7 | using SmallBasic.Compiler.Runtime; 8 | 9 | internal sealed class FlickrLibrary : IFlickrLibrary 10 | { 11 | // All members are deprecated 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/Graphics/BaseGraphicsObject.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries.Graphics 6 | { 7 | using SmallBasic.Editor.Components; 8 | using SmallBasic.Editor.Libraries.Utilities; 9 | 10 | internal abstract class BaseGraphicsObject 11 | { 12 | protected BaseGraphicsObject(GraphicsWindowStyles styles) 13 | { 14 | this.Styles = styles; 15 | } 16 | 17 | public GraphicsWindowStyles Styles { get; set; } 18 | 19 | public abstract void ComposeTree(TreeComposer composer); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/Graphics/EllipseGraphicsObject.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries.Graphics 6 | { 7 | using System.Collections.Generic; 8 | using System.Globalization; 9 | using SmallBasic.Editor.Components; 10 | using SmallBasic.Editor.Libraries.Utilities; 11 | 12 | internal sealed class EllipseGraphicsObject : BaseGraphicsObject 13 | { 14 | public EllipseGraphicsObject(decimal x, decimal y, decimal width, decimal height, GraphicsWindowStyles styles) 15 | : base(styles) 16 | { 17 | this.X = x + width; 18 | this.Y = y + height; 19 | this.Width = width; 20 | this.Height = height; 21 | } 22 | 23 | public decimal X { get; set; } 24 | 25 | public decimal Y { get; set; } 26 | 27 | public decimal Width { get; set; } 28 | 29 | public decimal Height { get; set; } 30 | 31 | public override void ComposeTree(TreeComposer composer) 32 | { 33 | composer.Element( 34 | name: "ellipse", 35 | styles: new Dictionary 36 | { 37 | { "fill", this.Styles.BrushColor }, 38 | { "stroke", this.Styles.PenColor }, 39 | { "stroke-width", $"{this.Styles.PenWidth}px" }, 40 | }, 41 | attributes: new Dictionary 42 | { 43 | { "cx", (this.X - (this.Width / 2)).ToString(CultureInfo.CurrentCulture) }, 44 | { "cy", (this.Y - (this.Height / 2)).ToString(CultureInfo.CurrentCulture) }, 45 | { "rx", (this.Width / 2).ToString(CultureInfo.CurrentCulture) }, 46 | { "ry", (this.Height / 2).ToString(CultureInfo.CurrentCulture) }, 47 | }); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/Graphics/ImageGraphicsObject.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries.Graphics 6 | { 7 | using System.Collections.Generic; 8 | using System.Globalization; 9 | using SmallBasic.Editor.Components; 10 | using SmallBasic.Editor.Libraries.Utilities; 11 | 12 | internal sealed class ImageGraphicsObject : BaseGraphicsObject 13 | { 14 | public ImageGraphicsObject(decimal x, decimal y, decimal scaleX, decimal scaleY, string name, GraphicsWindowStyles styles) 15 | : base(styles) 16 | { 17 | this.X = x; 18 | this.Y = y; 19 | this.ScaleX = scaleX; 20 | this.ScaleY = scaleY; 21 | this.Name = name; 22 | } 23 | 24 | public decimal X { get; set; } 25 | 26 | public decimal Y { get; set; } 27 | 28 | public decimal ScaleX { get; set; } 29 | 30 | public decimal ScaleY { get; set; } 31 | 32 | public string Name { get; set; } 33 | 34 | public override void ComposeTree(TreeComposer composer) 35 | { 36 | composer.Element( 37 | name: "image", 38 | attributes: new Dictionary 39 | { 40 | { "href", this.Name }, 41 | { "x", this.X.ToString(CultureInfo.CurrentCulture) }, 42 | { "y", this.Y.ToString(CultureInfo.CurrentCulture) }, 43 | { "transform", $"scale({this.ScaleX}, {this.ScaleY})" } 44 | }); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/Graphics/RectangleGraphicsObject.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries.Graphics 6 | { 7 | using System.Collections.Generic; 8 | using System.Globalization; 9 | using SmallBasic.Editor.Components; 10 | using SmallBasic.Editor.Libraries.Utilities; 11 | 12 | internal sealed class RectangleGraphicsObject : BaseGraphicsObject 13 | { 14 | public RectangleGraphicsObject(decimal x, decimal y, decimal width, decimal height, GraphicsWindowStyles styles) 15 | : base(styles) 16 | { 17 | this.X = x; 18 | this.Y = y; 19 | this.Width = width; 20 | this.Height = height; 21 | } 22 | 23 | public decimal X { get; set; } 24 | 25 | public decimal Y { get; set; } 26 | 27 | public decimal Width { get; set; } 28 | 29 | public decimal Height { get; set; } 30 | 31 | public override void ComposeTree(TreeComposer composer) 32 | { 33 | composer.Element( 34 | name: "rect", 35 | styles: new Dictionary 36 | { 37 | { "fill", this.Styles.BrushColor }, 38 | { "stroke", this.Styles.PenColor }, 39 | { "stroke-width", $"{this.Styles.PenWidth}px" }, 40 | }, 41 | attributes: new Dictionary 42 | { 43 | { "x", this.X.ToString(CultureInfo.CurrentCulture) }, 44 | { "y", this.Y.ToString(CultureInfo.CurrentCulture) }, 45 | { "width", this.Width.ToString(CultureInfo.CurrentCulture) }, 46 | { "height", this.Height.ToString(CultureInfo.CurrentCulture) }, 47 | }); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/Graphics/TextGraphicsObject.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries.Graphics 6 | { 7 | using System.Collections.Generic; 8 | using System.Globalization; 9 | using SmallBasic.Editor.Components; 10 | using SmallBasic.Editor.Libraries.Utilities; 11 | 12 | internal sealed class TextGraphicsObject : BaseGraphicsObject 13 | { 14 | public TextGraphicsObject(decimal x, decimal y, string text, decimal? width, GraphicsWindowStyles styles) 15 | : base(styles) 16 | { 17 | this.X = x; 18 | this.Y = y; 19 | this.Text = text; 20 | this.Width = width; 21 | } 22 | 23 | public decimal X { get; set; } 24 | 25 | public decimal Y { get; set; } 26 | 27 | public string Text { get; set; } 28 | 29 | public decimal? Width { get; set; } 30 | 31 | public override void ComposeTree(TreeComposer composer) 32 | { 33 | var attributes = new Dictionary 34 | { 35 | { "x", this.X.ToString(CultureInfo.CurrentCulture) }, 36 | { "y", this.Y.ToString(CultureInfo.CurrentCulture) }, 37 | { "dy", this.Styles.FontSize.ToString(CultureInfo.CurrentCulture) } 38 | }; 39 | 40 | if (this.Width.HasValue) 41 | { 42 | attributes.Add("textLength", this.Width.Value.ToString(CultureInfo.CurrentCulture)); 43 | } 44 | 45 | composer.Element( 46 | name: "text", 47 | styles: new Dictionary 48 | { 49 | { "fill", this.Styles.BrushColor }, 50 | { "font-weight", this.Styles.FontBold ? "bold" : "normal" }, 51 | { "font-style", this.Styles.FontItalic ? "italic" : "normal" }, 52 | { "font-family", $@"""{this.Styles.FontName}""" }, 53 | { "font-size", $"{this.Styles.FontSize}px" }, 54 | { "pointer-events", "none" } 55 | }, 56 | attributes: attributes, 57 | body: () => composer.Text(this.Text)); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/Graphics/TriangleGraphicsObject.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries.Graphics 6 | { 7 | using System.Collections.Generic; 8 | using SmallBasic.Editor.Components; 9 | using SmallBasic.Editor.Libraries.Utilities; 10 | 11 | internal sealed class TriangleGraphicsObject : BaseGraphicsObject 12 | { 13 | public TriangleGraphicsObject(decimal x1, decimal y1, decimal x2, decimal y2, decimal x3, decimal y3, GraphicsWindowStyles styles) 14 | : base(styles) 15 | { 16 | this.X1 = x1; 17 | this.Y1 = y1; 18 | this.X2 = x2; 19 | this.Y2 = y2; 20 | this.X3 = x3; 21 | this.Y3 = y3; 22 | } 23 | 24 | public decimal X1 { get; set; } 25 | 26 | public decimal Y1 { get; set; } 27 | 28 | public decimal X2 { get; set; } 29 | 30 | public decimal Y2 { get; set; } 31 | 32 | public decimal X3 { get; set; } 33 | 34 | public decimal Y3 { get; set; } 35 | 36 | public override void ComposeTree(TreeComposer composer) 37 | { 38 | composer.Element( 39 | name: "polygon", 40 | styles: new Dictionary 41 | { 42 | { "fill", this.Styles.BrushColor }, 43 | { "stroke", this.Styles.PenColor }, 44 | { "stroke-width", $"{this.Styles.PenWidth}px" }, 45 | }, 46 | attributes: new Dictionary 47 | { 48 | { "points", string.Join(",", this.X1, this.Y1, this.X2, this.Y2, this.X3, this.Y3) } 49 | }); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/Graphics/TurtleGraphicsObject.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries.Graphics 6 | { 7 | using System.Collections.Generic; 8 | using System.Globalization; 9 | using SmallBasic.Editor.Components; 10 | using SmallBasic.Editor.Libraries.Utilities; 11 | 12 | internal sealed class TurtleGraphicsObject : BaseGraphicsObject 13 | { 14 | public TurtleGraphicsObject(GraphicsWindowStyles styles) 15 | : base(styles) 16 | { 17 | } 18 | 19 | public static decimal Width => 48; 20 | 21 | public static decimal Height => 61; 22 | 23 | public override void ComposeTree(TreeComposer composer) 24 | { 25 | composer.Element( 26 | name: "image", 27 | attributes: new Dictionary 28 | { 29 | { "href", $"Turtle.svg" }, 30 | /* width and height attributes required on Firefox */ 31 | { "width", Width.ToString(CultureInfo.CurrentCulture) }, 32 | { "height", Height.ToString(CultureInfo.CurrentCulture) } 33 | }); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/ImageListLibrary.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Globalization; 10 | using System.Threading.Tasks; 11 | using SmallBasic.Compiler.Runtime; 12 | using SmallBasic.Editor.Components; 13 | using SmallBasic.Editor.Libraries.Utilities; 14 | using SmallBasic.Utilities.Bridge; 15 | 16 | internal sealed class ImageListLibrary : IImageListLibrary 17 | { 18 | private readonly LibrariesCollection libraries; 19 | private readonly NamedCounter counter = new NamedCounter(); 20 | private readonly Dictionary images = new Dictionary(); 21 | 22 | public ImageListLibrary(LibrariesCollection libraries) 23 | { 24 | this.libraries = libraries; 25 | } 26 | 27 | public decimal GetHeightOfImage(string imageName) 28 | { 29 | if (this.images.TryGetValue(imageName, out ImageListBridgeModels.ImageData image)) 30 | { 31 | return image.Height; 32 | } 33 | 34 | return 0; 35 | } 36 | 37 | public decimal GetWidthOfImage(string imageName) 38 | { 39 | if (this.images.TryGetValue(imageName, out ImageListBridgeModels.ImageData image)) 40 | { 41 | return image.Width; 42 | } 43 | 44 | return 0; 45 | } 46 | 47 | public async Task LoadImage(string fileNameOrUrl) 48 | { 49 | var name = this.counter.GetNext("ImageList"); 50 | var data = await Bridge.Network.LoadImage(fileNameOrUrl).ConfigureAwait(false); 51 | this.images.Add(name, data); 52 | return name; 53 | } 54 | 55 | internal void EmbedImages(TreeComposer composer) 56 | { 57 | composer.Element(name: "defs", body: () => 58 | { 59 | foreach (var pair in this.images) 60 | { 61 | composer.Element("image", attributes: new Dictionary 62 | { 63 | { "id", pair.Key }, 64 | { "width", pair.Value.Width.ToString(CultureInfo.CurrentCulture) }, 65 | { "height", pair.Value.Height.ToString(CultureInfo.CurrentCulture) }, 66 | { "href", $"data:image/image;base64,{pair.Value.Base64Contents}" } 67 | }); 68 | } 69 | }); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/MathLibrary.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries 6 | { 7 | using System; 8 | using SmallBasic.Compiler.Runtime; 9 | 10 | internal sealed class MathLibrary : IMathLibrary 11 | { 12 | private static readonly Random Random = new Random((int)DateTime.Now.Ticks); 13 | 14 | public decimal Get_Pi() => (decimal)Math.PI; 15 | 16 | public decimal Abs(decimal number) => Math.Abs(number); 17 | 18 | public decimal ArcCos(decimal cosValue) => (decimal)Math.Acos((double)cosValue); 19 | 20 | public decimal ArcSin(decimal sinValue) => (decimal)Math.Asin((double)sinValue); 21 | 22 | public decimal ArcTan(decimal tanValue) => (decimal)Math.Atan((double)tanValue); 23 | 24 | public decimal Ceiling(decimal number) => Math.Ceiling(number); 25 | 26 | public decimal Cos(decimal angle) => (decimal)Math.Cos((double)angle); 27 | 28 | public decimal Floor(decimal number) => Math.Floor(number); 29 | 30 | public decimal GetDegrees(decimal angle) => (180 * angle / (decimal)Math.PI) % 360; 31 | 32 | public decimal GetRadians(decimal angle) => (angle % 360) * (decimal)Math.PI / 180; 33 | 34 | public decimal GetRandomNumber(decimal maxNumber) => Random.Next((int)Math.Max(1, maxNumber)) + 1; 35 | 36 | public decimal Log(decimal number) => (decimal)Math.Log10((double)number); 37 | 38 | public decimal Max(decimal number1, decimal number2) => Math.Max(number1, number2); 39 | 40 | public decimal Min(decimal number1, decimal number2) => Math.Min(number1, number2); 41 | 42 | public decimal NaturalLog(decimal number) => (decimal)Math.Log((double)number); 43 | 44 | public decimal Power(decimal baseNumber, decimal exponent) => (decimal)Math.Pow((double)baseNumber, (double)exponent); 45 | 46 | public decimal Remainder(decimal dividend, decimal divisor) => divisor == 0 ? 0 : (dividend % divisor); 47 | 48 | public decimal Round(decimal number) => Math.Round(number); 49 | 50 | public decimal Sin(decimal angle) => (decimal)Math.Sin((double)angle); 51 | 52 | public decimal SquareRoot(decimal number) => (decimal)Math.Sqrt((double)number); 53 | 54 | public decimal Tan(decimal angle) => (decimal)Math.Tan((double)angle); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/NetworkLibrary.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries 6 | { 7 | using System.Threading.Tasks; 8 | using SmallBasic.Compiler.Runtime; 9 | 10 | internal sealed class NetworkLibrary : INetworkLibrary 11 | { 12 | public Task DownloadFile(string url) 13 | => Bridge.Network.DownloadFile(url); 14 | 15 | public Task GetWebPageContents(string url) 16 | => Bridge.Network.GetWebPageContents(url); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/ProgramLibrary.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries 6 | { 7 | using System; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using SmallBasic.Compiler.Runtime; 11 | using SmallBasic.Editor.Store; 12 | 13 | internal sealed class ProgramLibrary : IProgramLibrary 14 | { 15 | public Task Delay(decimal milliSeconds) 16 | { 17 | // Update display if needed 18 | GraphicsDisplayStore.UpdateDisplay(); 19 | 20 | return Task.Delay((int)milliSeconds); 21 | } 22 | 23 | public void End() => throw new InvalidOperationException("This should have been removed in binding."); 24 | 25 | public void Pause() => throw new InvalidOperationException("This should have been removed in binding."); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/Shapes/EllipseShape.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries.Shapes 6 | { 7 | using SmallBasic.Editor.Libraries.Graphics; 8 | using SmallBasic.Editor.Libraries.Utilities; 9 | 10 | internal sealed class EllipseShape : BaseShape 11 | { 12 | public EllipseShape(decimal width, decimal height, GraphicsWindowStyles styles) 13 | : base(new EllipseGraphicsObject(x: 0, y: 0, width, height, styles)) 14 | { 15 | } 16 | 17 | public override decimal Height => this.Graphics.Height; 18 | 19 | public override decimal Width => this.Graphics.Width; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/Shapes/ImageShape.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries.Shapes 6 | { 7 | using System; 8 | using SmallBasic.Editor.Libraries.Graphics; 9 | using SmallBasic.Editor.Libraries.Utilities; 10 | 11 | internal sealed class ImageShape : BaseShape 12 | { 13 | private readonly decimal width; 14 | private readonly decimal height; 15 | 16 | public ImageShape(string imageName, decimal width, decimal height, GraphicsWindowStyles styles) 17 | : base(new ImageGraphicsObject(x: 0, y: 0, scaleX: 1, scaleY: 1, imageName, styles)) 18 | { 19 | this.width = width; 20 | this.height = height; 21 | } 22 | 23 | public override decimal Height => this.height; 24 | 25 | public override decimal Width => this.width; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/Shapes/LineShape.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries.Shapes 6 | { 7 | using System; 8 | using SmallBasic.Editor.Libraries.Graphics; 9 | using SmallBasic.Editor.Libraries.Utilities; 10 | 11 | internal sealed class LineShape : BaseShape 12 | { 13 | public LineShape(decimal x1, decimal y1, decimal x2, decimal y2, GraphicsWindowStyles styles) 14 | : base(new LineGraphicsObject(x1, y1, x2, y2, styles)) 15 | { 16 | } 17 | 18 | public override decimal Height => Math.Abs(this.Graphics.Y1 - this.Graphics.Y2); 19 | 20 | public override decimal Width => Math.Abs(this.Graphics.X1 - this.Graphics.X2); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/Shapes/RectangleShape.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries.Shapes 6 | { 7 | using SmallBasic.Editor.Libraries.Graphics; 8 | using SmallBasic.Editor.Libraries.Utilities; 9 | 10 | internal sealed class RectangleShape : BaseShape 11 | { 12 | public RectangleShape(decimal width, decimal height, GraphicsWindowStyles styles) 13 | : base(new RectangleGraphicsObject(x: 0, y: 0, width, height, styles)) 14 | { 15 | } 16 | 17 | public override decimal Height => this.Graphics.Height; 18 | 19 | public override decimal Width => this.Graphics.Width; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/Shapes/TextShape.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries.Shapes 6 | { 7 | using SmallBasic.Editor.Libraries.Graphics; 8 | using SmallBasic.Editor.Libraries.Utilities; 9 | 10 | internal sealed class TextShape : BaseShape 11 | { 12 | public TextShape(string text, GraphicsWindowStyles styles) 13 | : base(new TextGraphicsObject(x: 0, y: 0, text, width: default, styles)) 14 | { 15 | } 16 | 17 | public override decimal Height => this.Graphics.Styles.FontSize; 18 | 19 | public override decimal Width => this.Graphics.Width ?? 0; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/Shapes/TriangleShape.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries.Shapes 6 | { 7 | using System; 8 | using SmallBasic.Editor.Libraries.Graphics; 9 | using SmallBasic.Editor.Libraries.Utilities; 10 | 11 | internal sealed class TriangleShape : BaseShape 12 | { 13 | public TriangleShape(decimal x1, decimal y1, decimal x2, decimal y2, decimal x3, decimal y3, GraphicsWindowStyles styles) 14 | : base(new TriangleGraphicsObject(x1, y1, x2, y2, x3, y3, styles)) 15 | { 16 | } 17 | 18 | public override decimal Height => Math.Max(this.Graphics.Y1, Math.Max(this.Graphics.Y2, this.Graphics.Y3)) - Math.Min(this.Graphics.Y1, Math.Min(this.Graphics.Y2, this.Graphics.Y3)); 19 | 20 | public override decimal Width => Math.Max(this.Graphics.X1, Math.Max(this.Graphics.X2, this.Graphics.X3)) - Math.Min(this.Graphics.X1, Math.Min(this.Graphics.X2, this.Graphics.X3)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/Shapes/TurtleShape.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries.Shapes 6 | { 7 | using SmallBasic.Editor.Libraries.Graphics; 8 | using SmallBasic.Editor.Libraries.Utilities; 9 | 10 | internal sealed class TurtleShape : BaseShape 11 | { 12 | public TurtleShape(GraphicsWindowStyles styles) 13 | : base(new TurtleGraphicsObject(styles)) 14 | { 15 | } 16 | 17 | public override decimal Height => TurtleGraphicsObject.Height; 18 | 19 | public override decimal Width => TurtleGraphicsObject.Width; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/SoundLibrary.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries 6 | { 7 | using SmallBasic.Compiler.Runtime; 8 | 9 | internal sealed class SoundLibrary : ISoundLibrary 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/StackLibrary.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries 6 | { 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using SmallBasic.Compiler.Runtime; 10 | 11 | internal sealed class StackLibrary : IStackLibrary 12 | { 13 | private readonly Dictionary> stacks = new Dictionary>(); 14 | 15 | public decimal GetCount(string stackName) 16 | { 17 | if (this.stacks.TryGetValue(stackName, out Stack stack)) 18 | { 19 | return stack.Count; 20 | } 21 | 22 | return 0; 23 | } 24 | 25 | public string PopValue(string stackName) 26 | { 27 | if (this.stacks.TryGetValue(stackName, out Stack stack) && stack.Any()) 28 | { 29 | return stack.Pop(); 30 | } 31 | 32 | return string.Empty; 33 | } 34 | 35 | public void PushValue(string stackName, string value) 36 | { 37 | if (!this.stacks.ContainsKey(stackName)) 38 | { 39 | this.stacks.Add(stackName, new Stack()); 40 | } 41 | 42 | this.stacks[stackName].Push(value); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/TextLibrary.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries 6 | { 7 | using System; 8 | using System.Globalization; 9 | using System.Linq; 10 | using SmallBasic.Compiler.Runtime; 11 | 12 | public sealed class TextLibrary : ITextLibrary 13 | { 14 | public string Append(string text1, string text2) => text1 + text2; 15 | 16 | public string ConvertToLowerCase(string text) => text.ToLower(CultureInfo.CurrentCulture); 17 | 18 | public string ConvertToUpperCase(string text) => text.ToUpper(CultureInfo.CurrentCulture); 19 | 20 | public bool EndsWith(string text, string subText) => text.EndsWith(subText, StringComparison.CurrentCulture); 21 | 22 | public string GetCharacter(decimal characterCode) => ((char)characterCode).ToString(CultureInfo.CurrentCulture); 23 | 24 | public decimal GetCharacterCode(string character) => character.Any() ? (decimal)character[0] : 0; 25 | 26 | public decimal GetIndexOf(string text, string subText) => text.IndexOf(subText, StringComparison.CurrentCulture) + 1; 27 | 28 | public decimal GetLength(string text) => text.Length; 29 | 30 | public string GetSubText(string text, decimal start, decimal length) 31 | { 32 | start--; 33 | 34 | if (start < 0 || start >= text.Length || length < 1) 35 | { 36 | return string.Empty; 37 | } 38 | 39 | length = Math.Min(length, text.Length - start); 40 | return text.Substring((int)start, (int)length); 41 | } 42 | 43 | public string GetSubTextToEnd(string text, decimal start) 44 | { 45 | start--; 46 | if (start < 0 || start >= text.Length) 47 | { 48 | return string.Empty; 49 | } 50 | 51 | return text.Substring((int)start); 52 | } 53 | 54 | public bool IsSubText(string text, string subText) => text.Contains(subText); 55 | 56 | public bool StartsWith(string text, string subText) => text.StartsWith(subText, StringComparison.CurrentCulture); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/TimerLibrary.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries 6 | { 7 | using System; 8 | using System.Threading; 9 | using SmallBasic.Compiler.Runtime; 10 | using SmallBasic.Utilities; 11 | 12 | internal sealed class TimerLibrary : ITimerLibrary, IDisposable 13 | { 14 | private const int MaxInterval = 100000000; 15 | 16 | private readonly Timer timer; 17 | 18 | private int interval; 19 | 20 | public TimerLibrary() 21 | { 22 | this.timer = new Timer((object state) => 23 | { 24 | if (!this.Tick.IsDefault()) 25 | { 26 | this.Tick(); 27 | } 28 | }); 29 | 30 | this.interval = MaxInterval; 31 | this.Pause(); 32 | } 33 | 34 | public event Action Tick; 35 | 36 | public decimal Get_Interval() => this.interval; 37 | 38 | public void Set_Interval(decimal value) 39 | { 40 | this.interval = Math.Max(Math.Min((int)value, MaxInterval), 10); 41 | this.timer.Change(this.interval, this.interval); 42 | } 43 | 44 | public void Pause() 45 | { 46 | this.timer.Change(Timeout.Infinite, Timeout.Infinite); 47 | } 48 | 49 | public void Resume() 50 | { 51 | this.timer.Change(this.interval, this.interval); 52 | } 53 | 54 | public void Dispose() 55 | { 56 | this.timer.Dispose(); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/Utilities/GraphicsWindowStyles.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries.Utilities 6 | { 7 | internal sealed class GraphicsWindowStyles 8 | { 9 | public GraphicsWindowStyles( 10 | decimal penWidth, 11 | string penColor, 12 | string brushColor, 13 | bool fontBold, 14 | bool fontItalic, 15 | string fontName, 16 | decimal fontSize) 17 | { 18 | this.PenWidth = penWidth; 19 | this.PenColor = penColor; 20 | this.BrushColor = brushColor; 21 | this.FontBold = fontBold; 22 | this.FontItalic = fontItalic; 23 | this.FontName = fontName; 24 | this.FontSize = fontSize; 25 | } 26 | 27 | public decimal PenWidth { get; private set; } 28 | 29 | public string PenColor { get; private set; } 30 | 31 | public string BrushColor { get; private set; } 32 | 33 | public bool FontBold { get; private set; } 34 | 35 | public bool FontItalic { get; private set; } 36 | 37 | public string FontName { get; private set; } 38 | 39 | public decimal FontSize { get; private set; } 40 | 41 | public GraphicsWindowStyles With( 42 | decimal? penWidth = default, 43 | string penColor = default, 44 | string brushColor = default, 45 | bool? fontBold = default, 46 | bool? fontItalic = default, 47 | string fontName = default, 48 | decimal? fontSize = default) 49 | => new GraphicsWindowStyles( 50 | penWidth: penWidth ?? this.PenWidth, 51 | penColor: penColor ?? this.PenColor, 52 | brushColor: brushColor ?? this.BrushColor, 53 | fontBold: fontBold ?? this.FontBold, 54 | fontItalic: fontItalic ?? this.FontItalic, 55 | fontName: fontName ?? this.FontName, 56 | fontSize: fontSize ?? this.FontSize); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Libraries/Utilities/NamedCounter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Libraries.Utilities 6 | { 7 | using System.Collections.Generic; 8 | 9 | public sealed class NamedCounter 10 | { 11 | private readonly Dictionary counters = new Dictionary(); 12 | 13 | public string GetNext(string name) 14 | { 15 | if (!this.counters.TryGetValue(name, out int value)) 16 | { 17 | value = 0; 18 | } 19 | 20 | this.counters[name] = ++value; 21 | return name + value; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Program.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor 6 | { 7 | using System.Globalization; 8 | using Microsoft.AspNetCore.Blazor.Builder; 9 | using Microsoft.AspNetCore.Blazor.Hosting; 10 | using Microsoft.Extensions.DependencyInjection; 11 | using SmallBasic.Editor.Components.Layout; 12 | 13 | public static class Program 14 | { 15 | public static void Main() 16 | { 17 | CultureInfo.CurrentCulture = new CultureInfo("en-US"); 18 | 19 | BlazorWebAssemblyHost 20 | .CreateDefaultBuilder() 21 | .UseBlazorStartup() 22 | .Build() 23 | .Run(); 24 | } 25 | } 26 | 27 | public class Startup 28 | { 29 | #pragma warning disable CA1801, CA1822 30 | public void ConfigureServices(IServiceCollection services) 31 | { 32 | } 33 | 34 | public void Configure(IBlazorApplicationBuilder app) 35 | { 36 | app.AddComponent("app"); 37 | } 38 | #pragma warning restore CA1801, CA1822 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:56177/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SmallBasic.Editor": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "http://localhost:56178/" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/SmallBasic.Editor.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 3.0 6 | 7 | 8 | 9 | dotnet 10 | blazor serve 11 | 12 | false 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | $([System.IO.Path]::GetFullPath($(OutputPath)\dist)) 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Store/CompilationStore.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Store 6 | { 7 | using System; 8 | using SmallBasic.Compiler; 9 | using SmallBasic.Utilities; 10 | 11 | internal static class CompilationStore 12 | { 13 | static CompilationStore() 14 | { 15 | Compilation = new SmallBasicCompilation( 16 | @"' A new Program! 17 | TextWindow.WriteLine(""What is your name?"") 18 | name = TextWindow.Read() 19 | TextWindow.WriteLine(""Hello "" + name + ""!"")"); 20 | } 21 | 22 | public static event Action CodeChanged; 23 | 24 | public static SmallBasicCompilation Compilation { get; private set; } 25 | 26 | public static void NotifyCodeChanged(string code) 27 | { 28 | Compilation = new SmallBasicCompilation(code); 29 | 30 | if (!CodeChanged.IsDefault()) 31 | { 32 | CodeChanged(); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Store/NavigationStore.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Store 6 | { 7 | using System; 8 | 9 | internal static class NavigationStore 10 | { 11 | static NavigationStore() 12 | { 13 | CurrentPage = PageId.Edit; 14 | } 15 | 16 | public static event Action PageChanged; 17 | 18 | internal enum PageId 19 | { 20 | Edit, 21 | Run, 22 | Debug, 23 | } 24 | 25 | public static PageId CurrentPage { get; private set; } 26 | 27 | public static void NagivateTo(PageId page) 28 | { 29 | CurrentPage = page; 30 | PageChanged(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Source/SmallBasic.Editor/Store/TextDisplayStore.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Editor.Store 6 | { 7 | using System.Diagnostics; 8 | using System.Threading.Tasks; 9 | using SmallBasic.Editor.Components.Display; 10 | using SmallBasic.Utilities; 11 | 12 | internal delegate void TextInputEventSignature(string text); 13 | 14 | internal static class TextDisplayStore 15 | { 16 | private static TextDisplay display; 17 | 18 | public static event TextInputEventSignature TextInput; 19 | 20 | public static string Title 21 | { 22 | get 23 | { 24 | if (display.IsDefault()) 25 | { 26 | return string.Empty; 27 | } 28 | 29 | return display.Title; 30 | } 31 | 32 | set 33 | { 34 | if (!display.IsDefault()) 35 | { 36 | display.Title = value; 37 | } 38 | } 39 | } 40 | 41 | public static void SetDisplay(TextDisplay instance) 42 | { 43 | display = instance; 44 | } 45 | 46 | public static void NotifyTextInput(string text) 47 | { 48 | if (!TextInput.IsDefault()) 49 | { 50 | TextInput(text); 51 | } 52 | } 53 | 54 | public static Task AppendOutput(OutputChunk chunk) 55 | { 56 | if (!display.IsDefault()) 57 | { 58 | return display.AppendOutput(chunk); 59 | } 60 | 61 | return Task.CompletedTask; 62 | } 63 | 64 | public static Task SetInputMode(AcceptedInputMode mode) 65 | { 66 | if (!display.IsDefault()) 67 | { 68 | return display.SetInputMode(mode); 69 | } 70 | 71 | return Task.CompletedTask; 72 | } 73 | 74 | public static void Clear() 75 | { 76 | if (!display.IsDefault()) 77 | { 78 | display.Clear(); 79 | } 80 | } 81 | 82 | public static void SetBackgroundColor(string hexColor) 83 | { 84 | if (!display.IsDefault()) 85 | { 86 | display.SetBackgroundColor(hexColor); 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Source/SmallBasic.Generators/BaseConverterTask.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Generators 6 | { 7 | using System; 8 | using System.IO; 9 | using System.Text; 10 | using System.Xml; 11 | using System.Xml.Schema; 12 | using System.Xml.Serialization; 13 | using SmallBasic.Utilities; 14 | 15 | public abstract class BaseConverterTask : BaseTask 16 | { 17 | public abstract int Execute(string inputFile, string outputFile); 18 | } 19 | 20 | public abstract class BaseConverterTask : BaseConverterTask 21 | { 22 | public override sealed int Execute(string inputFile, string outputFile) 23 | { 24 | var settings = new XmlReaderSettings(); 25 | settings.ValidationEventHandler += (sender, args) => 26 | { 27 | switch (args.Severity) 28 | { 29 | case XmlSeverityType.Error: 30 | this.LogError(args.Message); 31 | break; 32 | case XmlSeverityType.Warning: 33 | this.LogError(args.Message); 34 | break; 35 | default: 36 | throw ExceptionUtilities.UnexpectedValue(args.Severity); 37 | } 38 | }; 39 | 40 | using (var stream = new MemoryStream(File.ReadAllBytes(inputFile))) 41 | { 42 | using (var xmlReader = XmlReader.Create(stream, settings)) 43 | { 44 | var serializer = new XmlSerializer(typeof(TModel)); 45 | var model = (TModel)serializer.Deserialize(xmlReader); 46 | 47 | this.GenerateDocHeader(outputFile); 48 | this.Generate(model); 49 | 50 | return this.SaveAndExit(outputFile); 51 | } 52 | } 53 | } 54 | 55 | protected abstract void Generate(TModel model); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Source/SmallBasic.Generators/Binding/Models.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Generators.Binding 6 | { 7 | using System.Collections.Generic; 8 | using System.Xml.Serialization; 9 | 10 | public sealed class Member 11 | { 12 | [XmlAttribute] 13 | public string Name { get; set; } 14 | 15 | [XmlAttribute] 16 | public string Type { get; set; } 17 | 18 | [XmlAttribute] 19 | public bool IsOptional { get; set; } 20 | 21 | [XmlAttribute] 22 | public bool IsList { get; set; } 23 | } 24 | 25 | public sealed class BoundNode 26 | { 27 | [XmlAttribute] 28 | public string Name { get; set; } 29 | 30 | [XmlAttribute] 31 | public string Inherits { get; set; } 32 | 33 | [XmlAttribute] 34 | public bool IsAbstract { get; set; } 35 | 36 | [XmlAttribute] 37 | public string Syntax { get; set; } 38 | 39 | [XmlArray(nameof(Members))] 40 | [XmlArrayItem(typeof(Member))] 41 | public List Members { get; set; } 42 | } 43 | 44 | [XmlRoot("root")] 45 | public sealed class BoundNodeCollection : List 46 | { 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Source/SmallBasic.Generators/Bridge/BridgeTypes.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Source/SmallBasic.Generators/Bridge/Models.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Generators.Bridge 6 | { 7 | using System.Collections.Generic; 8 | using System.Xml.Serialization; 9 | 10 | public sealed class Method 11 | { 12 | [XmlAttribute] 13 | public string Name { get; set; } 14 | 15 | [XmlAttribute] 16 | public string InputType { get; set; } 17 | 18 | [XmlAttribute] 19 | public string InputName { get; set; } 20 | 21 | [XmlAttribute] 22 | public string OutputType { get; set; } 23 | } 24 | 25 | public sealed class BridgeType 26 | { 27 | [XmlAttribute] 28 | public string Name { get; set; } 29 | 30 | [XmlArray(nameof(Methods))] 31 | [XmlArrayItem(typeof(Method))] 32 | public List Methods { get; set; } 33 | } 34 | 35 | [XmlRoot("root")] 36 | public sealed class BridgeTypeCollection : List 37 | { 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Source/SmallBasic.Generators/Diagnostics/GenerateDiagnosticBag.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Generators.Diagnostics 6 | { 7 | using System.Linq; 8 | using SmallBasic.Utilities; 9 | 10 | public sealed class GenerateDiagnosticBag : BaseConverterTask 11 | { 12 | protected override void Generate(DiagnosticsCollection model) 13 | { 14 | this.Line("namespace SmallBasic.Compiler.Diagnostics"); 15 | this.Brace(); 16 | 17 | this.Line("using System.Collections.Generic;"); 18 | this.Line("using SmallBasic.Compiler.Scanning;"); 19 | this.Line("using SmallBasic.Utilities;"); 20 | this.Blank(); 21 | 22 | this.Line("internal sealed class DiagnosticBag"); 23 | this.Brace(); 24 | 25 | this.Line("private readonly List builder = new List();"); 26 | this.Blank(); 27 | 28 | this.Line("public IReadOnlyList Contents => this.builder;"); 29 | this.Blank(); 30 | 31 | foreach (var diagnostic in model) 32 | { 33 | string arguments = diagnostic.Parameters.Select(parameter => $", {parameter.Type} {parameter.Name}").Join(); 34 | this.Line($"public void Report{diagnostic.Name}(TextRange range{arguments})"); 35 | this.Brace(); 36 | 37 | string displayStrings = diagnostic.Parameters.Select(parameter => $", {parameter.Name}.ToDisplayString()").Join(); 38 | this.Line($"this.builder.Add(new Diagnostic(DiagnosticCode.{diagnostic.Name}, range{displayStrings}));"); 39 | this.Unbrace(); 40 | } 41 | 42 | this.Unbrace(); 43 | this.Unbrace(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Source/SmallBasic.Generators/Diagnostics/GenerateDiagnosticCode.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Generators.Diagnostics 6 | { 7 | public sealed class GenerateDiagnosticCode : BaseConverterTask 8 | { 9 | protected override void Generate(DiagnosticsCollection model) 10 | { 11 | this.Line("namespace SmallBasic.Compiler.Diagnostics"); 12 | this.Brace(); 13 | 14 | this.Line("using SmallBasic.Utilities;"); 15 | this.Line("using SmallBasic.Utilities.Resources;"); 16 | this.Blank(); 17 | 18 | this.Line("public enum DiagnosticCode"); 19 | this.Brace(); 20 | 21 | foreach (var diagnostic in model) 22 | { 23 | this.Line($"{diagnostic.Name},"); 24 | } 25 | 26 | this.Unbrace(); 27 | 28 | this.Line("internal static partial class DiagnosticCodeExtensions"); 29 | this.Brace(); 30 | 31 | this.Line("public static string ToDisplayString(this DiagnosticCode kind)"); 32 | this.Brace(); 33 | 34 | this.Line("switch (kind)"); 35 | this.Brace(); 36 | 37 | foreach (var diagnostic in model) 38 | { 39 | this.Line($"case DiagnosticCode.{diagnostic.Name}: return DiagnosticsResources.{diagnostic.Name};"); 40 | } 41 | 42 | this.Line("default: throw ExceptionUtilities.UnexpectedValue(kind);"); 43 | 44 | this.Unbrace(); 45 | this.Unbrace(); 46 | this.Unbrace(); 47 | this.Unbrace(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Source/SmallBasic.Generators/Diagnostics/Models.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Generators.Diagnostics 6 | { 7 | using System.Collections.Generic; 8 | using System.Xml.Serialization; 9 | 10 | public sealed class Parameter 11 | { 12 | [XmlAttribute] 13 | public string Name { get; set; } 14 | 15 | [XmlAttribute] 16 | public string Type { get; set; } 17 | } 18 | 19 | public sealed class Diagnostic 20 | { 21 | [XmlAttribute] 22 | public string Name { get; set; } 23 | 24 | [XmlArray(nameof(Parameters))] 25 | [XmlArrayItem(typeof(Parameter))] 26 | public List Parameters { get; set; } 27 | } 28 | 29 | [XmlRoot("root")] 30 | public sealed class DiagnosticsCollection : List 31 | { 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Source/SmallBasic.Generators/Interop/CSInteropTypes.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Source/SmallBasic.Generators/Interop/GenerateCSClientInterop.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Generators.Interop 6 | { 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using SmallBasic.Utilities; 10 | 11 | public sealed class GenerateCSClientInterop : BaseConverterTask 12 | { 13 | protected override void Generate(InteropTypeCollection model) 14 | { 15 | this.Blank(); 16 | this.Line(@"/// "); 17 | this.Blank(); 18 | 19 | this.Line("export module CSIntrop {"); 20 | this.Indent(); 21 | 22 | foreach (InteropType type in model) 23 | { 24 | this.Line($"export module {type.Name} {{"); 25 | this.Indent(); 26 | 27 | foreach (Method method in type.Methods) 28 | { 29 | this.Line($"export function {method.Name.ToLowerFirstChar()}({method.Parameters.Select(p => $"{p.Name}: {p.Type}").Join(", ")}): Promise<{method.ReturnType ?? "void"}> {{"); 30 | this.Indent(); 31 | 32 | IEnumerable arguments = new string[] 33 | { 34 | @"""SmallBasic.Editor""", 35 | $@"""CSIntrop.{type.Name}.{method.Name}""" 36 | }.Concat(method.Parameters.Select(p => p.Name)); 37 | 38 | if (method.ReturnType.IsDefault()) 39 | { 40 | this.Line($"return DotNet.invokeMethodAsync({arguments.Join(", ")}).then(() => {{"); 41 | this.Indent(); 42 | this.Line("Promise.resolve();"); 43 | this.Unindent(); 44 | this.Line("});"); 45 | } 46 | else 47 | { 48 | this.Line($"return DotNet.invokeMethodAsync<{method.ReturnType}>({arguments.Join(", ")});"); 49 | } 50 | 51 | this.Unbrace(); 52 | } 53 | 54 | this.Unbrace(); 55 | } 56 | 57 | this.Unbrace(); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /Source/SmallBasic.Generators/Interop/GenerateJSEditorInterop.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Generators.Interop 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using SmallBasic.Utilities; 11 | 12 | public sealed class GenerateJSEditorInterop : BaseConverterTask 13 | { 14 | protected override void Generate(InteropTypeCollection model) 15 | { 16 | this.Line("namespace SmallBasic.Editor.Interop"); 17 | this.Brace(); 18 | 19 | this.Line("using System.Threading.Tasks;"); 20 | this.Line("using Microsoft.AspNetCore.Blazor;"); 21 | this.Line("using Microsoft.JSInterop;"); 22 | this.Line("using SmallBasic.Compiler.Services;"); 23 | this.Blank(); 24 | 25 | this.Line("internal static class JSInterop"); 26 | this.Brace(); 27 | 28 | foreach (InteropType type in model) 29 | { 30 | this.Line($"public static class {type.Name}"); 31 | this.Brace(); 32 | 33 | foreach (Method method in type.Methods) 34 | { 35 | string returnType = method.ReturnType.IsDefault() ? "Task" : $"Task<{method.ReturnType.ToCSharpType()}>"; 36 | string parameters = method.Parameters.Select(p => $"{p.Type.ToCSharpType()} {p.Name}").Join(", "); 37 | this.Line($"public static {(method.ReturnType.IsDefault() ? "async " : string.Empty)}{returnType} {method.Name}({parameters})"); 38 | this.Brace(); 39 | 40 | IEnumerable arguments = new string[] 41 | { 42 | $@"""JSInterop.{type.Name}.{method.Name.ToLowerFirstChar()}""" 43 | }.Concat(method.Parameters.Select(p => p.Name)); 44 | 45 | if (method.ReturnType.IsDefault()) 46 | { 47 | this.Line($@"await JSRuntime.Current.InvokeAsync({arguments.Join(", ")}).ConfigureAwait(false);"); 48 | } 49 | else 50 | { 51 | this.Line($@"return JSRuntime.Current.InvokeAsync<{method.ReturnType.ToCSharpType()}>({arguments.Join(", ")});"); 52 | } 53 | 54 | this.Unbrace(); 55 | } 56 | 57 | this.Unbrace(); 58 | } 59 | 60 | this.Unbrace(); 61 | this.Unbrace(); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /Source/SmallBasic.Generators/Interop/Models.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Generators.Interop 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Xml.Serialization; 10 | using SmallBasic.Utilities; 11 | 12 | public static class InteropTypeExtensions 13 | { 14 | public static string ToCSharpType(this string type) 15 | { 16 | switch (type) 17 | { 18 | // Native Types 19 | case "number": return "decimal"; 20 | case "string": return "string"; 21 | case "boolean": return "bool"; 22 | 23 | // Blazor Types 24 | case "HTMLElement": return "ElementRef"; 25 | 26 | // Monaco Types 27 | case "monaco.languages.CompletionItem": return "MonacoCompletionItem"; 28 | case "monaco.IPosition": return "MonacoPosition"; 29 | case "monaco.IRange": return "MonacoRange"; 30 | 31 | case string array when array.EndsWith("[]", StringComparison.InvariantCulture): 32 | return ToCSharpType(array.RemoveSuffix("[]")) + "[]"; 33 | 34 | default: throw ExceptionUtilities.UnexpectedValue(type); 35 | } 36 | } 37 | } 38 | 39 | public sealed class Parameter 40 | { 41 | [XmlAttribute] 42 | public string Name { get; set; } 43 | 44 | [XmlAttribute] 45 | public string Type { get; set; } 46 | } 47 | 48 | public sealed class Method 49 | { 50 | [XmlAttribute] 51 | public string Name { get; set; } 52 | 53 | [XmlAttribute] 54 | public string ReturnType { get; set; } 55 | 56 | [XmlArray(nameof(Parameters))] 57 | [XmlArrayItem(typeof(Parameter))] 58 | public List Parameters { get; set; } 59 | } 60 | 61 | public sealed class InteropType 62 | { 63 | [XmlAttribute] 64 | public string Name { get; set; } 65 | 66 | [XmlArray(nameof(Methods))] 67 | [XmlArrayItem(typeof(Method))] 68 | public List Methods { get; set; } 69 | } 70 | 71 | [XmlRoot("root")] 72 | public sealed class InteropTypeCollection : List 73 | { 74 | } 75 | } -------------------------------------------------------------------------------- /Source/SmallBasic.Generators/Parsing/Models.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Generators.Parsing 6 | { 7 | using System.Collections.Generic; 8 | using System.Xml.Serialization; 9 | 10 | public sealed class Member 11 | { 12 | [XmlAttribute] 13 | public string Name { get; set; } 14 | 15 | [XmlAttribute] 16 | public string Type { get; set; } 17 | 18 | [XmlAttribute] 19 | public bool IsOptional { get; set; } 20 | 21 | [XmlAttribute] 22 | public bool IsList { get; set; } 23 | 24 | [XmlAttribute] 25 | public string TokenKinds { get; set; } 26 | } 27 | 28 | public sealed class SyntaxNode 29 | { 30 | [XmlAttribute] 31 | public string Name { get; set; } 32 | 33 | [XmlAttribute] 34 | public string Inherits { get; set; } 35 | 36 | [XmlAttribute] 37 | public bool IsAbstract { get; set; } 38 | 39 | [XmlArray(nameof(Members))] 40 | [XmlArrayItem(typeof(Member))] 41 | public List Members { get; set; } 42 | } 43 | 44 | [XmlRoot("root")] 45 | public sealed class SyntaxNodeCollection : List 46 | { 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Source/SmallBasic.Generators/Program.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Generators 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Globalization; 10 | using CommandLine; 11 | 12 | [Verb("convert", HelpText = "Converts a model file into a code file.")] 13 | public class ConvertVerb 14 | { 15 | [Option(longName: "task", Required = true, HelpText = "Task name to run")] 16 | public string TaskName { get; set; } 17 | 18 | [Option(longName: "input", Required = true, HelpText = "Path to input model file")] 19 | public string InputFile { get; set; } 20 | 21 | [Option(longName: "output", Required = true, HelpText = "Path to output code file")] 22 | public string OutputFile { get; set; } 23 | } 24 | 25 | public static class Program 26 | { 27 | public static int Main(string[] args) => 28 | CommandLine.Parser.Default.ParseArguments(args).MapResult( 29 | (ConvertVerb verb) => FindAndRunTask(verb.TaskName, task => task.Execute(verb.InputFile, verb.OutputFile)), 30 | (IEnumerable errors) => 31 | { 32 | foreach (var error in errors) 33 | { 34 | Console.Error.WriteLine(error.ToString()); 35 | } 36 | 37 | return 1; 38 | }); 39 | 40 | private static int FindAndRunTask(string taskName, Func then) 41 | { 42 | foreach (Type type in typeof(Program).Assembly.DefinedTypes) 43 | { 44 | if (type.Name.ToLower(CultureInfo.CurrentCulture) == taskName.Replace("-", string.Empty, StringComparison.CurrentCulture)) 45 | { 46 | return then((TTask)Activator.CreateInstance(type)); 47 | } 48 | } 49 | 50 | Console.Error.WriteLine($"Cannot find a task with name '{taskName}'."); 51 | return 1; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Source/SmallBasic.Generators/Scanning/GenerateTokenKinds.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Generators.Scanning 6 | { 7 | using System; 8 | using System.Linq; 9 | using SmallBasic.Utilities; 10 | 11 | public sealed class GenerateTokenKinds : BaseConverterTask 12 | { 13 | protected override void Generate(TokenKindCollection model) 14 | { 15 | this.Line("namespace SmallBasic.Compiler.Scanning"); 16 | this.Brace(); 17 | 18 | this.Line("using SmallBasic.Utilities;"); 19 | this.Line("using SmallBasic.Utilities.Resources;"); 20 | this.Blank(); 21 | 22 | this.Line("internal enum TokenKind"); 23 | this.Brace(); 24 | 25 | foreach (var tokenKind in model) 26 | { 27 | this.Line($"{tokenKind.Name},"); 28 | } 29 | 30 | this.Unbrace(); 31 | this.Blank(); 32 | 33 | this.Line("internal static partial class TokenKindExtensions"); 34 | this.Brace(); 35 | 36 | this.Line("public static string ToDisplayString(this TokenKind kind)"); 37 | this.Brace(); 38 | 39 | this.Line("switch (kind)"); 40 | this.Brace(); 41 | 42 | foreach (var tokenKind in model) 43 | { 44 | this.Line($"case TokenKind.{tokenKind.Name}: return {tokenKind.Display};"); 45 | } 46 | 47 | this.Line("default: throw ExceptionUtilities.UnexpectedValue(kind);"); 48 | 49 | this.Unbrace(); 50 | this.Unbrace(); 51 | this.Unbrace(); 52 | this.Unbrace(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Source/SmallBasic.Generators/Scanning/Models.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Generators.Scanning 6 | { 7 | using System.Collections.Generic; 8 | using System.Xml.Serialization; 9 | 10 | public sealed class TokenKind 11 | { 12 | [XmlAttribute] 13 | public string Name { get; set; } 14 | 15 | [XmlAttribute] 16 | public string Display { get; set; } 17 | } 18 | 19 | [XmlRoot("root")] 20 | public sealed class TokenKindCollection : List 21 | { 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Source/SmallBasic.Generators/Scanning/TokenKinds.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Source/SmallBasic.Server/Program.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Server 6 | { 7 | using System.Linq; 8 | using System.Net.Mime; 9 | using Microsoft.AspNetCore; 10 | using Microsoft.AspNetCore.Blazor.Server; 11 | using Microsoft.AspNetCore.Builder; 12 | using Microsoft.AspNetCore.Hosting; 13 | using Microsoft.AspNetCore.ResponseCompression; 14 | using Microsoft.Extensions.Configuration; 15 | using Microsoft.Extensions.DependencyInjection; 16 | 17 | public static class Program 18 | { 19 | public static void Main(string[] args) 20 | { 21 | var configuration = new ConfigurationBuilder() 22 | .AddCommandLine(args) 23 | .Build(); 24 | 25 | WebHost.CreateDefaultBuilder(args) 26 | .UseConfiguration(configuration) 27 | .UseStartup() 28 | .Build() 29 | .Run(); 30 | } 31 | } 32 | 33 | public class Startup 34 | { 35 | #pragma warning disable CA1801, CA1822 36 | public void ConfigureServices(IServiceCollection services) 37 | { 38 | services.AddResponseCompression(options => 39 | { 40 | options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] 41 | { 42 | MediaTypeNames.Application.Octet, 43 | WasmMediaTypeNames.Application.Wasm, 44 | }); 45 | }); 46 | } 47 | 48 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 49 | { 50 | app.UseResponseCompression(); 51 | 52 | if (env.IsDevelopment()) 53 | { 54 | app.UseDeveloperExceptionPage(); 55 | } 56 | 57 | app.UseBlazor(); 58 | } 59 | #pragma warning restore CA1801, CA1822 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Source/SmallBasic.Server/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:63223/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SmallBasic.Server": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "http://localhost:63226/" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Source/SmallBasic.Server/SmallBasic.Server.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Source/SmallBasic.Tests/Compiler/ScanningTests.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Tests.Compiler 6 | { 7 | using SmallBasic.Compiler; 8 | using SmallBasic.Compiler.Diagnostics; 9 | using Xunit; 10 | 11 | public sealed class ScanningTests : IClassFixture 12 | { 13 | [Fact] 14 | public void ItReportsUnterminatedStringLiterals() 15 | { 16 | new SmallBasicCompilation(@" 17 | x = ""name").VerifyDiagnostics( 18 | // x = "name 19 | // ^^^^^ 20 | // This string is missing its right double quotes. 21 | new Diagnostic(DiagnosticCode.UnterminatedStringLiteral, ((1, 4), (1, 8)))); 22 | } 23 | 24 | [Fact] 25 | public void ItReportsMultipleUnterminatedStringLiterals() 26 | { 27 | new SmallBasicCompilation(@" 28 | x = ""name 29 | y = ""another").VerifyDiagnostics( 30 | // x = "name 31 | // ^^^^^ 32 | // This string is missing its right double quotes. 33 | new Diagnostic(DiagnosticCode.UnterminatedStringLiteral, ((1, 4), (1, 8))), 34 | // y = "another 35 | // ^^^^^^^^ 36 | // This string is missing its right double quotes. 37 | new Diagnostic(DiagnosticCode.UnterminatedStringLiteral, ((2, 4), (2, 11)))); 38 | } 39 | 40 | [Fact] 41 | public void ItReportsUnrecognizedCharactersOnStartOfLine() 42 | { 43 | new SmallBasicCompilation(@" 44 | $").VerifyDiagnostics( 45 | // $ 46 | // ^ 47 | // I don't understand this character '$'. 48 | new Diagnostic(DiagnosticCode.UnrecognizedCharacter, ((1, 0), (1, 0)), "$")); 49 | } 50 | 51 | [Fact] 52 | public void ItReportsMultipleUnrecognizedCharacters() 53 | { 54 | new SmallBasicCompilation(@" 55 | x = ____^ 56 | ok = ""value $ value"" 57 | not_ok = 6 $ 58 | ' $ still ok").VerifyDiagnostics( 59 | // x = ____^ 60 | // ^ 61 | // I don't understand this character '^'. 62 | new Diagnostic(DiagnosticCode.UnrecognizedCharacter, ((1, 8), (1, 8)), "^"), 63 | // not_ok = 6 $ 64 | // ^ 65 | // I don't understand this character '$'. 66 | new Diagnostic(DiagnosticCode.UnrecognizedCharacter, ((3, 11), (3, 11)), "$")); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Source/SmallBasic.Tests/CultureFixture.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Tests 6 | { 7 | using System.Globalization; 8 | 9 | public class CultureFixture 10 | { 11 | public CultureFixture() 12 | { 13 | CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Source/SmallBasic.Tests/Runtime/ExpressionTests.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Tests.Runtime 6 | { 7 | using System.Threading.Tasks; 8 | using SmallBasic.Compiler; 9 | using Xunit; 10 | 11 | public sealed class ExpressionTests : IClassFixture 12 | { 13 | [Fact] 14 | public Task NumericStringsAreTreatedAsNumbers() 15 | { 16 | return new SmallBasicCompilation(@" 17 | x = ""1"" + 1 18 | y = 4 + ""-1""").VerifyLoggingRuntime(memoryContents: @" 19 | x = 2 20 | y = 3"); 21 | } 22 | 23 | [Fact] 24 | public Task NumbersAreConcatenatedWithStrings() 25 | { 26 | return new SmallBasicCompilation(@" 27 | x = 200 + "","" + 100 28 | y = 300 + "", "" + 100").VerifyLoggingRuntime(memoryContents: @" 29 | x = 200,100 30 | y = 300, 100"); 31 | } 32 | 33 | [Fact] 34 | public Task ItEvaluatesArrayAccess() 35 | { 36 | return new SmallBasicCompilation(@" 37 | ar[0] = ""first"" 38 | ar[1][0] = ""second"" 39 | ar[1][2] = ""third"" 40 | 41 | found_first = ar[0] 42 | found_second = ar[1][2] 43 | 44 | not_found_ar = none[0] 45 | not_found_first = ar[4] 46 | not_found_second = ar[1][6] 47 | ").VerifyLoggingRuntime(memoryContents: @" 48 | ar = 0=first;1=0\=second\;2\=third\;; 49 | found_first = first 50 | found_second = third 51 | not_found_ar = 52 | not_found_first = 53 | not_found_second = "); 54 | } 55 | 56 | [Fact] 57 | public Task ItSupportsDBCSInStrings() 58 | { 59 | return new SmallBasicCompilation(@" 60 | TextWindow.WriteLine(""こんにちは!"") 61 | ").VerifyLoggingRuntime(expectedLog: @" 62 | TextWindow.WriteLine(data: 'こんにちは!') 63 | "); 64 | } 65 | 66 | [Fact] 67 | public Task ItSupportsDBCSInIdentifiers() 68 | { 69 | return new SmallBasicCompilation(@" 70 | サブ() 71 | TextWindow.WriteLine(変数) 72 | Goto ラベル 73 | 74 | Sub サブ 75 | 変数 = 5 76 | EndSub 77 | 78 | TextWindow.WriteLine(""should be skipped"") 79 | ラベル: 80 | ").VerifyLoggingRuntime(expectedLog: @" 81 | TextWindow.WriteLine(data: '5') 82 | "); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Source/SmallBasic.Tests/Runtime/RuntimeAnalysisTests.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Tests.Runtime 6 | { 7 | using FluentAssertions; 8 | using SmallBasic.Compiler; 9 | using SmallBasic.Compiler.Binding; 10 | using Xunit; 11 | 12 | public sealed class RuntimeAnalysisTests : IClassFixture 13 | { 14 | [Fact] 15 | public void ItDoesNotUseGraphicsWindowWhenNotNeeded() 16 | { 17 | var compilation = new SmallBasicCompilation("TextWindow.WriteLine(5)"); 18 | compilation.Analysis.UsesTextWindow.Should().Be(true); 19 | compilation.Analysis.UsesGraphicsWindow.Should().Be(false); 20 | } 21 | 22 | [Fact] 23 | public void ItUsesGraphicsWindowWhenNeeded() 24 | { 25 | var compilation = new SmallBasicCompilation("GraphicsWindow.Clear()"); 26 | compilation.Analysis.UsesTextWindow.Should().Be(false); 27 | compilation.Analysis.UsesGraphicsWindow.Should().Be(true); 28 | } 29 | 30 | [Fact] 31 | public void ItUsesTextWindowWhenNothingIsNeeded() 32 | { 33 | var compilation = new SmallBasicCompilation("x = 1"); 34 | compilation.Analysis.UsesTextWindow.Should().Be(true); 35 | compilation.Analysis.UsesGraphicsWindow.Should().Be(false); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Source/SmallBasic.Tests/SmallBasic.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Source/SmallBasic.Utilities/Bridge/ImageListBridgeModels.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Utilities.Bridge 6 | { 7 | public static class ImageListBridgeModels 8 | { 9 | public sealed class ImageData 10 | { 11 | public ImageData() 12 | { 13 | } 14 | 15 | public ImageData(decimal width, decimal height, string base64Contents) 16 | { 17 | this.Width = width; 18 | this.Height = height; 19 | this.Base64Contents = base64Contents; 20 | } 21 | 22 | public decimal Width { get; set; } 23 | 24 | public decimal Height { get; set; } 25 | 26 | public string Base64Contents { get; set; } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Source/SmallBasic.Utilities/ExceptionUtilities.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Utilities 6 | { 7 | using System; 8 | 9 | public static class ExceptionUtilities 10 | { 11 | public static InvalidOperationException UnexpectedValue(TValue value) 12 | { 13 | return new InvalidOperationException($"Unexpected value '{value}' of type '{typeof(TValue).FullName}'"); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Source/SmallBasic.Utilities/Extensions/ObjectExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Utilities 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Globalization; 10 | 11 | public static class ObjectExtensions 12 | { 13 | #pragma warning disable SB1002 // Use IsDefault Helper 14 | public static bool IsDefault(this object value) => ReferenceEquals(value, default); 15 | #pragma warning restore SB1002 // Use IsDefault Helper 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Source/SmallBasic.Utilities/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Licensed under the MIT License. See LICENSE file in the project root for license information. 3 | // 4 | 5 | namespace SmallBasic.Utilities 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Globalization; 10 | 11 | public static class StringExtensions 12 | { 13 | public static string Join(this IEnumerable enumerable, string separator = "") => string.Join(separator, enumerable); 14 | 15 | public static string ToLowerFirstChar(this string value) => char.ToLowerInvariant(value[0]) + value.Substring(1); 16 | 17 | public static string RemovePrefix(this string value, string prefix) 18 | { 19 | if (!value.StartsWith(prefix, StringComparison.CurrentCulture)) 20 | { 21 | throw new ArgumentException($"Value '{value}' does not start with prefix '{prefix}'."); 22 | } 23 | 24 | return value.Substring(prefix.Length); 25 | } 26 | 27 | public static string RemoveSuffix(this string value, string suffix) 28 | { 29 | if (!value.EndsWith(suffix, StringComparison.CurrentCulture)) 30 | { 31 | throw new ArgumentException($"Value '{value}' does not end with suffix '{suffix}'."); 32 | } 33 | 34 | return value.Substring(0, value.Length - suffix.Length); 35 | } 36 | 37 | public static string ToDisplayString(this string value) => value; 38 | 39 | public static string ToDisplayString(this char value) => value.ToString(CultureInfo.CurrentCulture); 40 | 41 | public static string ToDisplayString(this int value) => value.ToString(CultureInfo.CurrentCulture); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Source/SmallBasic.Utilities/SmallBasic.Utilities.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | PublicResXFileCodeGenerator 10 | DiagnosticsResources.Designer.cs 11 | 12 | 13 | PublicResXFileCodeGenerator 14 | EditorResources.Designer.cs 15 | 16 | 17 | PublicResXFileCodeGenerator 18 | EditorResources.Designer.cs 19 | 20 | 21 | PublicResXFileCodeGenerator 22 | LibrariesResources.Designer.cs 23 | 24 | 25 | PublicResXFileCodeGenerator 26 | TokenKindsResources.Designer.cs 27 | 28 | 29 | 30 | 31 | 32 | True 33 | True 34 | DiagnosticsResources.resx 35 | 36 | 37 | True 38 | True 39 | EditorResources.resx 40 | 41 | 42 | True 43 | True 44 | EditorResources.resx 45 | 46 | 47 | True 48 | True 49 | LibrariesResources.resx 50 | 51 | 52 | True 53 | True 54 | TokenKindsResources.resx 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Source/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 3 | "settings": { 4 | "documentationRules": { 5 | "companyName": "MIT License", 6 | "copyrightText": "Licensed under the MIT License. See LICENSE file in the project root for license information." 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.1.816" 4 | } 5 | } 6 | --------------------------------------------------------------------------------