mirror of
https://github.com/Stone-Red-Code/YesNt-Interpreter.git
synced 2026-09-04 00:56:31 +02:00
Initial commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
||||||
+334
@@ -0,0 +1,334 @@
|
|||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.rsuser
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUNIT
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_h.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*_wpftmp.csproj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# JustCode is a .NET coding add-in
|
||||||
|
.JustCode
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
.idea/
|
||||||
|
*.sln.iml
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
.cr/personal
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
.localhistory/
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 16
|
||||||
|
VisualStudioVersion = 16.0.31410.357
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YesNt.Interpreter", "YesNt-Interpreter\YesNt.Interpreter.csproj", "{1D79AC8F-9FC7-4B4C-9899-8DAF2B630030}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YesNt.CodeEditor", "YesNt.CodeEditor\YesNt.CodeEditor.csproj", "{33BDA036-A37E-475E-AACF-8ED96E31BA1A}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{1D79AC8F-9FC7-4B4C-9899-8DAF2B630030}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1D79AC8F-9FC7-4B4C-9899-8DAF2B630030}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1D79AC8F-9FC7-4B4C-9899-8DAF2B630030}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1D79AC8F-9FC7-4B4C-9899-8DAF2B630030}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{33BDA036-A37E-475E-AACF-8ED96E31BA1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{33BDA036-A37E-475E-AACF-8ED96E31BA1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{33BDA036-A37E-475E-AACF-8ED96E31BA1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{33BDA036-A37E-475E-AACF-8ED96E31BA1A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {8597E655-80C0-48F2-AEB1-4A279F5173D6}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
using YesNt.Interpreter.Enums;
|
||||||
|
|
||||||
|
namespace YesNt.Interpreter.Attributes
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||||
|
internal class StatementAttribute : Attribute
|
||||||
|
{
|
||||||
|
public string Name { get; }
|
||||||
|
public SearchMode SearchMode { get; }
|
||||||
|
public SpaceAround SpaceAround { get; }
|
||||||
|
public Priority Priority { get; set; } = Priority.Normal;
|
||||||
|
public bool ExecuteInSearchLabelMode { get; set; }
|
||||||
|
public bool KeepStatementInArgs { get; set; }
|
||||||
|
public bool IgnoreSyntaxHighlighting { get; set; }
|
||||||
|
|
||||||
|
internal StatementAttribute(string name, SearchMode searchMode, SpaceAround spaceAround)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
SearchMode = searchMode;
|
||||||
|
SpaceAround = spaceAround;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
using YesNt.Interpreter.Enums;
|
||||||
|
|
||||||
|
namespace YesNt.Interpreter.Attributes
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||||
|
internal class StaticStatementAttribute : Attribute
|
||||||
|
{
|
||||||
|
public bool ExecuteInSearchLabelMode { get; set; }
|
||||||
|
public Priority Priority { get; set; } = Priority.Normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace YesNt.Interpreter.Enums
|
||||||
|
{
|
||||||
|
internal enum Priority
|
||||||
|
{
|
||||||
|
Highest,
|
||||||
|
VeryHigh,
|
||||||
|
High,
|
||||||
|
Normal,
|
||||||
|
Low,
|
||||||
|
VeryLow
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace YesNt.Interpreter.Enums
|
||||||
|
{
|
||||||
|
public enum SearchMode
|
||||||
|
{
|
||||||
|
StartOfLine,
|
||||||
|
EndOfLine,
|
||||||
|
Contains,
|
||||||
|
Exact
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace YesNt.Interpreter.Enums
|
||||||
|
{
|
||||||
|
public enum SpaceAround
|
||||||
|
{
|
||||||
|
StartEnd,
|
||||||
|
Start,
|
||||||
|
End,
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace YesNt.Interpreter
|
||||||
|
{
|
||||||
|
internal class Program
|
||||||
|
{
|
||||||
|
private static void Main(string[] args)
|
||||||
|
{
|
||||||
|
YesNtInterpreter interpreter = new YesNtInterpreter();
|
||||||
|
interpreter.Initialize();
|
||||||
|
interpreter.Execute(args[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"YesNt-Interpreter": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"commandLineArgs": "code.ynt"
|
||||||
|
},
|
||||||
|
"WSL": {
|
||||||
|
"commandName": "WSL2",
|
||||||
|
"environmentVariables": {},
|
||||||
|
"distributionName": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace YesNt.Interpreter.Runtime
|
||||||
|
{
|
||||||
|
public class DebugEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public int LineNumber { get; internal set; }
|
||||||
|
public string CurrentLine { get; internal set; }
|
||||||
|
public string OriginalLine { get; internal set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using YesNt.Interpreter.Utilities;
|
||||||
|
|
||||||
|
namespace YesNt.Interpreter
|
||||||
|
{
|
||||||
|
internal class RuntimeInformation
|
||||||
|
{
|
||||||
|
public Dictionary<string, string> Variables { get; } = new();
|
||||||
|
public Dictionary<string, int> Labels { get; } = new();
|
||||||
|
public List<string> Lines { get; set; } = new();
|
||||||
|
public string CurrentLine { get; set; } = string.Empty;
|
||||||
|
public Stack<int> LabelStack { get; set; } = new();
|
||||||
|
public string SearchLabel { get; set; } = string.Empty;
|
||||||
|
public int LineNumber { get; set; } = 0;
|
||||||
|
public bool Stop { get; private set; } = false;
|
||||||
|
public bool IsDebugMode { get; set; }
|
||||||
|
|
||||||
|
public event Action<string> OnDebugOutput;
|
||||||
|
|
||||||
|
public void WriteLine(string output)
|
||||||
|
{
|
||||||
|
if (IsDebugMode)
|
||||||
|
{
|
||||||
|
OnDebugOutput?.Invoke(output.FromSaveString() + Environment.NewLine);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine(output.FromSaveString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(string output)
|
||||||
|
{
|
||||||
|
if (IsDebugMode)
|
||||||
|
{
|
||||||
|
OnDebugOutput?.Invoke(output.FromSaveString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.Write(output.FromSaveString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Exit(string message)
|
||||||
|
{
|
||||||
|
WriteLine($"{Environment.NewLine}[The process was terminated at line {LineNumber + 1} with the message: {message}]");
|
||||||
|
Stop = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
CurrentLine = string.Empty;
|
||||||
|
Lines.Clear();
|
||||||
|
Variables.Clear();
|
||||||
|
Labels.Clear();
|
||||||
|
LabelStack.Clear();
|
||||||
|
SearchLabel = string.Empty;
|
||||||
|
Stop = false;
|
||||||
|
IsDebugMode = false;
|
||||||
|
LineNumber = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using YesNt.Interpreter.Enums;
|
||||||
|
|
||||||
|
namespace YesNt.Interpreter.Runtime
|
||||||
|
{
|
||||||
|
public class StatementInformation
|
||||||
|
{
|
||||||
|
public string Name { get; internal set; }
|
||||||
|
public SearchMode SearchMode { get; internal set; }
|
||||||
|
public SpaceAround SpaceAround { get; internal set; }
|
||||||
|
public bool IgnoreSyntaxHighlighting { get; internal set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace YesNt.Interpreter
|
||||||
|
{
|
||||||
|
internal abstract class StatementRuntimeInformation
|
||||||
|
{
|
||||||
|
public RuntimeInformation RuntimeInfo { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
using YesNt.Interpreter.Attributes;
|
||||||
|
using YesNt.Interpreter.Enums;
|
||||||
|
using YesNt.Interpreter.Runtime;
|
||||||
|
using YesNt.Interpreter.Utilities;
|
||||||
|
|
||||||
|
namespace YesNt.Interpreter
|
||||||
|
{
|
||||||
|
public class YesNtInterpreter
|
||||||
|
{
|
||||||
|
private readonly RuntimeInformation runtimeInfo = new RuntimeInformation();
|
||||||
|
private Dictionary<StatementAttribute, Action<string>> statements = new();
|
||||||
|
private List<KeyValuePair<StaticStatementAttribute, Action>> staticStatements = new();
|
||||||
|
|
||||||
|
public ReadOnlyCollection<StatementInformation> StatementInformation
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
List<StatementInformation> informations = statements.Select(s =>
|
||||||
|
{
|
||||||
|
return new StatementInformation()
|
||||||
|
{
|
||||||
|
Name = s.Key.Name,
|
||||||
|
SearchMode = s.Key.SearchMode,
|
||||||
|
SpaceAround = s.Key.SpaceAround,
|
||||||
|
IgnoreSyntaxHighlighting = s.Key.IgnoreSyntaxHighlighting
|
||||||
|
};
|
||||||
|
}).ToList();
|
||||||
|
return new ReadOnlyCollection<StatementInformation>(informations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Action<DebugEventArgs> OnLineExecuted;
|
||||||
|
|
||||||
|
public event Action<string> OnDebugOutput;
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
Assembly assembly = Assembly.GetExecutingAssembly();
|
||||||
|
Type[] types = assembly.GetTypes();
|
||||||
|
|
||||||
|
IEnumerable<Type> statementRuntimeInfos = types.Where(t => t.IsSubclassOf(typeof(StatementRuntimeInformation)));
|
||||||
|
|
||||||
|
statements.Clear();
|
||||||
|
|
||||||
|
foreach (Type type in statementRuntimeInfos)
|
||||||
|
{
|
||||||
|
object statementInfo = Activator.CreateInstance(type);
|
||||||
|
|
||||||
|
MethodInfo[] methodInfos = statementInfo.GetType().GetMethods();
|
||||||
|
|
||||||
|
StatementRuntimeInformation statementRuntimeInfo = statementInfo as StatementRuntimeInformation;
|
||||||
|
statementRuntimeInfo.RuntimeInfo = runtimeInfo;
|
||||||
|
|
||||||
|
foreach (MethodInfo methodInfo in methodInfos)
|
||||||
|
{
|
||||||
|
StatementAttribute statementAttribute = methodInfo.GetCustomAttribute<StatementAttribute>();
|
||||||
|
if (statementAttribute is not null)
|
||||||
|
{
|
||||||
|
Action<string> method = methodInfo.CreateDelegate(typeof(Action<string>), statementInfo) as Action<string>;
|
||||||
|
statements.Add(statementAttribute, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
StaticStatementAttribute staticStatementAttribute = methodInfo.GetCustomAttribute<StaticStatementAttribute>();
|
||||||
|
if (staticStatementAttribute is not null)
|
||||||
|
{
|
||||||
|
Action method = methodInfo.CreateDelegate(typeof(Action), statementInfo) as Action;
|
||||||
|
staticStatements.Add(new(staticStatementAttribute, method));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
statements = statements.OrderBy(s => s.Key.Priority).ToDictionary(x => x.Key, x => x.Value);
|
||||||
|
staticStatements = staticStatements.OrderBy(s => s.Key.Priority).ToList();
|
||||||
|
|
||||||
|
runtimeInfo.OnDebugOutput += (s) => OnDebugOutput?.Invoke(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute(string path, bool isDebugMode = false)
|
||||||
|
{
|
||||||
|
runtimeInfo.Reset();
|
||||||
|
runtimeInfo.IsDebugMode = isDebugMode;
|
||||||
|
LoadFile(path);
|
||||||
|
|
||||||
|
for (runtimeInfo.LineNumber = 0; runtimeInfo.LineNumber < runtimeInfo.Lines.Count; runtimeInfo.LineNumber++)
|
||||||
|
{
|
||||||
|
if (runtimeInfo.Stop)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
runtimeInfo.CurrentLine = runtimeInfo.Lines[runtimeInfo.LineNumber].TrimEnd().Replace("\r", string.Empty);
|
||||||
|
|
||||||
|
DebugEventArgs debugEventArgs = new DebugEventArgs()
|
||||||
|
{
|
||||||
|
LineNumber = runtimeInfo.LineNumber + 1,
|
||||||
|
OriginalLine = runtimeInfo.CurrentLine.FromSaveString()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(runtimeInfo.CurrentLine))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (KeyValuePair<StaticStatementAttribute, Action> staticStatement in staticStatements)
|
||||||
|
{
|
||||||
|
StaticStatementAttribute staticStatementAttribute = staticStatement.Key;
|
||||||
|
if (staticStatementAttribute.ExecuteInSearchLabelMode == false && !string.IsNullOrWhiteSpace(runtimeInfo.SearchLabel))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
staticStatement.Value.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool statementFound = false;
|
||||||
|
bool searchingLabel = string.IsNullOrWhiteSpace(runtimeInfo.SearchLabel);
|
||||||
|
|
||||||
|
foreach (KeyValuePair<StatementAttribute, Action<string>> statement in statements)
|
||||||
|
{
|
||||||
|
StatementAttribute statementAttribute = statement.Key;
|
||||||
|
|
||||||
|
if (statementAttribute.ExecuteInSearchLabelMode == false && !string.IsNullOrWhiteSpace(runtimeInfo.SearchLabel))
|
||||||
|
{
|
||||||
|
statementFound = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runtimeInfo.Stop)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
string name = statementAttribute.SpaceAround switch
|
||||||
|
{
|
||||||
|
SpaceAround.StartEnd => $" {statementAttribute.Name.Trim()} ",
|
||||||
|
SpaceAround.Start => $" {statementAttribute.Name.Trim()}",
|
||||||
|
SpaceAround.End => $"{statementAttribute.Name.Trim()} ",
|
||||||
|
_ => statementAttribute.Name
|
||||||
|
};
|
||||||
|
|
||||||
|
if (statementAttribute.SearchMode == SearchMode.StartOfLine && runtimeInfo.CurrentLine.StartsWith(name))
|
||||||
|
{
|
||||||
|
string copyLine = statementAttribute.KeepStatementInArgs ? runtimeInfo.CurrentLine : runtimeInfo.CurrentLine.Remove(0, name.Length);
|
||||||
|
statement.Value.Invoke(copyLine);
|
||||||
|
statementFound = true;
|
||||||
|
}
|
||||||
|
else if (statementAttribute.SearchMode == SearchMode.Contains && $"{runtimeInfo.CurrentLine} ".Contains(name))
|
||||||
|
{
|
||||||
|
runtimeInfo.CurrentLine = $"{runtimeInfo.CurrentLine} ";
|
||||||
|
string copyLine = statementAttribute.KeepStatementInArgs ? runtimeInfo.CurrentLine : runtimeInfo.CurrentLine.Replace(name, string.Empty);
|
||||||
|
statement.Value.Invoke(copyLine);
|
||||||
|
statementFound = true;
|
||||||
|
}
|
||||||
|
else if (statementAttribute.SearchMode == SearchMode.EndOfLine && runtimeInfo.CurrentLine.EndsWith(name))
|
||||||
|
{
|
||||||
|
string copyLine = statementAttribute.KeepStatementInArgs ? runtimeInfo.CurrentLine : runtimeInfo.CurrentLine.Remove(runtimeInfo.CurrentLine.Length - name.Length);
|
||||||
|
statement.Value.Invoke(copyLine);
|
||||||
|
statementFound = true;
|
||||||
|
}
|
||||||
|
else if (statementAttribute.SearchMode == SearchMode.Exact && runtimeInfo.CurrentLine.Equals(name))
|
||||||
|
{
|
||||||
|
statement.Value.Invoke(runtimeInfo.CurrentLine);
|
||||||
|
statementFound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!statementFound)
|
||||||
|
{
|
||||||
|
runtimeInfo.Exit("Invalid statement");
|
||||||
|
}
|
||||||
|
if (isDebugMode && searchingLabel)
|
||||||
|
{
|
||||||
|
debugEventArgs.CurrentLine = runtimeInfo.CurrentLine.FromSaveString();
|
||||||
|
OnLineExecuted?.Invoke(debugEventArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runtimeInfo.Stop == false)
|
||||||
|
{
|
||||||
|
runtimeInfo.Exit("End of file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadFile(string path)
|
||||||
|
{
|
||||||
|
if (!File.Exists(path))
|
||||||
|
{
|
||||||
|
runtimeInfo.WriteLine($"File \"{path}\" not found!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<string> lines = File.ReadAllLines(path);
|
||||||
|
runtimeInfo.Lines.AddRange(lines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,199 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
using YesNt.Interpreter.Attributes;
|
||||||
|
using YesNt.Interpreter.Enums;
|
||||||
|
using YesNt.Interpreter.Utilities;
|
||||||
|
|
||||||
|
namespace YesNt.Interpreter
|
||||||
|
{
|
||||||
|
internal class Statements : StatementRuntimeInformation
|
||||||
|
{
|
||||||
|
[Statement("cwl", SearchMode.StartOfLine, SpaceAround.End, Priority = Priority.VeryLow)]
|
||||||
|
public void WriteLine(string args)
|
||||||
|
{
|
||||||
|
RuntimeInfo.WriteLine(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Statement("cw", SearchMode.StartOfLine, SpaceAround.End, Priority = Priority.VeryLow)]
|
||||||
|
public void Write(string args)
|
||||||
|
{
|
||||||
|
RuntimeInfo.Write(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Statement("%crl", SearchMode.Contains, SpaceAround.End, KeepStatementInArgs = true, Priority = Priority.Highest)]
|
||||||
|
public void ReadLine(string args)
|
||||||
|
{
|
||||||
|
args += " ";
|
||||||
|
while (args.Contains("%crl "))
|
||||||
|
{
|
||||||
|
args = args.ReplaceFirstOccurrence("%crl ", Console.ReadLine().ToSaveString() + " ");
|
||||||
|
}
|
||||||
|
RuntimeInfo.CurrentLine = args.TrimEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Statement("%cr", SearchMode.Contains, SpaceAround.End, KeepStatementInArgs = true, Priority = Priority.Highest)]
|
||||||
|
public void ReadKey(string args)
|
||||||
|
{
|
||||||
|
args += " ";
|
||||||
|
while (args.Contains("%cr "))
|
||||||
|
{
|
||||||
|
args = args.ReplaceFirstOccurrence("%cr ", Console.ReadKey().KeyChar.ToString().ToSaveString() + " ");
|
||||||
|
}
|
||||||
|
RuntimeInfo.CurrentLine = args.TrimEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Statement("jmp", SearchMode.StartOfLine, SpaceAround.End)]
|
||||||
|
public void Jump(string args)
|
||||||
|
{
|
||||||
|
string key = args.Trim();
|
||||||
|
RuntimeInfo.LabelStack.Push(RuntimeInfo.LineNumber);
|
||||||
|
|
||||||
|
if (RuntimeInfo.Labels.ContainsKey(key))
|
||||||
|
{
|
||||||
|
RuntimeInfo.LineNumber = RuntimeInfo.Labels[key];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RuntimeInfo.SearchLabel = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Statement("jif", SearchMode.StartOfLine, SpaceAround.End)]
|
||||||
|
public void JumpIf(string args)
|
||||||
|
{
|
||||||
|
string[] parts = args.Split('|');
|
||||||
|
if (parts.Length != 2)
|
||||||
|
{
|
||||||
|
RuntimeInfo.Exit("Invalid syntax");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string key = parts[0].Trim();
|
||||||
|
string condition = parts[1].Trim();
|
||||||
|
|
||||||
|
bool? result = Evaluator.EvaluateCondition(condition);
|
||||||
|
|
||||||
|
if (result != true)
|
||||||
|
{
|
||||||
|
if (result is null)
|
||||||
|
{
|
||||||
|
RuntimeInfo.Exit("Invalid operation");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeInfo.LabelStack.Push(RuntimeInfo.LineNumber);
|
||||||
|
|
||||||
|
if (RuntimeInfo.Labels.ContainsKey(key))
|
||||||
|
{
|
||||||
|
RuntimeInfo.LineNumber = RuntimeInfo.Labels[key];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RuntimeInfo.SearchLabel = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Statement("lbl", SearchMode.StartOfLine, SpaceAround.End, ExecuteInSearchLabelMode = true)]
|
||||||
|
public void FindLabel(string args)
|
||||||
|
{
|
||||||
|
string key = args.Trim();
|
||||||
|
if (RuntimeInfo.Labels.ContainsKey(key))
|
||||||
|
{
|
||||||
|
RuntimeInfo.Labels[key] = RuntimeInfo.LineNumber;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RuntimeInfo.Labels.Add(key, RuntimeInfo.LineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(RuntimeInfo.SearchLabel) && RuntimeInfo.SearchLabel == key)
|
||||||
|
{
|
||||||
|
RuntimeInfo.SearchLabel = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Statement("ret", SearchMode.Exact, SpaceAround.None)]
|
||||||
|
public void Return(string _)
|
||||||
|
{
|
||||||
|
if (RuntimeInfo.LabelStack.Count > 0)
|
||||||
|
{
|
||||||
|
RuntimeInfo.LineNumber = RuntimeInfo.LabelStack.Pop();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RuntimeInfo.Exit("No label in stack");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Statement("end", SearchMode.Exact, SpaceAround.None)]
|
||||||
|
public void End(string _)
|
||||||
|
{
|
||||||
|
RuntimeInfo.Exit("Planned termination by code");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Statement("!calc", SearchMode.EndOfLine, SpaceAround.Start, Priority = Priority.High, ExecuteInSearchLabelMode = true)]
|
||||||
|
public void Calculate(string args)
|
||||||
|
{
|
||||||
|
MatchCollection matches = Regex.Matches(args, @"((\)?)+(\(?)+[0-9]+(((\s?)+(\)?)(\s?)+\+(\s?)+(\(?)+(\s?)+|(\s?)+(\)?)(\s?)+\-(\s?)+(\(?)+(\s?)+|(\s?)+(\)?)(\s?)+\*(\s?)+(\(?)+(\s?)+|(\s?)+(\)?)(\s?)+\/(\s?)+(\(?)+(\s?)+)|[,.])(?=[0-9])+)+[0-9]+(\)?)+");
|
||||||
|
|
||||||
|
for (int i = 0; i < matches.Count; i++)
|
||||||
|
{
|
||||||
|
string res = Evaluator.Calculate(matches[i].Value);
|
||||||
|
if (res is null)
|
||||||
|
{
|
||||||
|
RuntimeInfo.Exit("Invalid operation");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
args = args.Replace(matches[0].Value, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeInfo.CurrentLine = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Statement("!eval", SearchMode.EndOfLine, SpaceAround.Start, Priority = Priority.VeryHigh, ExecuteInSearchLabelMode = true)]
|
||||||
|
public void Evaluate(string args)
|
||||||
|
{
|
||||||
|
RuntimeInfo.CurrentLine = args.FromSaveString();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Statement("<", SearchMode.StartOfLine, SpaceAround.None, Priority = Priority.VeryLow, IgnoreSyntaxHighlighting = true)]
|
||||||
|
public void DefineVariable(string args)
|
||||||
|
{
|
||||||
|
string[] parts = args.Split('=');
|
||||||
|
if (parts.Length == 2)
|
||||||
|
{
|
||||||
|
string key = parts[0].Replace("<", "").Trim();
|
||||||
|
if (RuntimeInfo.Variables.ContainsKey(key))
|
||||||
|
{
|
||||||
|
RuntimeInfo.Variables[key] = parts[1].Trim();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RuntimeInfo.Variables.Add(key, parts[1].Trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RuntimeInfo.Exit("Invalid syntax");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[StaticStatement(ExecuteInSearchLabelMode = true)]
|
||||||
|
public void ReadVariable()
|
||||||
|
{
|
||||||
|
if (!RuntimeInfo.CurrentLine.Contains('>'))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (KeyValuePair<string, string> variable in RuntimeInfo.Variables)
|
||||||
|
{
|
||||||
|
RuntimeInfo.CurrentLine = RuntimeInfo.CurrentLine.Replace($">{variable.Key}", variable.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace YesNt.Interpreter.Utilities
|
||||||
|
{
|
||||||
|
internal static class Evaluator
|
||||||
|
{
|
||||||
|
public static bool? EvaluateCondition(string input)
|
||||||
|
{
|
||||||
|
string[] parts = input.Split("==");
|
||||||
|
if (parts.Length == 2)
|
||||||
|
{
|
||||||
|
string part1 = parts[0].FromSaveString().Trim();
|
||||||
|
string part2 = parts[1].FromSaveString().Trim();
|
||||||
|
return part1 == part2;
|
||||||
|
}
|
||||||
|
|
||||||
|
parts = input.Split("!=");
|
||||||
|
if (parts.Length == 2)
|
||||||
|
{
|
||||||
|
string part1 = parts[0].FromSaveString().Trim();
|
||||||
|
string part2 = parts[1].FromSaveString().Trim();
|
||||||
|
return part1 != part2;
|
||||||
|
}
|
||||||
|
|
||||||
|
parts = input.Split(">");
|
||||||
|
if (parts.Length == 2)
|
||||||
|
{
|
||||||
|
bool succ1 = parts[0].ToStandardizedNumber(out double part1);
|
||||||
|
bool succ2 = parts[1].ToStandardizedNumber(out double part2);
|
||||||
|
if (!succ1 || !succ2)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return part1 > part2;
|
||||||
|
}
|
||||||
|
|
||||||
|
parts = input.Split("<");
|
||||||
|
if (parts.Length == 2)
|
||||||
|
{
|
||||||
|
bool succ1 = parts[0].ToStandardizedNumber(out double part1);
|
||||||
|
bool succ2 = parts[1].ToStandardizedNumber(out double part2);
|
||||||
|
if (!succ1 || !succ2)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return part1 < part2;
|
||||||
|
}
|
||||||
|
|
||||||
|
parts = input.Split(">=");
|
||||||
|
if (parts.Length == 2)
|
||||||
|
{
|
||||||
|
bool succ1 = parts[0].ToStandardizedNumber(out double part1);
|
||||||
|
bool succ2 = parts[1].ToStandardizedNumber(out double part2);
|
||||||
|
if (!succ1 || !succ2)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return part1 >= part2;
|
||||||
|
}
|
||||||
|
|
||||||
|
parts = input.Split("<=");
|
||||||
|
if (parts.Length == 2)
|
||||||
|
{
|
||||||
|
bool succ1 = parts[0].ToStandardizedNumber(out double part1);
|
||||||
|
bool succ2 = parts[1].ToStandardizedNumber(out double part2);
|
||||||
|
if (!succ1 || !succ2)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return part1 <= part2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
((\)?)+(\(?)+[0-9]+(((\s?)+(\)?)(\s?)+\+(\s?)+(\(?)+(\s?)+|(\s?)+(\)?)(\s?)+\-(\s?)+(\(?)+(\s?)+|(\s?)+(\)?)(\s?)+\*(\s?)+(\(?)+(\s?)+|(\s?)+(\)?)(\s?)+\/(\s?)+(\(?)+(\s?)+)|[,.])(?=[0-9])+)+[0-9]+(\)?)+
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static string Calculate(string input, char op = '+')
|
||||||
|
{
|
||||||
|
if (input is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
MatchCollection matches = Regex.Matches(input, @"\(([^()]+)\)");
|
||||||
|
while (matches.Count > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < matches.Count; i++)
|
||||||
|
{
|
||||||
|
string calc = matches[i].Value.Substring(1, matches[i].Length - 2);
|
||||||
|
string ret = Calculate(calc);
|
||||||
|
input = input.Replace(matches[i].Value, ret);
|
||||||
|
}
|
||||||
|
matches = Regex.Matches(input, @"\(([^()]+)\)");
|
||||||
|
}
|
||||||
|
|
||||||
|
string[] parts = input.Split(op);
|
||||||
|
|
||||||
|
double number = double.NaN;
|
||||||
|
|
||||||
|
foreach (string p in parts)
|
||||||
|
{
|
||||||
|
string part = p;
|
||||||
|
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case '+':
|
||||||
|
part = Calculate(part, '-');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '-':
|
||||||
|
part = Calculate(part, '*');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '*':
|
||||||
|
part = Calculate(part, '/');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (part is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (part.ToStandardizedNumber(out double num))
|
||||||
|
{
|
||||||
|
if (double.IsNaN(number))
|
||||||
|
{
|
||||||
|
number = num;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case '+':
|
||||||
|
number += num;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '-':
|
||||||
|
number -= num;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '*':
|
||||||
|
number *= num;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '/':
|
||||||
|
number /= num;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return number.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace YesNt.Interpreter.Utilities
|
||||||
|
{
|
||||||
|
public static class StringExtentions
|
||||||
|
{
|
||||||
|
public static string ToSaveString(this string input)
|
||||||
|
{
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
foreach (char c in input)
|
||||||
|
{
|
||||||
|
output.Append($"\r{c}\r");
|
||||||
|
}
|
||||||
|
return output.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FromSaveString(this string input)
|
||||||
|
{
|
||||||
|
return input.Replace("\r", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ToStandardizedNumber(this string input, out double result)
|
||||||
|
{
|
||||||
|
return double.TryParse(input.FromSaveString().Replace(',', '.'), NumberStyles.Any, CultureInfo.InvariantCulture, out result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ReplaceFirstOccurrence(this string input, string oldValue, string newValue)
|
||||||
|
{
|
||||||
|
int place = input.IndexOf(oldValue);
|
||||||
|
return input.Remove(place, oldValue.Length).Insert(place, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ReplaceLastOccurrence(this string input, string oldValue, string newValue)
|
||||||
|
{
|
||||||
|
int place = input.LastIndexOf(oldValue);
|
||||||
|
return input.Remove(place, Math.Min(oldValue.Length, input.Length - place)).Insert(place, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int WhiteSpaceAtEnd(this string input)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
int index = input.Length - 1;
|
||||||
|
while (index >= 0 && char.IsWhiteSpace(input[index--]))
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
<RootNamespace>YesNt.Interpreter</RootNamespace>
|
||||||
|
<ApplicationIcon />
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<StartupObject />
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,357 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace YesNt.Interpreter
|
||||||
|
{
|
||||||
|
public class YesNtInterpreter_OLD
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, string> variables = new Dictionary<string, string>();
|
||||||
|
private readonly Dictionary<string, int> labels = new Dictionary<string, int>();
|
||||||
|
private List<string> lines = new List<string>();
|
||||||
|
private Stack<int> lastLabels = new();
|
||||||
|
|
||||||
|
public void Execute(string path)
|
||||||
|
{
|
||||||
|
lines.Clear();
|
||||||
|
variables.Clear();
|
||||||
|
labels.Clear();
|
||||||
|
lastLabels.Clear();
|
||||||
|
|
||||||
|
LoadFile(path);
|
||||||
|
|
||||||
|
string searchLabel = string.Empty;
|
||||||
|
|
||||||
|
for (int lineNum = 0; lineNum < lines.Count; lineNum++)
|
||||||
|
{
|
||||||
|
string line = lines[lineNum].Trim().Replace("\r", "");
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(line))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchLabel == string.Empty)
|
||||||
|
{
|
||||||
|
if (line.Contains("%crl"))
|
||||||
|
{
|
||||||
|
line = line.Replace("%crl", ToSaveString(Console.ReadLine()));
|
||||||
|
}
|
||||||
|
if (line.Contains("%cr"))
|
||||||
|
{
|
||||||
|
line = line.Replace("%cr", ToSaveString(Console.ReadKey().KeyChar.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (KeyValuePair<string, string> variable in variables)
|
||||||
|
{
|
||||||
|
line = line.Replace($">{variable.Key}", variable.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.Contains(" "))
|
||||||
|
{
|
||||||
|
int index = line.IndexOf(' ');
|
||||||
|
if (line.Contains(" = ") && line.IndexOf('=') > index)
|
||||||
|
{
|
||||||
|
index = line.IndexOf('=') + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
string cmd = line.Substring(0, index);
|
||||||
|
cmd += Calculate(line.Substring(index));
|
||||||
|
line = cmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.StartsWith("%lbl "))
|
||||||
|
{
|
||||||
|
string key = line.Substring(5).Trim();
|
||||||
|
if (labels.ContainsKey(key))
|
||||||
|
{
|
||||||
|
labels[key] = lineNum;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
labels.Add(key, lineNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchLabel != string.Empty && searchLabel == key)
|
||||||
|
{
|
||||||
|
searchLabel = string.Empty;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchLabel != string.Empty)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.StartsWith("%imp "))
|
||||||
|
{
|
||||||
|
string pat = line.Substring(5).Trim();
|
||||||
|
lines.RemoveAt(lineNum);
|
||||||
|
LoadFile(pat, lineNum);
|
||||||
|
lineNum--;
|
||||||
|
}
|
||||||
|
else if (line.StartsWith("%jmp "))
|
||||||
|
{
|
||||||
|
string key = FromSaveString(line.Substring(5).Trim());
|
||||||
|
lastLabels.Push(lineNum);
|
||||||
|
|
||||||
|
if (labels.ContainsKey(key))
|
||||||
|
{
|
||||||
|
lineNum = labels[key];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
searchLabel = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (line.StartsWith("%jif "))
|
||||||
|
{
|
||||||
|
if (line.Contains("|"))
|
||||||
|
{
|
||||||
|
string dat = line.Substring(5).Trim();
|
||||||
|
string key = dat.Substring(0, dat.IndexOf('|')).Trim();
|
||||||
|
string condition = dat.Substring(dat.IndexOf('|')).Replace("|", "").Trim();
|
||||||
|
if (EvaluateCondition(condition))
|
||||||
|
{
|
||||||
|
lastLabels.Push(lineNum);
|
||||||
|
if (labels.ContainsKey(key))
|
||||||
|
{
|
||||||
|
lineNum = labels[key];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
searchLabel = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (line.Equals("%ret"))
|
||||||
|
{
|
||||||
|
lineNum = lastLabels.Pop();
|
||||||
|
}
|
||||||
|
else if (line.Equals("%end"))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (line.StartsWith("%cwl "))
|
||||||
|
{
|
||||||
|
Console.WriteLine(FromSaveString(line.Substring(5)));
|
||||||
|
}
|
||||||
|
else if (line.StartsWith("%cw "))
|
||||||
|
{
|
||||||
|
Console.Write(FromSaveString(line.Substring(4)));
|
||||||
|
}
|
||||||
|
else if (line.StartsWith("<"))
|
||||||
|
{
|
||||||
|
string[] parts = line.Split(" = ");
|
||||||
|
if (parts.Length == 2)
|
||||||
|
{
|
||||||
|
string key = parts[0].Replace("<", "");
|
||||||
|
if (variables.ContainsKey(key))
|
||||||
|
{
|
||||||
|
variables[key] = parts[1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
variables.Add(key, parts[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadFile(string path, int index = 0)
|
||||||
|
{
|
||||||
|
if (!File.Exists(path))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"File \"{path}\" not found!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string input = File.ReadAllText(path);
|
||||||
|
string[] newLines = input.Split("\n");
|
||||||
|
foreach (string line in newLines)
|
||||||
|
{
|
||||||
|
lines.Insert(index, line);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ToSaveString(string input)
|
||||||
|
{
|
||||||
|
string output = "";
|
||||||
|
foreach (char c in input)
|
||||||
|
{
|
||||||
|
output += $"\r{c}\r";
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string FromSaveString(string input)
|
||||||
|
{
|
||||||
|
return input.Replace("\r", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool EvaluateCondition(string input)
|
||||||
|
{
|
||||||
|
string[] parts = input.Split(" == ");
|
||||||
|
if (parts.Length == 2)
|
||||||
|
{
|
||||||
|
string part1 = FromSaveString(parts[0]);
|
||||||
|
string part2 = FromSaveString(parts[1]);
|
||||||
|
return part1 == part2;
|
||||||
|
}
|
||||||
|
|
||||||
|
parts = input.Split(" != ");
|
||||||
|
if (parts.Length == 2)
|
||||||
|
{
|
||||||
|
string part1 = FromSaveString(parts[0]);
|
||||||
|
string part2 = FromSaveString(parts[1]);
|
||||||
|
return part1 != part2;
|
||||||
|
}
|
||||||
|
|
||||||
|
parts = input.Split(" > ");
|
||||||
|
if (parts.Length == 2)
|
||||||
|
{
|
||||||
|
bool succ1 = double.TryParse(FromSaveString(parts[0]), out double part1);
|
||||||
|
bool succ2 = double.TryParse(FromSaveString(parts[1]), out double part2);
|
||||||
|
if (!succ1 || !succ2)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return part1 > part2;
|
||||||
|
}
|
||||||
|
|
||||||
|
parts = input.Split(" < ");
|
||||||
|
if (parts.Length == 2)
|
||||||
|
{
|
||||||
|
bool succ1 = double.TryParse(FromSaveString(parts[0]), out double part1);
|
||||||
|
bool succ2 = double.TryParse(FromSaveString(parts[1]), out double part2);
|
||||||
|
if (!succ1 || !succ2)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return part1 < part2;
|
||||||
|
}
|
||||||
|
|
||||||
|
parts = input.Split(" >= ");
|
||||||
|
if (parts.Length == 2)
|
||||||
|
{
|
||||||
|
bool succ1 = double.TryParse(FromSaveString(parts[0]), out double part1);
|
||||||
|
bool succ2 = double.TryParse(FromSaveString(parts[1]), out double part2);
|
||||||
|
if (!succ1 || !succ2)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return part1 >= part2;
|
||||||
|
}
|
||||||
|
|
||||||
|
parts = input.Split(" <= ");
|
||||||
|
if (parts.Length == 2)
|
||||||
|
{
|
||||||
|
bool succ1 = double.TryParse(FromSaveString(parts[0]), out double part1);
|
||||||
|
bool succ2 = double.TryParse(FromSaveString(parts[1]), out double part2);
|
||||||
|
if (!succ1 || !succ2)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return part1 <= part2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string Calculate(string input, char op = '+')
|
||||||
|
{
|
||||||
|
string[] parts = input.Split($" {op} ");
|
||||||
|
string result = "";
|
||||||
|
|
||||||
|
double number = double.NaN;
|
||||||
|
int spacesToAdd = 0;
|
||||||
|
|
||||||
|
foreach (string p in parts)
|
||||||
|
{
|
||||||
|
string part = p;
|
||||||
|
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case '+':
|
||||||
|
part = Calculate(part, '-');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '-':
|
||||||
|
part = Calculate(part, '*');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '*':
|
||||||
|
part = Calculate(part, '/');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (double.TryParse(FromSaveString(part), out double num))
|
||||||
|
{
|
||||||
|
if (double.IsNaN(number))
|
||||||
|
{
|
||||||
|
spacesToAdd = CountSpaces(part);
|
||||||
|
number = num;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case '+':
|
||||||
|
number += num;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '-':
|
||||||
|
number -= num;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '*':
|
||||||
|
number *= num;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '/':
|
||||||
|
number /= num;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!double.IsNaN(number))
|
||||||
|
{
|
||||||
|
result += new string(' ', spacesToAdd) + number;
|
||||||
|
}
|
||||||
|
|
||||||
|
result += part;
|
||||||
|
|
||||||
|
number = double.NaN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!double.IsNaN(number))
|
||||||
|
{
|
||||||
|
result += new string(' ', spacesToAdd) + number;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int CountSpaces(string input)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
while (input[count] == ' ')
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,426 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
using YesNt.Interpreter;
|
||||||
|
|
||||||
|
namespace YesNt.CodeEditor
|
||||||
|
{
|
||||||
|
internal class TextEditor
|
||||||
|
{
|
||||||
|
private readonly YesNtInterpreter yesNtInterpreter = new();
|
||||||
|
private readonly SyntaxHighlighter syntaxHighlighter;
|
||||||
|
private int lineOffset = 0;
|
||||||
|
private readonly List<string> lines = new();
|
||||||
|
private readonly List<string> debugOutput = new();
|
||||||
|
private readonly Point cursorPosition = new(0, 0);
|
||||||
|
private Mode mode = Mode.Command;
|
||||||
|
|
||||||
|
private string currentPath = string.Empty;
|
||||||
|
|
||||||
|
public TextEditor(string path) : this()
|
||||||
|
{
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
Load(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextEditor()
|
||||||
|
{
|
||||||
|
yesNtInterpreter.Initialize();
|
||||||
|
yesNtInterpreter.OnDebugOutput += YesNtInterpreter_OnDebugOutput;
|
||||||
|
yesNtInterpreter.OnLineExecuted += YesNtInterpreter_OnLineExecuted;
|
||||||
|
syntaxHighlighter = new(yesNtInterpreter.StatementInformation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run()
|
||||||
|
{
|
||||||
|
Console.Clear();
|
||||||
|
|
||||||
|
Display(true);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Display(false);
|
||||||
|
} while (HandleInput());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Display(bool drawAll)
|
||||||
|
{
|
||||||
|
Console.CursorVisible = false;
|
||||||
|
|
||||||
|
Console.SetCursorPosition(0, 0);
|
||||||
|
int padding = (Console.WindowHeight + lineOffset - 3).ToString().Length;
|
||||||
|
padding = Math.Max(padding, lines.Count.ToString().Length);
|
||||||
|
padding += 1;
|
||||||
|
for (int i = lineOffset; i < Console.WindowHeight + lineOffset - 2; i++)
|
||||||
|
{
|
||||||
|
Console.SetCursorPosition(0, i - lineOffset);
|
||||||
|
|
||||||
|
string lineCountString = $"{i + 1}".PadRight(padding, ' ') + "| ";
|
||||||
|
if (i < lines.Count)
|
||||||
|
{
|
||||||
|
if (cursorPosition.Y == i || drawAll)
|
||||||
|
{
|
||||||
|
Console.Write(lineCountString);
|
||||||
|
string printLine = $"{lines[i].Substring(0, Math.Min(lines[i].Length, Console.WindowWidth))}".TrimEnd();
|
||||||
|
syntaxHighlighter.Write(printLine);
|
||||||
|
Console.Write(new string(' ', Math.Max(Console.WindowWidth - lineCountString.Length - printLine.Length, 0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (drawAll || cursorPosition.Y == i)
|
||||||
|
{
|
||||||
|
Console.Write(lineCountString + new string(' ', Console.WindowWidth - lineCountString.Length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.SetCursorPosition(0, Console.WindowHeight - 3);
|
||||||
|
Console.Write(new string('-', Console.WindowWidth));
|
||||||
|
Console.SetCursorPosition(0, Console.WindowHeight - 2);
|
||||||
|
Console.Write(">>>" + new string(' ', Console.WindowWidth - 3));
|
||||||
|
|
||||||
|
Console.SetCursorPosition(Math.Min(cursorPosition.X + padding + 2, Console.WindowWidth - 1), cursorPosition.Y - lineOffset);
|
||||||
|
Console.CursorVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HandleInput()
|
||||||
|
{
|
||||||
|
if (mode == Mode.Edit)
|
||||||
|
{
|
||||||
|
ConsoleKeyInfo keyInfo = Console.ReadKey(true);
|
||||||
|
|
||||||
|
if ((ConsoleModifiers.Alt & keyInfo.Modifiers) != 0 && keyInfo.Key == ConsoleKey.C)
|
||||||
|
{
|
||||||
|
mode = Mode.Command;
|
||||||
|
}
|
||||||
|
else if (keyInfo.Key == ConsoleKey.DownArrow)
|
||||||
|
{
|
||||||
|
cursorPosition.Y++;
|
||||||
|
if (cursorPosition.Y - lineOffset >= Console.WindowHeight - 3)
|
||||||
|
{
|
||||||
|
lineOffset++;
|
||||||
|
Display(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (keyInfo.Key == ConsoleKey.UpArrow)
|
||||||
|
{
|
||||||
|
if (cursorPosition.Y > 0)
|
||||||
|
{
|
||||||
|
cursorPosition.Y--;
|
||||||
|
if (cursorPosition.Y - lineOffset < 0 && lineOffset > 0)
|
||||||
|
{
|
||||||
|
lineOffset--;
|
||||||
|
Display(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (keyInfo.Key == ConsoleKey.LeftArrow)
|
||||||
|
{
|
||||||
|
if (cursorPosition.X > 0)
|
||||||
|
{
|
||||||
|
cursorPosition.X--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (keyInfo.Key == ConsoleKey.RightArrow)
|
||||||
|
{
|
||||||
|
if (cursorPosition.X + 6 < Console.WindowWidth - 1)
|
||||||
|
{
|
||||||
|
cursorPosition.X++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (keyInfo.Key == ConsoleKey.Enter)
|
||||||
|
{
|
||||||
|
while (lines.Count <= cursorPosition.Y)
|
||||||
|
{
|
||||||
|
lines.Add("");
|
||||||
|
}
|
||||||
|
lines.Insert(cursorPosition.Y, "");
|
||||||
|
|
||||||
|
string line = lines[cursorPosition.Y + 1];
|
||||||
|
|
||||||
|
lines[cursorPosition.Y] = line.Substring(0, Math.Min(cursorPosition.X, line.Length));
|
||||||
|
lines[cursorPosition.Y + 1] = line.Substring(Math.Min(cursorPosition.X, line.Length));
|
||||||
|
|
||||||
|
cursorPosition.X = 0;
|
||||||
|
cursorPosition.Y++;
|
||||||
|
|
||||||
|
if (cursorPosition.Y - lineOffset >= Console.WindowHeight - 3)
|
||||||
|
{
|
||||||
|
lineOffset++;
|
||||||
|
}
|
||||||
|
Display(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (lines.Count <= cursorPosition.Y)
|
||||||
|
{
|
||||||
|
lines.Add("");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (lines[cursorPosition.Y].Length <= cursorPosition.X)
|
||||||
|
{
|
||||||
|
lines[cursorPosition.Y] += " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyInfo.Key == ConsoleKey.Backspace)
|
||||||
|
{
|
||||||
|
if (cursorPosition.X > 0)
|
||||||
|
{
|
||||||
|
lines[cursorPosition.Y] = lines[cursorPosition.Y].Remove(cursorPosition.X - 1, 1);
|
||||||
|
cursorPosition.X--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (cursorPosition.Y > 0)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(lines[cursorPosition.Y - 1]))
|
||||||
|
{
|
||||||
|
lines.RemoveAt(--cursorPosition.Y);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cursorPosition.Y--;
|
||||||
|
cursorPosition.X = lines[cursorPosition.Y].TrimEnd().Length;
|
||||||
|
lines[cursorPosition.Y] += lines[cursorPosition.Y + 1];
|
||||||
|
lines.RemoveAt(cursorPosition.Y + 1);
|
||||||
|
}
|
||||||
|
if (cursorPosition.Y - lineOffset < 0 && lineOffset > 0)
|
||||||
|
{
|
||||||
|
lineOffset--;
|
||||||
|
}
|
||||||
|
Display(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (lines.Count > 0 && string.IsNullOrWhiteSpace(lines[lines.Count - 1]))
|
||||||
|
{
|
||||||
|
lines.RemoveAt(lines.Count - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char input = keyInfo.KeyChar;
|
||||||
|
if (!char.IsControl(input))
|
||||||
|
{
|
||||||
|
lines[cursorPosition.Y] = lines[cursorPosition.Y].Insert(cursorPosition.X, input.ToString());
|
||||||
|
cursorPosition.X++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mode == Mode.Command)
|
||||||
|
{
|
||||||
|
Console.SetCursorPosition(3, Console.WindowHeight - 2);
|
||||||
|
|
||||||
|
string input = Console.ReadLine() ?? string.Empty;
|
||||||
|
string command = input.Split(' ')[0].Trim();
|
||||||
|
string path;
|
||||||
|
|
||||||
|
switch (command)
|
||||||
|
{
|
||||||
|
case "edit":
|
||||||
|
WriteStatus(string.Empty);
|
||||||
|
mode = Mode.Edit;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "save":
|
||||||
|
Save(input, false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "run":
|
||||||
|
if (Save(input, true))
|
||||||
|
{
|
||||||
|
Console.Clear();
|
||||||
|
yesNtInterpreter.Execute(currentPath);
|
||||||
|
Console.ReadKey();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "debug":
|
||||||
|
if (Save(input, true))
|
||||||
|
{
|
||||||
|
Console.Clear();
|
||||||
|
yesNtInterpreter.Execute(currentPath, true);
|
||||||
|
Console.ReadKey();
|
||||||
|
WriteStatus(string.Empty);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "load":
|
||||||
|
if (input.Split(' ').Length == 2)
|
||||||
|
{
|
||||||
|
path = input.Split(' ')[1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteStatus("Invalid arguments!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!File.Exists(path))
|
||||||
|
{
|
||||||
|
WriteStatus("File does not exist!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Load(path);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
WriteStatus(ex.Message);
|
||||||
|
}
|
||||||
|
lineOffset = 0;
|
||||||
|
cursorPosition.X = 0;
|
||||||
|
cursorPosition.Y = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "new":
|
||||||
|
if (Save(input, false))
|
||||||
|
{
|
||||||
|
lineOffset = 0;
|
||||||
|
cursorPosition.X = 0;
|
||||||
|
cursorPosition.Y = 0;
|
||||||
|
currentPath = string.Empty;
|
||||||
|
lines.Clear();
|
||||||
|
WriteStatus(string.Empty);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "exit":
|
||||||
|
return false;
|
||||||
|
|
||||||
|
default:
|
||||||
|
WriteStatus("Command not found!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Display(true);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Load(string path)
|
||||||
|
{
|
||||||
|
if (!File.Exists(path))
|
||||||
|
{
|
||||||
|
WriteStatus("File does not exist!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.Clear();
|
||||||
|
foreach (string line in File.ReadAllLines(path))
|
||||||
|
{
|
||||||
|
lines.Add(line.Replace("\n", ""));
|
||||||
|
}
|
||||||
|
currentPath = path;
|
||||||
|
WriteStatus("File Loaded!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Save(string input, bool loadIfExists)
|
||||||
|
{
|
||||||
|
string text = "";
|
||||||
|
string path = "";
|
||||||
|
|
||||||
|
foreach (string line in lines)
|
||||||
|
{
|
||||||
|
text += line + "\n";
|
||||||
|
}
|
||||||
|
text = text.TrimEnd('\n');
|
||||||
|
|
||||||
|
if (input.Split(' ').Length == 2)
|
||||||
|
{
|
||||||
|
path = input.Split(' ')[1];
|
||||||
|
|
||||||
|
if (currentPath.Trim() != path.Trim() && loadIfExists)
|
||||||
|
{
|
||||||
|
if (Load(path))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentPath = path;
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrWhiteSpace(currentPath))
|
||||||
|
{
|
||||||
|
path = currentPath;
|
||||||
|
}
|
||||||
|
else if (input.Split(' ').Length > 2)
|
||||||
|
{
|
||||||
|
WriteStatus("Invalid arguments!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteStatus("File path is empty!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.WriteAllText(path, text);
|
||||||
|
WriteStatus("File Saved!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
WriteStatus(ex.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteStatus(string input)
|
||||||
|
{
|
||||||
|
Console.SetCursorPosition(0, Console.WindowHeight - 1);
|
||||||
|
Console.Write(input + new string(' ', Console.WindowWidth - input.Length - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void YesNtInterpreter_OnDebugOutput(string output)
|
||||||
|
{
|
||||||
|
debugOutput.Add(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void YesNtInterpreter_OnLineExecuted(Interpreter.Runtime.DebugEventArgs e)
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = ConsoleColor.Magenta;
|
||||||
|
if (e.OriginalLine == e.CurrentLine)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{Environment.NewLine}[{e.LineNumber}] [{e.CurrentLine}] ==>");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{Environment.NewLine}[{e.LineNumber}] [{e.OriginalLine}] => [{e.CurrentLine}] ==>");
|
||||||
|
}
|
||||||
|
Console.ResetColor();
|
||||||
|
|
||||||
|
foreach (string output in debugOutput)
|
||||||
|
{
|
||||||
|
Console.Write(output);
|
||||||
|
}
|
||||||
|
debugOutput.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class Point
|
||||||
|
{
|
||||||
|
public int X { get; set; }
|
||||||
|
public int Y { get; set; }
|
||||||
|
|
||||||
|
public Point(int x, int y)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal enum Mode
|
||||||
|
{
|
||||||
|
Edit,
|
||||||
|
Command
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace YesNt.CodeEditor
|
||||||
|
{
|
||||||
|
internal class Program
|
||||||
|
{
|
||||||
|
private static void Main(string[] args)
|
||||||
|
{
|
||||||
|
TextEditor textEditor;
|
||||||
|
|
||||||
|
Console.CancelKeyPress += Console_CancelKeyPress;
|
||||||
|
if (args.Length > 0)
|
||||||
|
{
|
||||||
|
textEditor = new TextEditor(args[0]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
textEditor = new TextEditor();
|
||||||
|
}
|
||||||
|
|
||||||
|
textEditor.Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
|
||||||
|
{
|
||||||
|
e.Cancel = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
using YesNt.Interpreter.Enums;
|
||||||
|
using YesNt.Interpreter.Runtime;
|
||||||
|
using YesNt.Interpreter.Utilities;
|
||||||
|
|
||||||
|
namespace YesNt.CodeEditor
|
||||||
|
{
|
||||||
|
internal class SyntaxHighlighter
|
||||||
|
{
|
||||||
|
private ReadOnlyCollection<StatementInformation> statementInformation;
|
||||||
|
|
||||||
|
public SyntaxHighlighter(ReadOnlyCollection<StatementInformation> statementInformation)
|
||||||
|
{
|
||||||
|
this.statementInformation = statementInformation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(string input)
|
||||||
|
{
|
||||||
|
input = input.Replace("\0", string.Empty);
|
||||||
|
foreach (StatementInformation statement in statementInformation)
|
||||||
|
{
|
||||||
|
if (statement.IgnoreSyntaxHighlighting)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string name = statement.SpaceAround switch
|
||||||
|
{
|
||||||
|
SpaceAround.StartEnd => $" {statement.Name.Trim()} ",
|
||||||
|
SpaceAround.Start => $" {statement.Name.Trim()}",
|
||||||
|
SpaceAround.End => $"{statement.Name.Trim()} ",
|
||||||
|
_ => statement.Name
|
||||||
|
};
|
||||||
|
input = input.TrimEnd();
|
||||||
|
if (statement.SearchMode == SearchMode.StartOfLine && input.StartsWith(name))
|
||||||
|
{
|
||||||
|
input = AddColorInformation(input, input.Substring(0, name.Length), ConsoleColor.DarkGreen, statement.SearchMode);
|
||||||
|
}
|
||||||
|
if (statement.SearchMode == SearchMode.Contains && $"{input} ".Contains(name))
|
||||||
|
{
|
||||||
|
input = AddColorInformation($"{input} ", $"{input} ".Substring($"{input} ".IndexOf(name), name.Length), ConsoleColor.Green, statement.SearchMode);
|
||||||
|
}
|
||||||
|
if (statement.SearchMode == SearchMode.EndOfLine && input.EndsWith(name))
|
||||||
|
{
|
||||||
|
input = AddColorInformation(input, input.Substring(input.Length - name.Length), ConsoleColor.DarkYellow, statement.SearchMode);
|
||||||
|
}
|
||||||
|
if (statement.SearchMode == SearchMode.Exact && input.Equals(name))
|
||||||
|
{
|
||||||
|
input = AddColorInformation(input, input, ConsoleColor.Red, statement.SearchMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MatchCollection matches = Regex.Matches(input, @">[a-zA-Z0-9]+");
|
||||||
|
for (int i = 0; i < matches.Count; i++)
|
||||||
|
{
|
||||||
|
input = AddColorInformation(input, matches[i].Value, ConsoleColor.Cyan, SearchMode.Contains);
|
||||||
|
}
|
||||||
|
|
||||||
|
matches = Regex.Matches(input, @"^<[a-zA-Z0-9]+");
|
||||||
|
for (int i = 0; i < matches.Count; i++)
|
||||||
|
{
|
||||||
|
input = AddColorInformation(input, matches[i].Value, ConsoleColor.DarkCyan, SearchMode.StartOfLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (string part in input.Split("\0"))
|
||||||
|
{
|
||||||
|
ConsoleColor consoleColor;
|
||||||
|
string messagePart = part;
|
||||||
|
|
||||||
|
string stringColor = Regex.Match(messagePart, "(?<=(\\r))(.*)(?=\\r)").Value;
|
||||||
|
bool succ = int.TryParse(stringColor, out int colorIndex);
|
||||||
|
if (succ && colorIndex >= 0 && colorIndex < 16)
|
||||||
|
{
|
||||||
|
messagePart = messagePart.Replace($"\r{stringColor}\r", string.Empty);
|
||||||
|
consoleColor = (ConsoleColor)colorIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
consoleColor = ConsoleColor.White;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (consoleColor == Console.BackgroundColor || consoleColor == ConsoleColor.DarkGray)
|
||||||
|
{
|
||||||
|
consoleColor = ConsoleColor.White;
|
||||||
|
}
|
||||||
|
Console.ForegroundColor = consoleColor;
|
||||||
|
Console.Write(messagePart, consoleColor);
|
||||||
|
Console.ResetColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string AddColorInformation(string originalString, string value, ConsoleColor color, SearchMode searchMode)
|
||||||
|
{
|
||||||
|
int spacesAtEnd = value.WhiteSpaceAtEnd();
|
||||||
|
string reult = searchMode switch
|
||||||
|
{
|
||||||
|
SearchMode.StartOfLine => originalString.ReplaceFirstOccurrence(value, $"\0\r{(int)color}\r{value.TrimEnd()}\0" + new string(' ', spacesAtEnd)),
|
||||||
|
SearchMode.EndOfLine => originalString.ReplaceLastOccurrence(value, $"\0\r{(int)color}\r{value.TrimEnd()}\0" + new string(' ', spacesAtEnd)),
|
||||||
|
_ => originalString.Replace(value, $"\0\r{(int)color}\r{value.TrimEnd()}\0" + new string(' ', spacesAtEnd))
|
||||||
|
};
|
||||||
|
return reult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\YesNt-Interpreter\YesNt.Interpreter.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
Reference in New Issue
Block a user