diff --git a/.github/workflows/CSharp.yml b/.github/workflows/CSharp.yml new file mode 100644 index 0000000..3436067 --- /dev/null +++ b/.github/workflows/CSharp.yml @@ -0,0 +1,42 @@ +name: CI CSharp + +on: + push: + branches: ["main"] + paths: + - "CSharp/**" + - "grammar/*.g4" + pull_request: + branches: ["main"] + paths: + - "CSharp/**" + - "grammar/*.g4" + +jobs: + build-and-test: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./CSharp + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: "6.0.x" + + - name: Generate ANTLR4 files + run: sh generate.sh + working-directory: . + + - name: Restore + run: dotnet restore CSharp.sln + + - name: Build + run: dotnet build CSharp.sln --configuration Release --no-restore + + - name: Test + run: dotnet test CSharp.sln --configuration Release --no-build --verbosity normal diff --git a/CSharp/.gitignore b/CSharp/.gitignore index 21bc544..9dd07fb 100644 --- a/CSharp/.gitignore +++ b/CSharp/.gitignore @@ -1 +1,425 @@ -src/Generated \ No newline at end of file +Interpreter/Generated + +## 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 + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +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 +*.appxbundle +*.appxupload + +# 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 +*- Backup*.rdl + +# 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/ + +# 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/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + + + +## VS CODE +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +.idea + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser \ No newline at end of file diff --git a/CSharp/CSharp.csproj b/CSharp/CSharp.csproj new file mode 100644 index 0000000..d74dd02 --- /dev/null +++ b/CSharp/CSharp.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + enable + enable + + false + true + Arcweave + + + + + + + + + + + + diff --git a/CSharp/CSharp.sln b/CSharp/CSharp.sln new file mode 100644 index 0000000..c54d833 --- /dev/null +++ b/CSharp/CSharp.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharp", "CSharp.csproj", "{46E8C9BE-D0EC-440D-9FBB-034D1FD4E434}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {46E8C9BE-D0EC-440D-9FBB-034D1FD4E434}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {46E8C9BE-D0EC-440D-9FBB-034D1FD4E434}.Debug|Any CPU.Build.0 = Debug|Any CPU + {46E8C9BE-D0EC-440D-9FBB-034D1FD4E434}.Release|Any CPU.ActiveCfg = Release|Any CPU + {46E8C9BE-D0EC-440D-9FBB-034D1FD4E434}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/CSharp/Interpreter/ArcscriptExceptions.cs b/CSharp/Interpreter/ArcscriptExceptions.cs new file mode 100644 index 0000000..b3bb46d --- /dev/null +++ b/CSharp/Interpreter/ArcscriptExceptions.cs @@ -0,0 +1,23 @@ +namespace Arcweave.Interpreter; + +public class RuntimeException : Exception +{ + public RuntimeException(string message) : base(message) + { + } + + public RuntimeException(string message, Exception innerException) : base(message, innerException) + { + } +} + +public class ParseException : Exception +{ + public ParseException(string message) : base(message) + { + } + + public ParseException(string message, Exception innerException) : base(message, innerException) + { + } +} \ No newline at end of file diff --git a/CSharp/Interpreter/ArcscriptExpression.cs b/CSharp/Interpreter/ArcscriptExpression.cs new file mode 100644 index 0000000..bb9b67c --- /dev/null +++ b/CSharp/Interpreter/ArcscriptExpression.cs @@ -0,0 +1,375 @@ + +using System; +using System.Collections; + +namespace Arcweave.Interpreter +{ + public class Expression : ArcscriptExpressionBase, IComparable + { + public object Value { get; set; } + public Expression() { Value = null; } + public Expression(object value) { Value = value; } + public Expression(object value, Type type) { Value = Convert.ChangeType(value, type); } + public Expression(Expression expression) { Value = expression.Value; } + public void SetValue(object value) { Value = value; } + public Type Type() { return Value.GetType(); } + public static Expression operator +(Expression e) { return e; } + public static Expression operator -(Expression e) + { + if (e.Value is int i) + { + return new Expression(-i); + } + else if (e.Value is double value) + { + return new Expression(-value); + } + return e; + } + public static Expression operator +(Expression first, Expression second) + { + if (first.Type() == typeof(string) || second.Type() == typeof(string)) + { + return new Expression(first.ToString() + second.ToString()); + } + var doubleValues = GetDoubleValues(first.Value, second.Value); + if (!doubleValues.HasDouble) + { + var intValue = (int)(doubleValues.Value1 + doubleValues.Value2); + return new Expression(intValue); + } + else + { + return new Expression(doubleValues.Value1 + doubleValues.Value2); + } + } + + public static Expression operator -(Expression first, Expression second) + { + if (first.Type() == typeof(string) || second.Type() == typeof(string)) + { + throw new InvalidOperationException("Subtraction is not supported for string types."); + } + var doubleValues = GetDoubleValues(first.Value, second.Value); + if (!doubleValues.HasDouble) + { + var intValue = (int) (doubleValues.Value1 - doubleValues.Value2); + return new Expression(intValue); + } else + { + return new Expression(doubleValues.Value1 - doubleValues.Value2); + } + } + + public static Expression operator *(Expression first, Expression second) + { + if (first.Type() == typeof(string) || second.Type() == typeof(string)) + { + throw new InvalidOperationException("Multiplication is not supported for string types."); + } + var doubleValues = GetDoubleValues(first.Value, second.Value); + if (!doubleValues.HasDouble) + { + var intValue = (int)(doubleValues.Value1 * doubleValues.Value2); + return new Expression(intValue); + } + else + { + return new Expression(doubleValues.Value1 * doubleValues.Value2); + } + } + + public static Expression operator /(Expression first, Expression second) + { + if (first.Type() == typeof(string) || second.Type() == typeof(string)) + { + throw new InvalidOperationException("Division is not supported for string types."); + } + var doubleValues = GetDoubleValues(first.Value, second.Value); + var result = doubleValues.Value1 / doubleValues.Value2; + if (double.IsInfinity(result)) + { + throw new DivideByZeroException("Division by zero."); + } + return new Expression(result); + } + + public static bool operator ==(Expression first, Expression second) + { + System.Type type1 = first.Type(); + if (type1 == typeof(int) || type1 == typeof(double)) + { + DoubleValues doubleValues = GetDoubleValues(first.Value, second.Value); + return doubleValues.Value1 == doubleValues.Value2; + } + if (type1 == typeof(bool)) + { + return (bool)first.Value == (bool)second.Value; + } + if (type1 == typeof(string)) + { + return (string)first.Value == (string)second.Value; + } + return false; + } + + public int CompareTo(object other) + { + Expression o = (Expression)other; + DoubleValues fValues = GetDoubleValues(this.Value, o.Value); + double result = fValues.Value1 - fValues.Value2; + if (result < 0) + { + return -1; + } else if (result > 0) + { + return 1; + } + return 0; + } + + public override bool Equals(object obj) + { + if (obj == null || !(obj is Expression)) + { + return false; + } + Expression e = (Expression)obj; + + System.Type type1 = Type(); + if (type1 == typeof(int) || type1 == typeof(double)) + { + DoubleValues doubleValues = GetDoubleValues(Value, e.Value); + return doubleValues.Value1 == doubleValues.Value2; + } + if (type1 == typeof(bool)) + { + return (bool)Value == (bool)e.Value; + } + if (type1 == typeof(string)) + { + return (string)Value == (string)e.Value; + } + return false; + } + + public static bool operator !=(Expression first, Expression second) + { + System.Type type1 = first.Type(); + if (type1 == typeof(int) || type1 == typeof(double)) + { + DoubleValues doubleValues = GetDoubleValues(first.Value, second.Value); + return doubleValues.Value1 != doubleValues.Value2; + } + if (type1 == typeof(bool)) + { + return (bool)first.Value != (bool)second.Value; + } + if (type1 == typeof(string)) + { + return (string)first.Value != (string)second.Value; + } + return true; + } + public static bool operator ==(Expression first, bool second) + { + return GetBoolValue(first.Value) == second; + } + public static bool operator !=(Expression first, bool second) + { + return GetBoolValue(first.Value) != second; + } + public static bool operator ==(Expression first, int second) + { + DoubleValues doubleValues = GetDoubleValues(first.Value, second); + return doubleValues.Value1 == doubleValues.Value2; + } + public static bool operator !=(Expression first, int second) + { + DoubleValues doubleValues = GetDoubleValues(first.Value, second); + return doubleValues.Value1 != doubleValues.Value2; + } + public static bool operator ==(Expression first, double second) + { + DoubleValues doubleValues = GetDoubleValues(first.Value, second); + return doubleValues.Value1 == doubleValues.Value2; + } + public static bool operator !=(Expression first, double second) + { + DoubleValues doubleValues = GetDoubleValues(first.Value, second); + return doubleValues.Value1 != doubleValues.Value2; + } + public static bool operator ==(Expression first, string second) + { + return (string)first.Value == second; + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + public static bool operator !=(Expression first, string second) + { + return (string)first.Value == second; + } + + public static bool operator >(Expression first, Expression second) + { + DoubleValues doubleValues = GetDoubleValues(first.Value, second.Value); + return doubleValues.Value1 > doubleValues.Value2; + } + public static bool operator <(Expression first, Expression second) + { + DoubleValues doubleValues = GetDoubleValues(first.Value, second.Value); + return doubleValues.Value1 < doubleValues.Value2; + } + public static bool operator >=(Expression first, Expression second) + { + DoubleValues doubleValues = GetDoubleValues(first.Value, second.Value); + return doubleValues.Value1 >= doubleValues.Value2; + } + public static bool operator <=(Expression first, Expression second) + { + DoubleValues doubleValues = GetDoubleValues(first.Value, second.Value); + return doubleValues.Value1 <= doubleValues.Value2; + } + public static bool operator >(Expression first, int second) + { + DoubleValues doubleValues = GetDoubleValues(first.Value, second); + return doubleValues.Value1 > doubleValues.Value2; + } + public static bool operator <(Expression first, int second) + { + DoubleValues doubleValues = GetDoubleValues(first.Value, second); + return doubleValues.Value1 < doubleValues.Value2; + } + public static bool operator >=(Expression first, int second) + { + DoubleValues doubleValues = GetDoubleValues(first.Value, second); + return doubleValues.Value1 >= doubleValues.Value2; + } + public static bool operator <=(Expression first, int second) + { + DoubleValues doubleValues = GetDoubleValues(first.Value, second); + return doubleValues.Value1 <= doubleValues.Value2; + } + public static bool operator >(Expression first, double second) + { + DoubleValues doubleValues = GetDoubleValues(first.Value, second); + return doubleValues.Value1 > doubleValues.Value2; + } + public static bool operator <(Expression first, double second) + { + DoubleValues doubleValues = GetDoubleValues(first.Value, second); + return doubleValues.Value1 < doubleValues.Value2; + } + public static bool operator >=(Expression first, double second) + { + DoubleValues doubleValues = GetDoubleValues(first.Value, second); + return doubleValues.Value1 >= doubleValues.Value2; + } + public static bool operator <=(Expression first, double second) + { + DoubleValues doubleValues = GetDoubleValues(first.Value, second); + return doubleValues.Value1 <= doubleValues.Value2; + } + public static Expression operator !(Expression e) + { + return new Expression(!GetBoolValue(e.Value)); + } + public static bool operator true(Expression e) + { + return GetBoolValue(e.Value); + } + public static bool operator false(Expression e) + { + return !GetBoolValue(e.Value); + } + public static Expression operator &(Expression first, Expression second) + { + if (GetBoolValue(first.Value) && GetBoolValue(second.Value)) + { + return new Expression(true); + } + return new Expression(false); + } + + public static Expression operator |(Expression first, Expression second) + { + if (GetBoolValue(first.Value) || GetBoolValue(second.Value)) + { + return new Expression(true); + } + return new Expression(false); + } + + public override string ToString() + { + if (Value.GetType() == typeof(bool)) + { + if ((bool)Value) + { + return "true"; + } + return "false"; + } + + if (Value.GetType() == typeof(double)) + { + return ((double)Value).ToString(NumberFormat); + } + return Value.ToString(); + } + + private struct DoubleValues + { + public double Value1; + public double Value2; + public bool HasDouble; + + public DoubleValues(double val1, double val2, bool hasDouble) { Value1 = val1; Value2 = val2; HasDouble = hasDouble; } + } + + private static DoubleValues GetDoubleValues(object first, object second) { + bool hasDouble = false; + int value1, value2; + double flValue1 = 0, flValue2 = 0; + if (first.GetType() == typeof(int)) + { + value1 = (int)first; + flValue1 = value1; + } else if (first.GetType() == typeof(double)) + { + hasDouble = true; + flValue1 = (double)first; + } else if (first.GetType() == typeof(bool)) + { + flValue1 = (bool)first ? 1 : 0; + } + + if (second.GetType() == typeof(int)) + { + value2 = (int)second; + flValue2 = value2; + } else if (second.GetType() == typeof(double)) + { + hasDouble = true; + flValue2 = (double)second; + } else if (second.GetType() == typeof(bool)) + { + flValue2 = (bool)second ? 1 : 0; + } + + return new DoubleValues(flValue1, flValue2, hasDouble); + } + + internal static bool GetBoolValue(object value) + { + if (value.GetType() == typeof(bool)) { return (bool)value; } + if (value.GetType() == typeof(string)) { return ((string)value).Length > 0; } + if (value.GetType() == typeof(int)) { return (int)value > 0; } + if (value.GetType() == typeof(double)) { return (double)value > 0; } + return false; + } + } +} \ No newline at end of file diff --git a/CSharp/Interpreter/ArcscriptExpressionBase.cs b/CSharp/Interpreter/ArcscriptExpressionBase.cs new file mode 100644 index 0000000..8dc0958 --- /dev/null +++ b/CSharp/Interpreter/ArcscriptExpressionBase.cs @@ -0,0 +1,16 @@ +using System.Globalization; + +namespace Arcweave.Interpreter; + +public class ArcscriptExpressionBase +{ + public NumberFormatInfo NumberFormat { get; private set; } + public ArcscriptExpressionBase() + { + NumberFormat = new NumberFormatInfo + { + NumberDecimalSeparator = ".", + NumberGroupSeparator = "," + }; + } +} \ No newline at end of file diff --git a/CSharp/Interpreter/ArcscriptFunctions.cs b/CSharp/Interpreter/ArcscriptFunctions.cs new file mode 100644 index 0000000..20605e0 --- /dev/null +++ b/CSharp/Interpreter/ArcscriptFunctions.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Arcweave.Interpreter.INodes; + +namespace Arcweave.Interpreter +{ + public class Functions + { + private static readonly Random _getrandom = new Random(); + private IProject _project; + private string elementId; + private ArcscriptState state; + public Dictionary, object>> functions { get; private set; } = new Dictionary, object>>(); + + public Dictionary returnTypes = new Dictionary() + { + { "sqrt", typeof (double) }, + { "sqr", typeof (double) }, + { "abs", typeof (double) }, + { "random", typeof (double) }, + { "roll", typeof (int) }, + { "show", typeof (string) }, + { "reset", typeof (void) }, + { "resetAll", typeof (void) }, + { "round", typeof (int) }, + { "min", typeof (double) }, + { "max", typeof (double) }, + { "visits", typeof (int) } + }; + + public Functions(string elementId, IProject project, ArcscriptState state) { + this._project = project; + this.elementId = elementId; + this.state = state; + + this.functions["sqrt"] = this.Sqrt; + this.functions["sqr"] = this.Sqr; + this.functions["abs"] = this.Abs; + this.functions["random"] = this.Random; + this.functions["roll"] = this.Roll; + this.functions["show"] = this.Show; + this.functions["reset"] = this.Reset; + this.functions["resetAll"] = this.ResetAll; + this.functions["round"] = this.Round; + this.functions["min"] = this.Min; + this.functions["max"] = this.Max; + this.functions["visits"] = this.Visits; + } + + public object Sqrt(IList args) { + Expression e = args[0] as Expression; + double n; + if (e.Value is int i) + { + n = i; + } else if (e.Value is double d) + { + n = d; + } + else + { + n = (double)e.Value; + } + var result = Math.Sqrt(n); + if (double.IsNaN(result)) + { + throw new InvalidOperationException("Cannot compute square root of a negative number."); + } + return result; + } + + public object Sqr(IList args) { + Expression e = args[0] as Expression; + double n; + if (e.Value is int i) + { + n = i; + } else if (e.Value is double d) + { + n = d; + } + else + { + n = (double)e.Value; + } + return n * n; + } + + public object Abs(IList args) { + Expression e = args[0] as Expression; + double n; + if (e.Value is int i) + { + n = i; + } else if (e.Value is double d) + { + n = d; + } + else + { + n = (double)e.Value; + } + return Math.Abs(n); + } + + public object Random(IList args) { + lock ( _getrandom ) { + return _getrandom.NextDouble(); + } + } + + public object Roll(IList args) { + Expression e = args[0] as Expression; + int maxRoll = (int)e.Value; + int numRolls = 1; + if ( args.Count == 2 ) { + Expression e2 = args[1] as Expression; + numRolls = (int)e2.Value; + } + int sum = 0; + for ( int i = 0; i < numRolls; i++ ) { + int oneRoll = _getrandom.Next(1, maxRoll + 1); + sum += oneRoll; + } + return sum; + } + + public object Show(IList args) { + List results = new List(); + foreach (Expression arg in args ) { + results.Add(arg.ToString()); + } + string result = String.Join("", results.ToArray()); + // Replace escaped sequences with their actual characters + result = result + .Replace("\\a", "\a") + .Replace("\\b", "\b") + .Replace("\\f", "\f") + .Replace("\\n", "\n") + .Replace("\\r", "\r") + .Replace("\\t", "\t") + .Replace("\\v", "\v") + .Replace("\\'", "'") + .Replace("\\\"", "\"") + .Replace("\\\\", "\\"); + this.state.Outputs.AddScriptOutput(result); + return null; + } + + public object Reset(IList args) { + foreach (IVariable argument in args ) { + state.SetVarValue(argument.Name, argument.DefaultValue); + } + return null; + } + + public object ResetAll(IList args) { + List variableNames = new List(); + foreach ( IVariable argument in args ) { + variableNames.Add(argument.Name); + } + foreach ( IVariable variable in this._project.Variables ) { + if ( !variableNames.Contains(variable.Name) ) { + state.SetVarValue(variable.Name, variable.DefaultValue); + } + } + return null; + } + + public object Round(IList args) { + Expression e = args[0] as Expression; + double n = (double)e.Value; + return (int)Math.Round(n); + } + + public object Min(IList args) + { + IList arguments = args.Cast().ToList(); + return arguments.Min(); + } + + public object Max(IList args) { + IList arguments = args.Cast().ToList(); + return arguments.Max(); + } + + public object Visits(IList args) { + string elementId = this.elementId; + if ( args != null && args.Count == 1 ) { + ArcscriptVisitor.Mention mention = (ArcscriptVisitor.Mention)args[0]; + elementId = mention.attrs["data-id"]; + } + IElement element = this._project.ElementWithId(elementId); + return element.Visits; + } + } +} \ No newline at end of file diff --git a/CSharp/Interpreter/ArcscriptOutputs.cs b/CSharp/Interpreter/ArcscriptOutputs.cs new file mode 100644 index 0000000..fe658a2 --- /dev/null +++ b/CSharp/Interpreter/ArcscriptOutputs.cs @@ -0,0 +1,162 @@ +#nullable enable +using System.Collections.Generic; +using System.Linq; + +namespace Arcweave.Interpreter +{ + public interface IOutputNode + { + public string GetText(); + public void MergeScriptOutput(string text); + } + + public interface IHasParagraphs + { + public void AppendParagraph(string text); + } + + public class Paragraph : IOutputNode + { + private string _text; + + public Paragraph(string text) + { + _text = text; + } + + public void MergeScriptOutput(string text) + { + if (text.Length > 0) + { + if (_text.Length > 0) + { + this._text += ' ' + text; + } + else + { + this._text = text; + } + + } + } + + public string GetText() + { + return "

" + _text + "

"; + } + } + + public class Blockquote : IHasParagraphs, IOutputNode + { + public List Paragraphs { get; set; } = new(); + + public void AppendParagraph(string text) + { + Paragraphs.Add(new Paragraph(text)); + } + + public void MergeScriptOutput(string text) + { + if (Paragraphs.Count == 0) + { + AppendParagraph(text); + return; + } + + Paragraphs.Last().MergeScriptOutput(text); + } + + public string GetText() + { + var output = ""; + foreach (var p in Paragraphs) + { + output += p.GetText(); + } + + return "
" + output + "
"; + } + } + + public class ArcscriptOutputs : IHasParagraphs + { + private List Outputs { get; } = new(); + private IHasParagraphs? _currentNode; + private bool _addedScript; + + public void AppendParagraph(string text) + { + Outputs.Add(new Paragraph(text)); + } + + public void AddParagraph(string text) + { + _currentNode ??= this; + + if (_addedScript) + { + if (Outputs.Count > 0 && Outputs.Last() is Blockquote && _currentNode is not Blockquote) + { + AppendParagraph(text); + } + else + { + AddScriptOutput(text); + } + _addedScript = false; + return; + } + _currentNode.AppendParagraph(text); + } + + public void AddBlockquote() + { + if (_addedScript && Outputs.Count > 0) + { + IOutputNode n = Outputs.Last(); + if (n is Blockquote blockquote) + { + _currentNode = blockquote; + return; + } + } + Blockquote b = new Blockquote(); + Outputs.Add(b); + _currentNode = b; + } + + public void AddScriptOutput(string? text) + { + _addedScript = true; + if (text == null) + { + return; + } + if (Outputs.Count == 0) + { + Outputs.Add(new Paragraph(text)); + return; + } + + var n = Outputs.Last(); + n.MergeScriptOutput(text); + } + + public void ExitBlockquote() + { + _currentNode = this; + } + + public string GetText() + { + var output = ""; + foreach (var o in Outputs) + { + output += o.GetText(); + } + + return output; + } + } +} + diff --git a/CSharp/Interpreter/ArcscriptParserBase.cs b/CSharp/Interpreter/ArcscriptParserBase.cs new file mode 100644 index 0000000..0640205 --- /dev/null +++ b/CSharp/Interpreter/ArcscriptParserBase.cs @@ -0,0 +1,135 @@ +using System.Collections.Generic; +using Antlr4.Runtime; +using System.IO; +using System.Linq; +using Arcweave.Interpreter.INodes; + +namespace Arcweave.Interpreter +{ + public class ArcscriptParserBase : Parser + { + private class FunctionArgs + { + public int? MinArgs { get; set; } + public int? MaxArgs { get; set; } + } + + private Dictionary ArcscriptFunctions; + private protected IProject Project { get; private set; } + + internal int currentLine; + internal int openTagEndPos; + public ArcscriptParserBase(ITokenStream input) : base(input) { + var functions = new Dictionary() + { + { "abs", new FunctionArgs { MinArgs=1, MaxArgs=1 } }, + { "max", new FunctionArgs { MinArgs=2 } }, + { "min", new FunctionArgs { MinArgs=2 } }, + { "random", new FunctionArgs { MinArgs=0, MaxArgs=0 } }, + { "roll", new FunctionArgs { MinArgs=1, MaxArgs=2 } }, + { "round", new FunctionArgs { MinArgs=1, MaxArgs=1 } }, + { "sqr", new FunctionArgs { MinArgs=1, MaxArgs=1 } }, + { "sqrt", new FunctionArgs { MinArgs=1, MaxArgs=1 } }, + { "visits", new FunctionArgs { MinArgs=0, MaxArgs=1 } }, + { "show", new FunctionArgs { MinArgs=1 } }, + { "reset", new FunctionArgs { MinArgs=1 } }, + { "resetAll", new FunctionArgs { MinArgs=0 } }, + }; + + this.ArcscriptFunctions = functions; + } + + public ArcscriptParserBase(ITokenStream input, TextWriter output, TextWriter errorOutput) : base(input, output, errorOutput) { + var functions = new Dictionary() + { + { "abs", new FunctionArgs { MinArgs=1, MaxArgs=1 } }, + { "max", new FunctionArgs { MinArgs=2 } }, + { "min", new FunctionArgs { MinArgs=2 } }, + { "random", new FunctionArgs { MinArgs=0, MaxArgs=0 } }, + { "roll", new FunctionArgs { MinArgs=1, MaxArgs=2 } }, + { "round", new FunctionArgs { MinArgs=1, MaxArgs=1 } }, + { "sqr", new FunctionArgs { MinArgs=1, MaxArgs=1 } }, + { "sqrt", new FunctionArgs { MinArgs=1, MaxArgs=1 } }, + { "visits", new FunctionArgs { MinArgs=0, MaxArgs=1 } }, + { "show", new FunctionArgs { MinArgs=1 } }, + { "reset", new FunctionArgs { MinArgs=1 } }, + { "resetAll", new FunctionArgs { MinArgs=0 } }, + }; + + this.ArcscriptFunctions = functions; + } + + public void SetProject(IProject project) { + this.Project = project; + } + + public override string[] RuleNames => throw new System.NotImplementedException(); + + public override IVocabulary Vocabulary => throw new System.NotImplementedException(); + + public override string GrammarFileName => throw new System.NotImplementedException(); + + public bool assertVariable(IToken variable) { + var variableName = variable.Text; + var found = this.Project.Variables.First(x => x.Name == variableName); + if ( found != null ) { + return false; + } + return true; + } + + public bool assertMention(IList attrCtxList) { + Dictionary attrs = new Dictionary(); + foreach ( var attrCtx in attrCtxList ) { + string attrName = attrCtx.GetToken(ArcscriptParser.ATTR_NAME, 0)?.GetText(); + string attrValue = attrCtx.GetToken(ArcscriptParser.ATTR_VALUE, 0)?.GetText() ?? ""; + if ( attrValue.StartsWith("\"") && attrValue.EndsWith("\"") ) { + attrValue = attrValue.Substring(1, attrValue.Length - 2); + } else if ( attrValue.StartsWith("'") && attrValue.EndsWith("'") ) { + attrValue = attrValue.Substring(1, attrValue.Length - 2); + } + attrs.Add(attrName, attrValue); + } + string[] classList = attrs["class"].Split(" "); + if ( !classList.Contains("mention") ) { + return false; + } + if ( attrs["data-type"] != "element" ) { + return false; + } + if ( this.Project.ElementWithId(attrs["data-id"]) == null ) { + return false; + } + return true; + } + + public bool assertFunctionArguments(IToken fname, ArcscriptParser.Argument_listContext argumentList) { + int argListLength = 0; + if ( argumentList != null && argumentList.argument() != null ) { + argListLength = argumentList.argument().Length; + } + var min = this.ArcscriptFunctions[fname.Text].MinArgs; + var max = this.ArcscriptFunctions[fname.Text].MaxArgs; + if ( ( min != null && argListLength < min ) || ( max != null && argListLength > max ) ) { + throw new RecognitionException("Incorrect number of arguments for function " + fname.Text, this, this.InputStream, this.Context); + } + return true; + } + + public bool assertFunctionArguments(IToken fname, ArcscriptParser.Variable_listContext variable_List) { + int varListLength = variable_List.VARIABLE().Length; + var min = this.ArcscriptFunctions[fname.Text].MinArgs; + var max = this.ArcscriptFunctions[fname.Text].MaxArgs; + if ( ( min != null && varListLength < min ) || ( max != null && varListLength > max ) ) { + throw new RecognitionException("Incorrect number of arguments for function " + fname.Text, this, this.InputStream, this.Context); + } + return true; + } + + internal void setLineStart(IToken token) + { + openTagEndPos = token.StartIndex + token.Text.Length; + } + } + +} \ No newline at end of file diff --git a/CSharp/Interpreter/ArcscriptState.cs b/CSharp/Interpreter/ArcscriptState.cs new file mode 100644 index 0000000..378ac62 --- /dev/null +++ b/CSharp/Interpreter/ArcscriptState.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using System.Linq; +using Arcweave.Interpreter.INodes; + +namespace Arcweave.Interpreter +{ + public class ArcscriptState + { + public Dictionary VariableChanges = new Dictionary(); + // public List outputs = new List(); + public ArcscriptOutputs Outputs; + public string currentElement { get; set; } + public IProject project { get; set; } + public ArcscriptState(string elementId, IProject project) + { + Outputs = new ArcscriptOutputs(); + this.currentElement = elementId; + this.project = project; + } + + public IVariable GetVariable(string name) { + try + { + return this.project.Variables.First(variable => variable.Name == name); + } + catch (System.InvalidOperationException) + { + return null; + } + } + + public object GetVarValue(string name) { + if ( this.VariableChanges.ContainsKey(name) ) { + return VariableChanges[name]; + } + return this.project.GetVariable(name).ObjectValue; + } + + public void SetVarValue(string name, object value) { VariableChanges[name] = value; } + + public void SetVarValues(string[] names, string[] values) { + for ( int i = 0; i < names.Length; i++ ) { + this.VariableChanges[names[i]] = values[i]; + } + } + } +} \ No newline at end of file diff --git a/CSharp/Interpreter/ArcscriptTranspiler.cs b/CSharp/Interpreter/ArcscriptTranspiler.cs new file mode 100644 index 0000000..2d22f7f --- /dev/null +++ b/CSharp/Interpreter/ArcscriptTranspiler.cs @@ -0,0 +1,105 @@ +using Antlr4.Runtime; +using System.Collections.Generic; +using Arcweave.Interpreter.INodes; + +namespace Arcweave.Interpreter +{ + + public class AwInterpreter + { + private IProject Project { get; set; } + private string ElementId { get; set; } + + public AwInterpreter(IProject project, string elementId = "") + { + this.Project = project; + this.ElementId = elementId; + } + + private ArcscriptParser.InputContext GetParseTree(string code) + { + ICharStream stream = CharStreams.fromString(code); + ArcscriptLexer lexer = new ArcscriptLexer(stream); + var lexerErrorListener = new ErrorListener(); + lexer.AddErrorListener(lexerErrorListener); + ITokenStream tokens = new CommonTokenStream(lexer); + var parserErrorListener = new ErrorListener(); + ArcscriptParser parser = new ArcscriptParser(tokens); + parser.AddErrorListener(parserErrorListener); + parser.SetProject(Project); + + ArcscriptParser.InputContext tree = parser.input(); + + if (lexerErrorListener.HasErrors) + { + throw new ParseException("Lexing errors:\n" + string.Join("\n", lexerErrorListener.Errors)); + } + if (parserErrorListener.HasErrors) + { + throw new ParseException("Parsing errors:\n" + string.Join("\n", parserErrorListener.Errors)); + } + + return tree; + } + + public TranspilerOutput RunScript(string code) + { + if (string.IsNullOrEmpty(code)) + { + return new TranspilerOutput(); + } + + ArcscriptParser.InputContext tree; + try + { + tree = this.GetParseTree(code); + } + catch (Exception e) + { + throw new ParseException(e.Message, e); + } + + ArcscriptVisitor visitor = new ArcscriptVisitor(this.ElementId, this.Project); + object result; + try + { + result = tree.Accept(visitor); + } + catch (System.Exception e) + { + throw new RuntimeException($"Error interpreting Arcscript code: {e.Message}\nCode:\n{code}", e); + } + + // List outputs = visitor.state.outputs; + var outputResult = visitor.state.Outputs.GetText(); + + var isCondition = tree.script() != null; + + return new TranspilerOutput(outputResult, visitor.state.VariableChanges, result, isCondition); + } + + public class TranspilerOutput + { + public string Output { get; private set; } + public Dictionary Changes { get; private set; } + public object Result { get; private set; } + public bool IsCondition { get; private set; } + + public TranspilerOutput() + { + Result = false; + Output = ""; + Changes = new Dictionary(); + IsCondition = false; + } + public TranspilerOutput(string output, Dictionary changes, object result, + bool isCondition = false) + { + this.Result = result; + this.Output = output; + this.Changes = changes; + IsCondition = isCondition; + } + } + } +} \ No newline at end of file diff --git a/CSharp/Interpreter/ArcscriptVisitor.cs b/CSharp/Interpreter/ArcscriptVisitor.cs new file mode 100644 index 0000000..741050a --- /dev/null +++ b/CSharp/Interpreter/ArcscriptVisitor.cs @@ -0,0 +1,470 @@ +using Antlr4.Runtime.Misc; +using System.Globalization; +using Antlr4.Runtime.Tree; +using Arcweave.Interpreter.INodes; + +namespace Arcweave.Interpreter +{ + public class ArcscriptVisitor : ArcscriptParserBaseVisitor + { + public IProject project; + public readonly ArcscriptState state; + public string elementId; + private readonly Functions _functions; + public ArcscriptVisitor(string elementId, IProject project) { + this.elementId = elementId; + this.project = project; + this.state = new ArcscriptState(elementId, project); + this._functions = new Functions(elementId, project, this.state); + } + + public override object VisitInput([NotNull] ArcscriptParser.InputContext context) { + if ( context.script() != null ) { + return this.VisitScript(context.script()); + } + + Expression comp_cond = (Expression)this.VisitCompound_condition_or(context.compound_condition_or()); + return Expression.GetBoolValue(comp_cond.Value); + } + + public override object VisitScript_section([NotNull] ArcscriptParser.Script_sectionContext context) { + if ( context == null ) { + return null; + } + + var blockquoteContexts = context.blockquote(); + if (blockquoteContexts != null && blockquoteContexts.Length > 0) + { + object[] result = new object[blockquoteContexts.Length]; + int index = 0; + foreach (var blockquoteContext in blockquoteContexts) + { + result[index++] = this.VisitBlockquote(blockquoteContext); + } + return result; + } + + var paragraphContexts = context.paragraph(); + if (paragraphContexts != null && paragraphContexts.Length > 0) + { + object[] result = new object[paragraphContexts.Length]; + int index = 0; + foreach (var paragraphContext in paragraphContexts) + { + result[index++] = this.VisitParagraph(paragraphContext); + } + return result; + } + // if ( context.normal_text() != null && context.normal_text().Length > 0 ) { + // this.state.outputs.Add(context.GetText()); + // return context.GetText(); + // } + + return this.VisitChildren(context); + } + + public override object VisitParagraph(ArcscriptParser.ParagraphContext context) + { + // this.state.outputs.Add(context.GetText()); + var paragraphEnd = context.PARAGRAPHEND().GetText(); + var paragraphContent = paragraphEnd.Substring(0, paragraphEnd.Length - "

".Length); + this.state.Outputs.AddParagraph(paragraphContent); + return context.GetText(); + } + + public override object VisitBlockquote(ArcscriptParser.BlockquoteContext context) + { + this.state.Outputs.AddBlockquote(); + this.VisitChildren(context); + this.state.Outputs.ExitBlockquote(); + return context.GetText(); + } + + public override object VisitAssignment_segment([NotNull] ArcscriptParser.Assignment_segmentContext context) { + this.state.Outputs.AddScriptOutput(null); + return this.VisitStatement_assignment(context.statement_assignment()); + } + + public override object VisitFunction_call_segment([NotNull] ArcscriptParser.Function_call_segmentContext context) { + this.state.Outputs.AddScriptOutput(null); + return this.VisitStatement_function_call(context.statement_function_call()); + } + + public override object VisitConditional_section([NotNull] ArcscriptParser.Conditional_sectionContext context) { + this.state.Outputs.AddScriptOutput(null); + ConditionalSection if_section = (ConditionalSection)this.VisitIf_section(context.if_section()); + if ( if_section.Clause ) { + this.state.Outputs.AddScriptOutput(null); + return if_section.Script; + } + foreach(ArcscriptParser.Else_if_sectionContext else_if_context in context.else_if_section()) + { + ConditionalSection elif_section = (ConditionalSection)this.VisitElse_if_section(else_if_context); + if (elif_section.Clause ) + { + return elif_section.Script; + } + } + + if ( context.else_section() != null ) { + ConditionalSection elseSection = (ConditionalSection)this.VisitElse_section(context.else_section()); + this.state.Outputs.AddScriptOutput(null); + return elseSection.Script; + } + this.state.Outputs.AddScriptOutput(null); + return null; + } + + public override object VisitIf_section([NotNull] ArcscriptParser.If_sectionContext context) { + Expression result = (Expression)this.VisitIf_clause(context.if_clause()); + ConditionalSection ifSection = new ConditionalSection(false, null); + if ( result ) { + ifSection.Clause = true; + ifSection.Script = this.VisitScript(context.script()); + } + return ifSection; + } + + public override object VisitElse_if_section([NotNull] ArcscriptParser.Else_if_sectionContext context) { + Expression result = (Expression)this.VisitElse_if_clause(context.else_if_clause()); + ConditionalSection elseIfSection = new ConditionalSection(false, null); + if ( result ) { + elseIfSection.Clause = true; + elseIfSection.Script = this.VisitScript(context.script()); + } + return elseIfSection; + } + + public override object VisitElse_section([NotNull] ArcscriptParser.Else_sectionContext context) { + return new ConditionalSection(true, this.VisitScript(context.script())); + } + + public override object VisitIf_clause([NotNull] ArcscriptParser.If_clauseContext context) { + return this.VisitCompound_condition_or(context.compound_condition_or()); + } + + public override object VisitElse_if_clause([NotNull] ArcscriptParser.Else_if_clauseContext context) { + return this.VisitCompound_condition_or(context.compound_condition_or()); + } + + public override object VisitStatement_assignment([NotNull] ArcscriptParser.Statement_assignmentContext context) { + string variableName = context.VARIABLE().GetText(); + IVariable variable = this.state.GetVariable(variableName); + + Expression compound_condition_or = (Expression)this.VisitCompound_condition_or(context.compound_condition_or()); + if ( context.ASSIGN() != null ) { + this.state.SetVarValue(variableName, compound_condition_or.Value); + return null; + } + + Expression variableValue = new Expression(this.state.GetVarValue(variableName)); + + if ( context.ASSIGNADD() != null ) { + variableValue += compound_condition_or; + } else if ( context.ASSIGNSUB() != null ) { + variableValue -= compound_condition_or; + } else if ( context.ASSIGNMUL() != null ) { + variableValue *= compound_condition_or; + } else if ( context.ASSIGNDIV() != null ) { + variableValue /= compound_condition_or; + } + + this.state.SetVarValue(variableName, variableValue.Value); + return null; + } + + public override object VisitCompound_condition_or([NotNull] ArcscriptParser.Compound_condition_orContext context) { + Expression compound_condition_and = (Expression)this.VisitCompound_condition_and(context.compound_condition_and()); + if ( context.compound_condition_or() != null ) { + Expression compound_condition_or = (Expression)this.VisitCompound_condition_or(context.compound_condition_or()); + Expression result = compound_condition_and || compound_condition_or; + return result; + } + return compound_condition_and; + } + + public override object VisitCompound_condition_and([NotNull] ArcscriptParser.Compound_condition_andContext context) { + Expression negated_unary_condition = (Expression)this.VisitNegated_unary_condition(context.negated_unary_condition()); + if ( context.compound_condition_and() != null ) { + Expression compound_condition_and = (Expression)this.VisitCompound_condition_and(context.compound_condition_and()); + Expression result = negated_unary_condition && compound_condition_and; + return result; + } + + return negated_unary_condition; + } + + public override object VisitNegated_unary_condition([NotNull] ArcscriptParser.Negated_unary_conditionContext context) { + Expression unary_condition = (Expression)this.VisitUnary_condition(context.unary_condition()); + + if ( context.NEG() != null || context.NOTKEYWORD() != null ) { + return !unary_condition; + } + + return unary_condition; + } + + public override object VisitUnary_condition([NotNull] ArcscriptParser.Unary_conditionContext context) { + return this.VisitCondition(context.condition()); + } + + public override object VisitCondition([NotNull] ArcscriptParser.ConditionContext context) { + if ( context.expression().Length == 1 ) { + return this.VisitExpression(context.expression()[0]); + } + ArcscriptParser.Conditional_operatorContext conditional_operator_context = context.conditional_operator(); + Expression exp0 = (Expression)this.VisitExpression(context.expression()[0]); + Expression exp1 = (Expression)this.VisitExpression(context.expression()[1]); + + bool result = false; + if ( conditional_operator_context.GT() != null ) { + result = exp0 > exp1; + + } + if ( conditional_operator_context.GE() != null ) { + result = exp0 >= exp1; + } + if ( conditional_operator_context.LT() != null ) { + result = exp0 < exp1; + } + if ( conditional_operator_context.LE() != null ) { + result = exp0 <= exp1; + } + if ( conditional_operator_context.EQ() != null ) { + result = exp0 == exp1; + } + if ( conditional_operator_context.NE() != null ) { + result = exp0 != exp1; + } + if ( conditional_operator_context.ISKEYWORD() != null ) { + if ( conditional_operator_context.NOTKEYWORD() != null ) { + result = exp0 != exp1; + } else + { + result = exp0 == exp1; + } + } + + return new Expression(result); + } + + public override object VisitExpression([NotNull] ArcscriptParser.ExpressionContext context) { + if ( context.STRING() != null ) { + string result = context.STRING().GetText(); + result = result.Substring(1, result.Length - 2); + return new Expression(result); + } + if ( context.BOOLEAN() != null ) { + return new Expression(context.BOOLEAN().GetText() == "true"); + } + return this.VisitAdditive_numeric_expression(context.additive_numeric_expression()); + } + + public override object VisitAdditive_numeric_expression([NotNull] ArcscriptParser.Additive_numeric_expressionContext context) { + if ( context.additive_numeric_expression() != null ) { + Expression result = (Expression)this.VisitAdditive_numeric_expression(context.additive_numeric_expression()); + Expression mult_num_expression = (Expression)this.VisitMultiplicative_numeric_expression(context.multiplicative_numeric_expression()); + if ( context.ADD() != null ) { + return result + mult_num_expression; + } + // Else MINUS + return result - mult_num_expression; + } + + return (Expression)this.VisitMultiplicative_numeric_expression(context.multiplicative_numeric_expression()); + } + + public override object VisitMultiplicative_numeric_expression([NotNull] ArcscriptParser.Multiplicative_numeric_expressionContext context) { + if ( context.multiplicative_numeric_expression() != null ) { + Expression result = (Expression)this.VisitMultiplicative_numeric_expression(context.multiplicative_numeric_expression()); + Expression signed_unary_num_expr = (Expression)this.VisitSigned_unary_numeric_expression(context.signed_unary_numeric_expression()); + if ( context.MUL() != null ) { + return result * signed_unary_num_expr; + } + // Else DIV + return result / signed_unary_num_expr; + } + + return (Expression)this.VisitSigned_unary_numeric_expression(context.signed_unary_numeric_expression()); + } + + public override object VisitSigned_unary_numeric_expression([NotNull] ArcscriptParser.Signed_unary_numeric_expressionContext context) { + Expression unary_num_expr = (Expression)this.VisitUnary_numeric_expression(context.unary_numeric_expression()); + ArcscriptParser.SignContext sign = context.sign(); + + if ( sign != null ) { + if ( sign.ADD() != null ) { + return unary_num_expr; + } + // Else MINUS + return -unary_num_expr; + } + return unary_num_expr; + } + + public override object VisitUnary_numeric_expression([NotNull] ArcscriptParser.Unary_numeric_expressionContext context) { + if ( context.FLOAT() != null ) { + return new Expression(double.Parse(context.FLOAT().GetText(), CultureInfo.InvariantCulture)); + } + if ( context.INTEGER() != null ) { + return new Expression(int.Parse(context.INTEGER().GetText())); + } + + if (context.STRING() != null) + { + string result = context.STRING().GetText(); + result = result.Substring(1, result.Length - 2); + return new Expression(result); + } + + if (context.BOOLEAN() != null) + { + return new Expression(context.BOOLEAN().GetText() == "true"); + } + if ( context.VARIABLE() != null ) { + string variableName = context.VARIABLE().GetText(); + return new Expression(this.state.GetVarValue(variableName)); + } + + if ( context.function_call() != null ) + { + object functionResult = this.VisitFunction_call(context.function_call()); + if (functionResult.GetType() == typeof(Expression)) + { + return functionResult; + } + return new Expression(functionResult); + } + return this.VisitCompound_condition_or(context.compound_condition_or()); + } + public override object VisitVoid_function_call([NotNull] ArcscriptParser.Void_function_callContext context) + { + string fname = ""; + IList argument_list_result = null; + if (context.VFNAME() != null) + { + fname = context.VFNAME().GetText(); + if (context.argument_list() != null) + { + argument_list_result = (IList)this.VisitArgument_list(context.argument_list()); + } + } + if (context.VFNAMEVARS() != null) + { + fname = context.VFNAMEVARS().GetText(); + if (context.variable_list() != null) + { + argument_list_result = (IList)this.VisitVariable_list(context.variable_list()); + } + } + + object returnValue = this._functions.functions[fname](argument_list_result); + + return returnValue; + } + + public override object VisitFunction_call([NotNull] ArcscriptParser.Function_callContext context) { + IList argument_list_result = null; + if ( context.argument_list() != null ) { + argument_list_result = (IList)this.VisitArgument_list(context.argument_list()); + } + + string fname = context.FNAME().GetText(); + + Type resultType = this._functions.returnTypes[fname]; + object returnValue = this._functions.functions[fname](argument_list_result); + + return returnValue; + } + + public override object VisitVariable_list([NotNull] ArcscriptParser.Variable_listContext context) + { + List variables = new List(); + foreach (ITerminalNode variable in context.VARIABLE()) + { + IVariable varObject = this.state.GetVariable(variable.GetText()); + variables.Add(varObject); + } + return variables; + } + + public override object VisitArgument_list([NotNull] ArcscriptParser.Argument_listContext context) { + List argumentList = new List(); + foreach ( ArcscriptParser.ArgumentContext argument in context.argument() ) { + argumentList.Add(this.VisitArgument(argument)); + } + return argumentList; + } + + public override object VisitArgument([NotNull] ArcscriptParser.ArgumentContext context) { + if ( context.STRING() != null ) { + string result = context.STRING().GetText(); + result = result.Substring(1, result.Length - 2); + return new Expression(result); + } + if ( context.mention() != null ) { + Mention mention_result = (Mention)this.VisitMention(context.mention()); + return mention_result; + } + return this.VisitAdditive_numeric_expression(context.additive_numeric_expression()); + } + + public override object VisitMention([NotNull] ArcscriptParser.MentionContext context) { + Dictionary attrs = new Dictionary(); + + foreach ( ArcscriptParser.Mention_attributesContext attr in context.mention_attributes() ) { + Dictionary res = (Dictionary)this.VisitMention_attributes(attr); + attrs.Add((string)res["name"], (string)res["value"]); + } + string label = ""; + if ( context.MENTION_LABEL() != null ) { + label = context.MENTION_LABEL().GetText(); + } + return new Mention(label, attrs); + } + + public override object VisitMention_attributes([NotNull] ArcscriptParser.Mention_attributesContext context) { + string name = context.ATTR_NAME().GetText(); + ITerminalNode ctxvalue = context.ATTR_VALUE(); + object value = true; + if ( ctxvalue != null ) { + string strvalue = ctxvalue.GetText(); + if ( ( strvalue.StartsWith('"') && strvalue.EndsWith('"') ) || + ( strvalue.StartsWith("'") && strvalue.EndsWith("'") ) ) { + strvalue = strvalue.Substring(1, strvalue.Length - 2); + } + value = strvalue; + } + return new Dictionary { { "name", name }, { "value", value } }; + } + + public class Argument + { + public Type type { get; private set; } + public object value { get; private set; } + public Argument(Type type, object value) { + this.type = type; + this.value = value; + } + } + + public class Mention + { + public string label { get; private set; } + public Dictionary attrs { get; private set; } + + public Mention(string label, Dictionary attrs) { + this.label = label; + this.attrs = attrs; + } + } + + public struct ConditionalSection + { + public bool Clause; + public object Script; + + public ConditionalSection(bool clause, object script) { Clause = clause; Script = script; } + } + } +} \ No newline at end of file diff --git a/CSharp/Interpreter/ErrorListener.cs b/CSharp/Interpreter/ErrorListener.cs new file mode 100644 index 0000000..397d2c1 --- /dev/null +++ b/CSharp/Interpreter/ErrorListener.cs @@ -0,0 +1,17 @@ +using Antlr4.Runtime; + +namespace Arcweave.Interpreter; + +public class ErrorListener: ConsoleErrorListener +{ + public bool HasErrors = false; + public List Errors = new List(); + + public override void SyntaxError(TextWriter output, IRecognizer recognizer, S offendingSymbol, int line, int charPositionInLine, + string msg, RecognitionException e) + { + HasErrors = true; + Errors.Add($"line {line}:{charPositionInLine} {msg}"); + base.SyntaxError(output, recognizer, offendingSymbol, line, charPositionInLine, msg, e); + } +} \ No newline at end of file diff --git a/CSharp/Interpreter/INodes/IAttribute.cs b/CSharp/Interpreter/INodes/IAttribute.cs new file mode 100644 index 0000000..2023d33 --- /dev/null +++ b/CSharp/Interpreter/INodes/IAttribute.cs @@ -0,0 +1,29 @@ +namespace Arcweave.Interpreter.INodes +{ + public interface IAttribute + { + public enum DataType + { + Undefined, + StringPlainText, + StringRichText, + ComponentList, + } + + public enum ContainerType + { + Undefined, + Component, + Element, + } + + public string Name { get; } + + public DataType Type { get; } + + public ContainerType containerType { get; } + + public string containerId { get; } + + } +} diff --git a/CSharp/Interpreter/INodes/IBoard.cs b/CSharp/Interpreter/INodes/IBoard.cs new file mode 100644 index 0000000..94faf57 --- /dev/null +++ b/CSharp/Interpreter/INodes/IBoard.cs @@ -0,0 +1,6 @@ +namespace Arcweave.Interpreter.INodes +{ + public interface IBoard + { + } +} diff --git a/CSharp/Interpreter/INodes/IComponent.cs b/CSharp/Interpreter/INodes/IComponent.cs new file mode 100644 index 0000000..8e4efe8 --- /dev/null +++ b/CSharp/Interpreter/INodes/IComponent.cs @@ -0,0 +1,6 @@ +namespace Arcweave.Interpreter.INodes +{ + public interface IComponent : IHasAttributes + { + } +} diff --git a/CSharp/Interpreter/INodes/IConnection.cs b/CSharp/Interpreter/INodes/IConnection.cs new file mode 100644 index 0000000..68d7517 --- /dev/null +++ b/CSharp/Interpreter/INodes/IConnection.cs @@ -0,0 +1,20 @@ +namespace Arcweave.Interpreter.INodes +{ + public interface IConnection + { + public string Id { get; } + public string RawLabel { get; } + public string RuntimeLabel { get; } + + public INode Source { get; } + public INode Target { get; } + + public Arcweave.Project.Project Project { get; } + + public void Set(string label, INode source, INode target); + + public void RunLabelScript(); + + public Arcweave.Project.Path ResolvePath(Arcweave.Project.Path p); + } +} diff --git a/CSharp/Interpreter/INodes/IElement.cs b/CSharp/Interpreter/INodes/IElement.cs new file mode 100644 index 0000000..2e45d8a --- /dev/null +++ b/CSharp/Interpreter/INodes/IElement.cs @@ -0,0 +1,24 @@ +#if GODOT +using Godot.Collections; +#else +using System.Collections.Generic; +#endif +namespace Arcweave.Interpreter.INodes +{ + public interface IElement : INode, IHasAttributes + { + public int Visits { get; set; } + + public string Title { get; } + public string RawContent { get; } + +#if GODOT + public Array Outputs { get; } +#else + public List Outputs { get; } +#endif + public void RunContentScript(); + + public Arcweave.Project.Options GetOptions(); + } +} diff --git a/CSharp/Interpreter/INodes/IHasAttributes.cs b/CSharp/Interpreter/INodes/IHasAttributes.cs new file mode 100644 index 0000000..2a06a67 --- /dev/null +++ b/CSharp/Interpreter/INodes/IHasAttributes.cs @@ -0,0 +1,19 @@ +#if GODOT +using Godot.Collections; +#else +using System.Collections.Generic; +#endif + +namespace Arcweave.Interpreter.INodes +{ + public interface IHasAttributes + { +#if GODOT + Array Attributes { get; } +#else + List Attributes { get; } +#endif + + public void AddAttribute(Arcweave.Project.Attribute attribute); + } +} diff --git a/CSharp/Interpreter/INodes/IHelperClasses.cs b/CSharp/Interpreter/INodes/IHelperClasses.cs new file mode 100644 index 0000000..94ea149 --- /dev/null +++ b/CSharp/Interpreter/INodes/IHelperClasses.cs @@ -0,0 +1,37 @@ +#if GODOT +using Godot.Collections; +#else +using System.Collections.Generic; +#endif +namespace Arcweave.Interpreter.INodes +{ + public interface IOptions + { + public Arcweave.Project.Element Element { get; set; } +#if GODOT + public Array Paths { get; set; } +#else + public List Paths { get; set; } +#endif + public bool HasPaths => Paths != null; + public bool HasOptions => HasPaths && ( Paths.Count > 1 || !string.IsNullOrEmpty(Paths[0].label) ); + } + + public interface IPath + { + public string label { get; set; } + public Arcweave.Project.Element TargetElement { get; set; } +#if GODOT + public Array _connections { get; set; } +#else + public List _connections { get; set; } +#endif + internal bool IsValid => TargetElement != null; + + internal static IPath Invalid => default(IPath); + + public void AppendConnection(Arcweave.Project.Connection connection); + + public void ExecuteAppendedConnectionLabels(); + } +} diff --git a/CSharp/Interpreter/INodes/INode.cs b/CSharp/Interpreter/INodes/INode.cs new file mode 100644 index 0000000..d877dbd --- /dev/null +++ b/CSharp/Interpreter/INodes/INode.cs @@ -0,0 +1,10 @@ +namespace Arcweave.Interpreter.INodes +{ + public partial interface INode + { + string Id { get; } + Arcweave.Project.Project Project { get; } + + Arcweave.Project.Path ResolvePath(Arcweave.Project.Path path); + } +} diff --git a/CSharp/Interpreter/INodes/IProject.cs b/CSharp/Interpreter/INodes/IProject.cs new file mode 100644 index 0000000..b732008 --- /dev/null +++ b/CSharp/Interpreter/INodes/IProject.cs @@ -0,0 +1,20 @@ +#if GODOT +using Godot.Collections; +#else +using System.Collections.Generic; +#endif + +namespace Arcweave.Interpreter.INodes +{ + public interface IProject + { +#if GODOT + public Array Variables { get; } +#else + public List Variables { get; } +#endif + public Arcweave.Project.Element ElementWithId(string id); + + public Arcweave.Project.Variable GetVariable(string name); + } +} diff --git a/CSharp/Interpreter/INodes/IVariable.cs b/CSharp/Interpreter/INodes/IVariable.cs new file mode 100644 index 0000000..b6ec4d6 --- /dev/null +++ b/CSharp/Interpreter/INodes/IVariable.cs @@ -0,0 +1,23 @@ +#if GODOT +using Godot; +#endif + +namespace Arcweave.Interpreter.INodes +{ + public interface IVariable + { + public string Name { get; set; } +#if GODOT + Variant Value { get; } + Variant DefaultValue { get; } +#else + object Value { get; } + object DefaultValue { get; } +#endif + public object ObjectValue { get; } + + System.Type Type { get; } + + public void ResetToDefaultValue(); + } +} diff --git a/CSharp/Interpreter/Nodes.cs b/CSharp/Interpreter/Nodes.cs new file mode 100644 index 0000000..a7a99a0 --- /dev/null +++ b/CSharp/Interpreter/Nodes.cs @@ -0,0 +1,31 @@ +#if GODOT +using Godot; +#endif + +using Arcweave.Interpreter.INodes; + +namespace Arcweave.Project +{ +#if GODOT + public partial class Attribute : GodotObject, IAttribute { } + public partial class Board : GodotObject, IBoard { } + public partial class Component : GodotObject, IComponent { } + public partial class Connection : GodotObject, IConnection { } + public partial class Element : GodotObject, IElement { } + public partial class Project : GodotObject, IProject { } + public partial class Variable : GodotObject, IVariable { } + public partial class Options : GodotObject, IOptions { } + public partial class Path : GodotObject, IPath { } +#else + public partial class Attribute : IAttribute { } + public partial class Board : IBoard { } + public partial class Component : IComponent { } + public partial class Connection : IConnection { } + public partial class Element : IElement { } + public partial class Project : IProject { } + public partial class Variable : IVariable { } + public partial class Options : IOptions { } + public partial class Path : IPath { } +#endif +} + diff --git a/CSharp/Project/Attribute.cs b/CSharp/Project/Attribute.cs new file mode 100644 index 0000000..0857d12 --- /dev/null +++ b/CSharp/Project/Attribute.cs @@ -0,0 +1,11 @@ +using Arcweave.Interpreter.INodes; + +namespace Arcweave.Project; + +public partial class Attribute +{ + public string Name { get; } + public IAttribute.DataType Type { get; } + public IAttribute.ContainerType containerType { get; } + public string containerId { get; } +} \ No newline at end of file diff --git a/CSharp/Project/Component.cs b/CSharp/Project/Component.cs new file mode 100644 index 0000000..35d4a43 --- /dev/null +++ b/CSharp/Project/Component.cs @@ -0,0 +1,10 @@ +namespace Arcweave.Project; + +public partial class Component +{ + public List Attributes { get; } + public void AddAttribute(Attribute attribute) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/CSharp/Project/Connection.cs b/CSharp/Project/Connection.cs new file mode 100644 index 0000000..24d9127 --- /dev/null +++ b/CSharp/Project/Connection.cs @@ -0,0 +1,27 @@ +using Arcweave.Interpreter.INodes; + +namespace Arcweave.Project; + +public partial class Connection +{ + public string Id { get; } + public string RawLabel { get; } + public string RuntimeLabel { get; } + public INode Source { get; } + public INode Target { get; } + public Project Project { get; } + public void Set(string label, INode source, INode target) + { + throw new NotImplementedException(); + } + + public void RunLabelScript() + { + throw new NotImplementedException(); + } + + public Path ResolvePath(Path p) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/CSharp/Project/Element.cs b/CSharp/Project/Element.cs new file mode 100644 index 0000000..e25204b --- /dev/null +++ b/CSharp/Project/Element.cs @@ -0,0 +1,32 @@ +namespace Arcweave.Project; + +public partial class Element +{ + public string Id { get; } + public Project Project { get; } + + public Path ResolvePath(Path path) + { + throw new NotImplementedException(); + } + + public List Attributes { get; } + public void AddAttribute(Attribute attribute) + { + throw new NotImplementedException(); + } + + public int Visits { get; set; } + public string Title { get; } + public string RawContent { get; } + public List Outputs { get; } + public void RunContentScript() + { + throw new NotImplementedException(); + } + + public Options GetOptions() + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/CSharp/Project/HelperClasses.cs b/CSharp/Project/HelperClasses.cs new file mode 100644 index 0000000..8356bc8 --- /dev/null +++ b/CSharp/Project/HelperClasses.cs @@ -0,0 +1,23 @@ +namespace Arcweave.Project; + +public partial class Options +{ + public Element Element { get; set; } + public List Paths { get; set; } +} + +public partial class Path +{ + public string label { get; set; } + public Element TargetElement { get; set; } + public List _connections { get; set; } + public void AppendConnection(Connection connection) + { + throw new NotImplementedException(); + } + + public void ExecuteAppendedConnectionLabels() + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/CSharp/Project/Project.cs b/CSharp/Project/Project.cs new file mode 100644 index 0000000..6a61f85 --- /dev/null +++ b/CSharp/Project/Project.cs @@ -0,0 +1,26 @@ +namespace Arcweave.Project; + +public partial class Project +{ + public List Variables { get; } + public Dictionary Elements; + public Project(List variables, Dictionary elements) + { + Variables = variables; + Elements = elements; + } + public Element ElementWithId(string id) + { + if (Elements.ContainsKey(id)) + { + return Elements[id]; + } + + return null; + } + + public Variable GetVariable(string name) + { + return Variables.FirstOrDefault(variable => variable.Name == name); + } +} \ No newline at end of file diff --git a/CSharp/Project/Variable.cs b/CSharp/Project/Variable.cs new file mode 100644 index 0000000..40455b4 --- /dev/null +++ b/CSharp/Project/Variable.cs @@ -0,0 +1,22 @@ +namespace Arcweave.Project; + +public partial class Variable +{ + public string Name { get; set; } + public object Value { get; set; } + public object ObjectValue => Value; + public Type Type { get; } + private object _defaultValue; + public object DefaultValue => _defaultValue; + public Variable(string name, object value) + { + Name = name; + Value = value; + _defaultValue = value; + Type = value.GetType(); + } + public void ResetToDefaultValue() + { + Value = _defaultValue; + } +} \ No newline at end of file diff --git a/CSharp/TestObjects.cs b/CSharp/TestObjects.cs new file mode 100644 index 0000000..17736c3 --- /dev/null +++ b/CSharp/TestObjects.cs @@ -0,0 +1,26 @@ +namespace Arcweave; + +public struct TestVariable +{ + public string id; + public string name; + public string type; + public object value; +} + +public struct TestCase +{ + public string code; + public Dictionary changes; + public string? output; + public string elementId; + public object result; + public string? error; + public Dictionary? visits; +} + +public struct TestFile +{ + public Dictionary initialVars; + public List cases; +} \ No newline at end of file diff --git a/CSharp/UnitTests.cs b/CSharp/UnitTests.cs new file mode 100644 index 0000000..83dbb53 --- /dev/null +++ b/CSharp/UnitTests.cs @@ -0,0 +1,244 @@ +using NUnit.Framework; +using Newtonsoft.Json; +using Arcweave.Interpreter; +using Arcweave.Project; + +namespace Arcweave; + +public class Tests +{ + struct TestData + { + public readonly Dictionary Variables; + public readonly List Cases; + + public TestData(Dictionary variables, List cases) + { + Variables = variables; + Cases = cases; + } + } + + private static IEnumerable GetValidTestData() + { + var tests = LoadJson("../../../__tests__/valid.json"); + + foreach (var testCase in tests.Cases) + { + yield return new TestCaseData(tests.Variables, testCase); + } + } + + private static IEnumerable GetConditionsTestData() + { + var tests = LoadJson("../../../__tests__/conditions.json"); + + foreach (var validTestsCase in tests.Cases) + { + yield return new TestCaseData(tests.Variables, validTestsCase); + } + } + + private static IEnumerable GetStringConcatTestData() + { + var tests = LoadJson("../../../__tests__/stringConcat.json"); + + foreach (var stringConcatTestCase in tests.Cases) + { + yield return new TestCaseData(tests.Variables, stringConcatTestCase); + } + } + + private static IEnumerable GetRuntimeErrorTestData() + { + var tests = LoadJson("../../../__tests__/runtimeErrors.json"); + + foreach (var runtimeErrorTestCase in tests.Cases) + { + yield return new TestCaseData(tests.Variables, runtimeErrorTestCase); + } + } + + private static IEnumerable GetParseErrorTestData() + { + var tests = LoadJson("../../../__tests__/parseErrors.json"); + + foreach (var parseErrorTestCase in tests.Cases) + { + yield return new TestCaseData(tests.Variables, parseErrorTestCase); + } + } + + private static TestData LoadJson(string filePath) + { + using var r = new StreamReader(filePath); + var json = r.ReadToEnd(); + var testFile = JsonConvert.DeserializeObject(json); + var variables = new Dictionary(); + + foreach (var variable in testFile.initialVars.Values) + { + if (variable.value is long l) + { + variables[variable.id] = new Variable(variable.name, (int)l); + } + else + { + variables[variable.id] = new Variable(variable.name, variable.value); + } + } + + return new TestData(variables, testFile.cases); + } + + private static Dictionary? ChangesByName(Dictionary variables, + Dictionary? changes) + { + if (changes == null || changes.Count == 0) + { + return null; + } + + var changesByName = new Dictionary(); + foreach (var varChange in changes) + { + changesByName[variables[varChange.Key].Name] = varChange.Value; + } + + return changesByName; + } + + [Test] + [TestCaseSource(nameof(GetValidTestData))] + public void ValidTests(Dictionary variables, TestCase testCase) + { + var elements = new Dictionary(); + if (testCase.visits != null) + { + foreach (var kvp in testCase.visits) + { + elements[kvp.Key] = new Element + { + Visits = kvp.Value + }; + } + } + + Console.WriteLine("Testing Case: " + testCase.code); + var project = new Project.Project(variables.Values.ToList(), elements); + var i = new AwInterpreter(project, testCase.elementId); + var output = i.RunScript(testCase.code); + + if (testCase.output != null) + { + Assert.That(output.Output, Is.EqualTo(testCase.output)); + } + + var changesByName = ChangesByName(variables, testCase.changes); + if (changesByName != null) + { + Assert.That(output.Changes, Is.EqualTo(changesByName)); + } + } + + [Test] + [TestCaseSource(nameof(GetConditionsTestData))] + public void ConditionTests(Dictionary variables, TestCase testCase) + { + var elements = new Dictionary(); + if (testCase.visits != null) + { + foreach (var kvp in testCase.visits) + { + elements[kvp.Key] = new Element + { + Visits = kvp.Value + }; + } + } + + Console.WriteLine("Testing Case: " + testCase.code); + var project = new Project.Project(variables.Values.ToList(), elements); + var i = new AwInterpreter(project, testCase.elementId); + var output = i.RunScript(testCase.code); + + Assert.That(output.Result, Is.EqualTo(testCase.result)); + } + + [Test] + [TestCaseSource(nameof(GetStringConcatTestData))] + public void StringConcatTests(Dictionary variables, TestCase testCase) + { + var elements = new Dictionary(); + if (testCase.visits != null) + { + foreach (var kvp in testCase.visits) + { + elements[kvp.Key] = new Element + { + Visits = kvp.Value + }; + } + } + + Console.WriteLine("Testing Case: " + testCase.code); + var project = new Project.Project(variables.Values.ToList(), elements); + var i = new AwInterpreter(project, testCase.elementId); + var output = i.RunScript(testCase.code); + + if (testCase.output != null) + { + Assert.That(output.Output, Is.EqualTo(testCase.output)); + } + + var changesByName = ChangesByName(variables, testCase.changes); + if (changesByName != null) + { + Assert.That(output.Changes, Is.EqualTo(changesByName)); + } + } + + [Test] + [TestCaseSource(nameof(GetRuntimeErrorTestData))] + public void RuntimeErrorTests(Dictionary variables, TestCase testCase) + { + var Elements = new Dictionary(); + if (testCase.visits != null) + { + foreach (var kvp in testCase.visits) + { + Elements[kvp.Key] = new Element + { + Visits = kvp.Value + }; + } + } + + Console.WriteLine("Testing Case: " + testCase.code); + var project = new Project.Project(variables.Values.ToList(), Elements); + var i = new AwInterpreter(project, testCase.elementId); + var ex = Assert.Throws(() => i.RunScript(testCase.code)); + } + + [Test] + [TestCaseSource(nameof(GetParseErrorTestData))] + public void ParseErrorTests(Dictionary variables, TestCase testCase) + { + var Elements = new Dictionary(); + if (testCase.visits != null) + { + foreach (var kvp in testCase.visits) + { + Elements[kvp.Key] = new Element + { + Visits = kvp.Value + }; + } + } + + Console.WriteLine("Testing Case: " + testCase.code); + var project = new Project.Project(variables.Values.ToList(), Elements); + var i = new AwInterpreter(project, testCase.elementId); + var ex = Assert.Throws(() => i.RunScript(testCase.code)); + } +} \ No newline at end of file diff --git a/CSharp/__tests__/conditions.json b/CSharp/__tests__/conditions.json new file mode 100644 index 0000000..88331f1 --- /dev/null +++ b/CSharp/__tests__/conditions.json @@ -0,0 +1,100 @@ +{ + "initialVars": { + "var1": { + "id": "var1", + "name": "x", + "type": "integer", + "value": 14 + }, + "var2": { + "id": "var2", + "name": "y", + "type": "integer", + "value": 15 + }, + "var3": { + "id": "var3", + "name": "z", + "type": "integer", + "value": 0 + }, + "var4": { + "id": "var4", + "name": "w", + "type": "string", + "value": "Dummy text" + }, + "var5": { + "id": "var5", + "name": "$c5", + "type": "integer", + "value": 0 + }, + "var6": { + "id": "var6", + "name": "_a", + "type": "boolean", + "value": false + }, + "var7": { + "id": "var7", + "name": "xy", + "type": "integer", + "value": -1 + } + }, + "cases": [ + { + "code": "
x >= 14
", + "result": true + }, + { + "code": "
x == 14
", + "result": true + }, + { + "code": "
x is 14
", + "result": true + }, + { + "code": "
x == 45
", + "result": false + }, + { + "code": "
x is 45
", + "result": false + }, + { + "code": "
x == 14 && y == 15
", + "result": true + }, + { + "code": "
x == 14 and y == 15
", + "result": true + }, + { + "code": "
x == 3 || y != 0
", + "result": true + }, + { + "code": "
x == 3 or y != 0
", + "result": true + }, + { + "code": "
w == \"Dummy text\"
", + "result": true + }, + { + "code": "
w != \"Dummy text\"
", + "result": false + }, + { + "code": "
w is not \"Dummy text\"
", + "result": false + }, + { + "code": "
x is not \"Dummy text\"
", + "result": true + } + ] +} \ No newline at end of file diff --git a/CSharp/__tests__/parseErrors.json b/CSharp/__tests__/parseErrors.json new file mode 100644 index 0000000..07d79d9 --- /dev/null +++ b/CSharp/__tests__/parseErrors.json @@ -0,0 +1,96 @@ +{ + "initialVars": { + "var1": { + "id": "var1", + "name": "x", + "type": "integer", + "value": 14 + }, + "var2": { + "id": "var2", + "name": "y", + "type": "integer", + "value": 15 + }, + "var3": { + "id": "var3", + "name": "z", + "type": "integer", + "value": 0 + }, + "var4": { + "id": "var4", + "name": "w", + "type": "string", + "value": "Dummy text" + }, + "var5": { + "id": "var5", + "name": "$c5", + "type": "integer", + "value": 0 + }, + "var6": { + "id": "var6", + "name": "_a", + "type": "boolean", + "value": false + }, + "var7": { + "id": "var7", + "name": "xy", + "type": "integer", + "value": -1 + } + }, + "cases": [ + { + "code": "
p = 1
", + "error": "parse" + }, + { + "code": "
x = p
", + "error": "parse" + }, + { + "code": "
show(x, y, z, abc)
", + "error": "parse" + }, + { + "code": "
testFunction(x, y)
", + "error": "parse" + }, + { + "code": "
x = 1;
", + "error": "parse" + }, + { + "code": "
We want to be friends
", + "error": "parse" + }, + { + "code": "
x = sqr()
", + "error": "parse" + }, + { + "code": "
x = random(2)
", + "error": "parse" + }, + { + "code": "
y = 33
reset(13)
", + "error": "parse" + }, + { + "code": "
ελληνικά
", + "error": "parse" + }, + { + "code": "
visits(Untitled Comp) == 9
", + "error": "parse" + }, + { + "code": "

Test

if true

inside

endif

this

", + "error": "parse" + } + ] +} \ No newline at end of file diff --git a/CSharp/__tests__/replaceVariables.json b/CSharp/__tests__/replaceVariables.json new file mode 100644 index 0000000..4fc89b0 --- /dev/null +++ b/CSharp/__tests__/replaceVariables.json @@ -0,0 +1,69 @@ +{ + "initialVars": { + "var1": { + "id": "var1", + "name": "x", + "type": "integer", + "value": 14 + }, + "var2": { + "id": "var2", + "name": "y", + "type": "integer", + "value": 15 + }, + "var3": { + "id": "var3", + "name": "z", + "type": "integer", + "value": 0 + }, + "var4": { + "id": "var4", + "name": "w", + "type": "string", + "value": "Dummy text" + }, + "var5": { + "id": "var5", + "name": "$c5", + "type": "integer", + "value": 0 + }, + "var6": { + "id": "var6", + "name": "_a", + "type": "boolean", + "value": false + }, + "var7": { + "id": "var7", + "name": "xy", + "type": "integer", + "value": -1 + } + }, + "cases": [ + { + "code": "
x = x + 2 * sqrt(x)
y = 5 + x
", + "variableChanges": { + "var1": "zed" + }, + "result": "
zed = zed + 2 * sqrt(zed)
y = 5 + zed
" + }, + { + "code": "
x = \"x marks the spot\"
xy = 5 + x
", + "variableChanges": { + "var1": "zed" + }, + "result": "
zed = \"x marks the spot\"
xy = 5 + zed
" + }, + { + "code": "
b = \"x marks the spot\"
xy = 5 + x
", + "variableChanges": { + "var1": "zed" + }, + "result": "
b = \"x marks the spot\"
xy = 5 + zed
" + } + ] +} \ No newline at end of file diff --git a/CSharp/__tests__/runtimeErrors.json b/CSharp/__tests__/runtimeErrors.json new file mode 100644 index 0000000..2f4924a --- /dev/null +++ b/CSharp/__tests__/runtimeErrors.json @@ -0,0 +1,72 @@ +{ + "initialVars": { + "var1": { + "id": "var1", + "name": "x", + "type": "integer", + "value": 14 + }, + "var2": { + "id": "var2", + "name": "y", + "type": "integer", + "value": 15 + }, + "var3": { + "id": "var3", + "name": "z", + "type": "integer", + "value": 0 + }, + "var4": { + "id": "var4", + "name": "w", + "type": "string", + "value": "Dummy text" + }, + "var5": { + "id": "var5", + "name": "$c5", + "type": "integer", + "value": 0 + }, + "var6": { + "id": "var6", + "name": "_a", + "type": "boolean", + "value": false + }, + "var7": { + "id": "var7", + "name": "xy", + "type": "integer", + "value": -1 + } + }, + "cases": [ + { + "code": "
x = sqrt(-3)
", + "error": "runtime" + }, + { + "code": "
x = 3 / 0
", + "error": "runtime" + }, + { + "code": "
x = roll(\"2\")
", + "error": "runtime" + }, + { + "code": "
w = \"2\" - \"test\"
", + "error": "runtime" + }, + { + "code": "
w = w - \"test\"
", + "error": "runtime" + }, + { + "code": "
w -= _a
", + "error": "runtime" + } + ] +} \ No newline at end of file diff --git a/CSharp/__tests__/stringConcat.json b/CSharp/__tests__/stringConcat.json new file mode 100644 index 0000000..d9b3503 --- /dev/null +++ b/CSharp/__tests__/stringConcat.json @@ -0,0 +1,132 @@ +{ + "initialVars": { + "var1": { + "id": "var1", + "name": "x", + "type": "integer", + "value": 14 + }, + "var2": { + "id": "var2", + "name": "y", + "type": "integer", + "value": 15 + }, + "var3": { + "id": "var3", + "name": "z", + "type": "integer", + "value": 0 + }, + "var4": { + "id": "var4", + "name": "w", + "type": "string", + "value": "Dummy text" + }, + "var5": { + "id": "var5", + "name": "$c5", + "type": "integer", + "value": 0 + }, + "var6": { + "id": "var6", + "name": "_a", + "type": "boolean", + "value": false + }, + "var7": { + "id": "var7", + "name": "xy", + "type": "integer", + "value": -1 + }, + "var8": { + "id": "var8", + "name": "man", + "type": "string", + "value": "Different Text" + } + }, + "cases": [ + { + "code": "
w = \"test\"
w = w + \"ing\"
", + "changes": { + "var4": "testing" + } + }, + { + "code": "
w = \"test\" + \"ing\"
", + "changes": { + "var4": "testing" + } + }, + { + "code": "
w = 0.42 + \"ing\"
", + "changes": { + "var4": "0.42ing" + } + }, + { + "code": "
w = 0.42 + \"\"
", + "changes": { + "var4": "0.42" + } + }, + { + "code": "
w = \"test\" + 44
", + "changes": { + "var4": "test44" + } + }, + { + "code": "
w = \"test\" + false
", + "changes": { + "var4": "testfalse" + } + }, + { + "code": "
w = \"test\" + true + true
", + "changes": { + "var4": "testtruetrue" + } + }, + { + "code": "
w = true + true + \"test\"
", + "changes": { + "var4": "2test" + } + }, + { + "code": "
w += \" test\"
", + "changes": { + "var4": "Dummy text test" + } + }, + { + "code": "
x += \"test\"
", + "changes": { + "var1": "14test" + } + }, + { + "code": "
w += 42
", + "changes": { + "var4": "Dummy text42" + } + }, + { + "code": "
w += _a
", + "changes": { + "var4": "Dummy textfalse" + } + }, + { + "code": "
w += man
", + "changes": { + "var4": "Dummy textDifferent Text" + } + } + ] +} \ No newline at end of file diff --git a/CSharp/__tests__/valid.json b/CSharp/__tests__/valid.json new file mode 100644 index 0000000..6df3bb8 --- /dev/null +++ b/CSharp/__tests__/valid.json @@ -0,0 +1,478 @@ +{ + "initialVars": { + "var1": { + "id": "var1", + "name": "x", + "type": "integer", + "value": 14 + }, + "var2": { + "id": "var2", + "name": "y", + "type": "integer", + "value": 15 + }, + "var3": { + "id": "var3", + "name": "z", + "type": "integer", + "value": 0 + }, + "var4": { + "id": "var4", + "name": "w", + "type": "string", + "value": "Dummy text" + }, + "var5": { + "id": "var5", + "name": "$c5", + "type": "integer", + "value": 0 + }, + "var6": { + "id": "var6", + "name": "_a", + "type": "boolean", + "value": false + }, + "var7": { + "id": "var7", + "name": "xy", + "type": "integer", + "value": -1 + }, + "var8": { + "id": "var8", + "name": "man", + "type": "string", + "value": "Different Text" + } + }, + "cases": [ + { + "code": "
x=5
", + "changes": { + "var1": 5 + } + }, + { + "code": "
x=-5
", + "changes": { + "var1": -5 + } + }, + { + "code": "
x=5.23
", + "changes": { + "var1": 5.23 + } + }, + { + "code": "
x=3
y=2
", + "changes": { + "var1": 3, + "var2": 2 + } + }, + { + "code": "
x=\"test\"
", + "changes": { + "var1": "test" + } + }, + { + "code": "
x=true
", + "changes": { + "var1": true + } + }, + { + "code": "
x = y
", + "changes": { + "var1": 15 + } + }, + { + "code": "
x = 5 + 3
", + "changes": { + "var1": 8 + } + }, + { + "code": "
x = 3 / 4 * 5
", + "changes": { + "var1": 3.75 + } + }, + { + "code": "
x = 3 + 4 * 5
", + "changes": { + "var1": 23 + } + }, + { + "code": "
x = 3 * 4 + 5
", + "changes": { + "var1": 17 + } + }, + { + "code": "
x = 3 / 4 / 5
", + "changes": { + "var1": 0.15 + } + }, + { + "code": "
x = sqr(2)
", + "changes": { + "var1": 4 + } + }, + { + "code": "
x = 3 > 2
", + "changes": { + "var1": true + } + }, + { + "code": "
x = 3 >= 2
", + "changes": { + "var1": true + } + }, + { + "code": "
x = 3 < 2
", + "changes": { + "var1": false + } + }, + { + "code": "
x = 3 <= 2
", + "changes": { + "var1": false + } + }, + { + "code": "
x = 3 == 2
", + "changes": { + "var1": false + } + }, + { + "code": "
x = 3 != 2
", + "changes": { + "var1": true + } + }, + { + "code": "
_a = !(3 == 2)
", + "changes": { + "var6": true + } + }, + { + "code": "
_a = not (3 == 2)
", + "changes": { + "var6": true + } + }, + { + "code": "
x = !0
", + "changes": { + "var1": true + } + }, + { + "code": "
x = true || false
", + "changes": { + "var1": true + } + }, + { + "code": "
x = true && false
", + "changes": { + "var1": false + } + }, + { + "code": "
x = -3
y = y + 3
z = sqr(x)
", + "changes": { + "var1": -3, + "var2": 18, + "var3": 9 + } + }, + { + "code": "
x = sqrt(y + 2)
", + "changes": { + "var1": 4.123105625617661 + } + }, + { + "code": "
x = z + 12 == 4 * 3
", + "changes": { + "var1": true + } + }, + { + "code": "
x = y == 15 && !(z == 1)
", + "changes": { + "var1": true + } + }, + { + "code": "
x = (x*x + 3)/y
", + "changes": { + "var1": 13.266666666666667 + } + }, + { + "code": "
x = x*(x + 1)/y
", + "changes": { + "var1": 14 + } + }, + { + "code": "
x += 2
", + "changes": { + "var1": 16 + } + }, + { + "code": "
x -= 2
", + "changes": { + "var1": 12 + } + }, + { + "code": "
x *= 2
", + "changes": { + "var1": 28 + } + }, + { + "code": "
x /= 2
", + "changes": { + "var1": 7 + } + }, + { + "code": "
x = abs(-6)
", + "changes": { + "var1": 6 + } + }, + { + "code": "
x = min(5, -y, 2.3)
", + "changes": { + "var1": -15 + } + }, + { + "code": "
x = max(5, -y, 2.3)
", + "changes": { + "var1": 5 + } + }, + { + "code": "
x = random()
", + "changes": {} + }, + { + "code": "
x = roll(6)
", + "changes": {} + }, + { + "code": "
x = roll(6, 5)
", + "changes": {} + }, + { + "code": "
x = roll(y)
", + "changes": {} + }, + { + "code": "
x = round(2.65)
", + "changes": { + "var1": 3 + } + }, + { + "code": "
show(\"x is \", x)
", + "changes": {}, + "output": "

x is 14

" + }, + { + "code": "
y = 33
reset(y)
", + "changes": { + "var2": 15 + } + }, + { + "code": "
x = 16
y = 33
resetAll(y)
", + "changes": { + "var1": 14, + "var2": 33, + "var3": 0, + "var4": "Dummy text", + "var5": 0, + "var6": false, + "var7": -1, + "var8": "Different Text" + } + }, + { + "code": "
$c5 = x
", + "changes": { + "var5": 14 + } + }, + { + "code": "
if (x==14) 
x=5
endif
", + "changes": { + "var1": 5 + } + }, + { + "code": "
if (x==11)
x=5
else
x=7
endif
", + "changes": { + "var1": 7 + } + }, + { + "code": "
if x==3
x=5
elseif x==14
x=2
else
x = 0
endif
", + "changes": { + "var1": 2 + } + }, + { + "code": "

Mein gott!

x = 1

What is that?

", + "changes": { + "var1": 1 + }, + "output": "

Mein gott! What is that?

" + }, + { + "code": "

Hello my dear

if x == 1

We might ride to the mountains.

endif
", + "changes": {}, + "output": "

Hello my dear

" + }, + { + "code": "

You carry

if x == 12

a small knife

elseif y == 12

a large sword

elseif z == 1

just a rotten tomato

else

nothing really

endif
", + "changes": {}, + "output": "

You carry nothing really

" + }, + { + "code": "

How are you?

x = 1

I am fine, thank you.

", + "changes": {}, + "output": "

How are you?

I am fine, thank you.

" + }, + { + "code": "
show(\"Hello friends:\", x)
", + "changes": {}, + "output": "

Hello friends:14

" + }, + { + "code": "
_a = true
if (((_a)))
x = 1
endif
", + "changes": { + "var1": 1, + "var6": true + } + }, + { + "code": "
visits() == 1
", + "visits": { + "a": 1, + "b": 1, + "c": 0 + }, + "elementId": "b", + "result": true + }, + { + "code": "
visits(Untitled Comp) == 9
", + "visits": { + "a": 1, + "b": 1, + "c": 0 + }, + "elementId": "b", + "result": false + }, + { + "code": "

this

text

must

not

be

in

one

paragraph

", + "output": "

this

text

must

not

be

in

one

paragraph

" + }, + { + "code": "

three

separate

lines

just

show(\"one\")
show(\"and\")
show(\"single\")

line

three

more

lines

", + "output": "

three

separate

lines

just one and single line

three

more

lines

" + }, + { + "code": "

This is a blockquote

if x

This is a separate line

endif

This is another blockquote

", + "output": "

This is a blockquote

This is a separate line

This is another blockquote

" + }, + { + "code": "

This is a blockquote

show(x)

test

", + "output": "

This is a blockquote 14

test

" + }, + { + "code": "

This is a blockquote

show(x)

test

", + "output": "

This is a blockquote 14 test

" + }, + { + "code": "

one

if true

two

endif

three

", + "output": "

one

two

three

" + }, + { + "code": "

one

if true

two

endif

three

", + "output": "

one two

three

" + }, + { + "code": "

one

if true

two

endif

three

", + "output": "

one two

three

" + }, + { + "code": "

one

if true
show(x)
endif

three

", + "output": "

one 14

three

" + }, + { + "code": "

one

if true

two

test

four

endif

three

", + "output": "

one two

test

four

three

" + }, + { + "code": "

one

if true

two

test

show(\"four\")
endif

three

", + "output": "

one two

test four

three

" + }, + { + "code": "

one

if false

two

endif

three

", + "output": "

one

three

" + }, + { + "code": "

This is a blockquote

if true

Second blockquote

Separate Line

endif

Outside blockquote

", + "output": "

This is a blockquote

Second blockquote

Separate Line

Outside blockquote

" + }, + { + "code": "

Test

_a = true

this

", + "output": "

Test this

", + "changes": { + "var6": true + } + }, + { + "code": "

Test

show(x)

this

", + "output": "

Test 14 this

" + }, + { + "code": "

this is a test

show(x)

continue

testing

", + "output": "

this is a test

14 continue

testing

" + }, + { + "code": "

This is a test

Manolis

inside blockquotes

if true

John

endif
", + "output": "

This is a test

Manolis

inside blockquotes

John

" + }, + { + "code": "
w = \"String with \\\"quotes\\\" \\t tabs and \\n new lines\"
show(w)
", + "output": "

String with \"quotes\" \t tabs and \n new lines

" + } + ] +} \ No newline at end of file diff --git a/generate.ps1 b/generate.ps1 new file mode 100644 index 0000000..593c942 --- /dev/null +++ b/generate.ps1 @@ -0,0 +1,33 @@ +# Download antlr4.jar if it doesn't exist +if (-Not (Test-Path "./antlr4.jar")) { + Invoke-WebRequest -Uri "https://www.antlr.org/download/antlr-4.13.1-complete.jar" -OutFile "antlr4.jar" +} + +Set-Location "grammar" + +# JavaScript generation +java -Xmx500M -cp ../antlr4.jar org.antlr.v4.Tool -Dlanguage=JavaScript ArcscriptLexer.g4 ArcscriptParser.g4 -visitor -no-listener -o ./JavaScript +New-Item -ItemType Directory -Force -Path ../JavaScript/src/Generated | Out-Null +Copy-Item JavaScript\*.js ../JavaScript/src/Generated\ +Remove-Item -Recurse -Force ./JavaScript + +# Copy grammar files for Cpp +Copy-Item Arcscript*.g4 ./Cpp/ +Set-Location "Cpp" +python transformGrammar.py +java -Xmx500M -cp ../../antlr4.jar org.antlr.v4.Tool -Dlanguage=Cpp ArcscriptLexer.g4 ArcscriptParser.g4 -visitor -no-listener -o ./Generated -package Arcweave +New-Item -ItemType Directory -Force -Path ../../Cpp/src/Generated/ArcscriptLexer | Out-Null +New-Item -ItemType Directory -Force -Path ../../Cpp/src/Generated/ArcscriptParser | Out-Null +Copy-Item Generated\*Lexer*.cpp ../../Cpp/src/Generated/ArcscriptLexer +Copy-Item Generated\*Lexer*.h ../../Cpp/src/Generated/ArcscriptLexer +Copy-Item Generated\*Parser*.cpp ../../Cpp/src/Generated/ArcscriptParser +Copy-Item Generated\*Parser*.h ../../Cpp/src/Generated/ArcscriptParser +Remove-Item -Recurse -Force ./Generated +Remove-Item *.g4,*.g4.bak -Force +Set-Location .. + +# CSharp generation +java -Xmx500M -cp ../antlr4.jar org.antlr.v4.Tool -Dlanguage=CSharp ArcscriptLexer.g4 ArcscriptParser.g4 -visitor -no-listener -o ./CSharp -package Arcweave.Interpreter +New-Item -ItemType Directory -Force -Path ../CSharp/src/Generated | Out-Null +Copy-Item CSharp\*.cs ../CSharp/src/Generated\ +Remove-Item -Recurse -Force ./CSharp \ No newline at end of file diff --git a/generate.sh b/generate.sh index 7a66e68..5c253ec 100644 --- a/generate.sh +++ b/generate.sh @@ -23,6 +23,6 @@ rm -rf *.g4 *.g4.bak cd .. java -Xmx500M -cp ../antlr4.jar org.antlr.v4.Tool -Dlanguage=CSharp ArcscriptLexer.g4 ArcscriptParser.g4 -visitor -no-listener -o ./CSharp -package Arcweave.Interpreter -mkdir -p ../CSharp/src/Generated -cp CSharp/*.cs ../CSharp/src/Generated/. +mkdir -p ../CSharp/Interpreter/Generated +cp CSharp/*.cs ../CSharp/Interpreter/Generated/. rm -rf ./CSharp \ No newline at end of file