├── .editorconfig ├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE.txt ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── icons ├── ichor_a_512.png ├── ichor_github_banner.png ├── ichor_r_512.png ├── ichor_s_128.png ├── ichor_s_512.png └── ichor_s_64.png ├── settings.gradle └── src ├── main └── java │ └── dev │ └── latvian │ └── apps │ └── ichor │ ├── Callable.java │ ├── CallableTypeAdapter.java │ ├── Context.java │ ├── ContextProperty.java │ ├── DebuggerCallback.java │ ├── Deletable.java │ ├── Evaluable.java │ ├── EvaluableType.java │ ├── Interpretable.java │ ├── Optimizable.java │ ├── Parser.java │ ├── RemappedEnum.java │ ├── RootScope.java │ ├── Scope.java │ ├── ScriptValue.java │ ├── Special.java │ ├── TypeAdapter.java │ ├── annotation │ ├── Hidden.java │ ├── Remap.java │ ├── RemapPrefix.java │ └── RemapPrefixRep.java │ ├── ast │ ├── AppendableAst.java │ ├── Ast.java │ ├── AstStringBuilder.java │ ├── expression │ │ ├── AstAwait.java │ │ ├── AstCall.java │ │ ├── AstClassFunction.java │ │ ├── AstClassPrototype.java │ │ ├── AstDelete.java │ │ ├── AstExpression.java │ │ ├── AstFunction.java │ │ ├── AstGetBase.java │ │ ├── AstGetByEvaluable.java │ │ ├── AstGetByIndex.java │ │ ├── AstGetByName.java │ │ ├── AstGetByNameOptional.java │ │ ├── AstGetFrom.java │ │ ├── AstGetScopeMember.java │ │ ├── AstList.java │ │ ├── AstMap.java │ │ ├── AstObjectPrototype.java │ │ ├── AstParam.java │ │ ├── AstSet.java │ │ ├── AstSpread.java │ │ ├── AstTempExpression.java │ │ ├── AstTemplateLiteral.java │ │ ├── AstTernary.java │ │ ├── AstType.java │ │ ├── AstTypeOf.java │ │ ├── binary │ │ │ ├── AstAdd.java │ │ │ ├── AstAnd.java │ │ │ ├── AstBinary.java │ │ │ ├── AstBinaryBoolean.java │ │ │ ├── AstBitwiseAnd.java │ │ │ ├── AstBitwiseOr.java │ │ │ ├── AstDiv.java │ │ │ ├── AstEq.java │ │ │ ├── AstGt.java │ │ │ ├── AstGte.java │ │ │ ├── AstIn.java │ │ │ ├── AstInstanceOf.java │ │ │ ├── AstLsh.java │ │ │ ├── AstLt.java │ │ │ ├── AstLte.java │ │ │ ├── AstMod.java │ │ │ ├── AstMul.java │ │ │ ├── AstNc.java │ │ │ ├── AstNeq.java │ │ │ ├── AstOr.java │ │ │ ├── AstPow.java │ │ │ ├── AstRsh.java │ │ │ ├── AstSeq.java │ │ │ ├── AstSneq.java │ │ │ ├── AstSub.java │ │ │ ├── AstUrsh.java │ │ │ └── AstXor.java │ │ └── unary │ │ │ ├── AstAdd1L.java │ │ │ ├── AstAdd1R.java │ │ │ ├── AstAdditive1.java │ │ │ ├── AstBitwiseNot.java │ │ │ ├── AstNegate.java │ │ │ ├── AstNot.java │ │ │ ├── AstPositive.java │ │ │ ├── AstSub1L.java │ │ │ ├── AstSub1R.java │ │ │ └── AstUnary.java │ └── statement │ │ ├── AstBlock.java │ │ ├── AstBreak.java │ │ ├── AstClass.java │ │ ├── AstContinue.java │ │ ├── AstDebugger.java │ │ ├── AstDeclareStatement.java │ │ ├── AstDoWhile.java │ │ ├── AstEmptyBlock.java │ │ ├── AstExport.java │ │ ├── AstExpressionStatement.java │ │ ├── AstFor.java │ │ ├── AstForIn.java │ │ ├── AstForOf.java │ │ ├── AstFunctionDeclareStatement.java │ │ ├── AstIf.java │ │ ├── AstInterpretableGroup.java │ │ ├── AstLabeledStatement.java │ │ ├── AstMultiDeclareStatement.java │ │ ├── AstReturn.java │ │ ├── AstSingleDeclareStatement.java │ │ ├── AstStatement.java │ │ ├── AstSuperStatement.java │ │ ├── AstSwitch.java │ │ ├── AstThisStatement.java │ │ ├── AstThrow.java │ │ ├── AstTry.java │ │ ├── AstWhile.java │ │ ├── AstYield.java │ │ ├── LabeledStatement.java │ │ └── decl │ │ ├── AstDeclaration.java │ │ ├── DestructuredArray.java │ │ ├── DestructuredArrayName.java │ │ ├── DestructuredObject.java │ │ ├── DestructuredObjectName.java │ │ ├── NameDeclaration.java │ │ └── NestedDestructuredPart.java │ ├── error │ ├── ArgumentCountMismatchError.java │ ├── CastError.java │ ├── ConstantReassignError.java │ ├── ConstructorError.java │ ├── IchorError.java │ ├── IndexedMemberNotFoundError.java │ ├── InternalScriptError.java │ ├── NamedMemberNotFoundError.java │ ├── ParseError.java │ ├── ParseErrorMessage.java │ ├── ParseErrorType.java │ ├── RedeclarationError.java │ ├── ScopeDepthError.java │ ├── ScopeMemberNotFoundError.java │ ├── ScriptError.java │ ├── ScriptThrowError.java │ ├── ScriptTimedOutError.java │ ├── TokenStreamError.java │ └── WIPFeatureError.java │ ├── exit │ ├── BreakExit.java │ ├── ContinueExit.java │ ├── EndOfFileExit.java │ ├── ExitType.java │ ├── LabelExit.java │ ├── ReturnExit.java │ └── ScopeExit.java │ ├── java │ ├── AnnotatedElementPrototype.java │ ├── BooleanPrototype.java │ ├── JavaClassPrototype.java │ ├── JavaMembers.java │ ├── LocalJavaMembers.java │ └── StaticJavaMembers.java │ ├── prototype │ ├── Prototype.java │ ├── PrototypeConstant.java │ ├── PrototypeConstructor.java │ ├── PrototypeFunction.java │ ├── PrototypeProperty.java │ ├── PrototypeStaticFunction.java │ ├── PrototypeStaticProperty.java │ ├── PrototypeSupplier.java │ └── PrototypeTypeAdapter.java │ ├── slot │ ├── EmptySlotMap.java │ ├── Slot.java │ ├── SlotArrayMap.java │ ├── SlotHashMap.java │ └── SlotMap.java │ ├── token │ ├── DeclaringToken.java │ ├── IdentifierToken.java │ ├── InKeywordToken.java │ ├── InstanceofKeywordToken.java │ ├── Keyword.java │ ├── KeywordToken.java │ ├── PositionedToken.java │ ├── Symbol.java │ ├── Token.java │ ├── TokenPos.java │ ├── TokenPosSupplier.java │ ├── TokenSource.java │ └── TokenStream.java │ ├── type │ ├── ArrayJS.java │ ├── CollectionJS.java │ ├── IterableJS.java │ ├── ListJS.java │ ├── MapJS.java │ ├── MathJS.java │ ├── NumberJS.java │ ├── ObjectJS.java │ ├── RegExpJS.java │ ├── SetJS.java │ └── StringJS.java │ └── util │ ├── AssignType.java │ ├── CharacterScanner.java │ ├── ClassFunctionInstance.java │ ├── ClassPrototype.java │ ├── Empty.java │ ├── EvaluableConstant.java │ ├── FunctionInstance.java │ ├── Functions.java │ ├── IchorUtils.java │ ├── JavaArray.java │ ├── NamedSignature.java │ ├── NamedTokenSource.java │ ├── PrintWrapper.java │ ├── ScopeWrapper.java │ └── Signature.java └── test └── java └── dev └── latvian └── apps └── ichor └── test ├── AdvancedInterpreterTests.java ├── AdvancedTestUtils.java ├── InterpreterExpressionTests.java ├── InterpreterTests.java ├── ParserTests.java ├── ReflectionExample.java ├── ScopeTests.java ├── TestConsole.java └── TokenTests.java /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'main' 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | if: | 12 | !contains(github.event.head_commit.message, '[ci skip]') 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v3 16 | - name: Set up JDK 17 17 | uses: actions/setup-java@v3 18 | with: 19 | distribution: 'temurin' 20 | java-version: '17' 21 | cache: gradle 22 | - name: Grant execute permission for gradlew 23 | run: chmod +x gradlew 24 | - name: Validate Gradle Wrapper 25 | uses: gradle/wrapper-validation-action@v1 26 | - name: Build and Publish with Gradle 27 | uses: gradle/gradle-build-action@v2 28 | env: 29 | MAVEN_TOKEN: ${{ secrets.MAVEN_TOKEN }} 30 | SAPS_TOKEN: ${{ secrets.SAPS_TOKEN }} 31 | with: 32 | arguments: build -x test publish --stacktrace --no-daemon -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # eclipse 2 | /bin 3 | *.launch 4 | .settings 5 | .metadata 6 | .classpath 7 | .project 8 | 9 | # idea 10 | /out 11 | *.ipr 12 | *.iws 13 | *.iml 14 | .idea 15 | /run 16 | /parser_file_tests 17 | /test262 18 | 19 | # gradle 20 | /build 21 | .gradle 22 | /logs 23 | 24 | # other 25 | /eclipse 26 | /data 27 | *.sh 28 | *.log 29 | *.hprof -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Kristiāns Micītis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](icons/ichor_github_banner.png) 2 | 3 | --- 4 | 5 | # About 6 | 7 | Ichor is a WIP language interpreter for Java that supports JavaScript, TypeScript and other languages in the future, made from scratch by some guy who was just really bored. 8 | 9 | It's loosely based on concepts of [Crafting Interpreters' JLox](https://github.com/munificent/craftinginterpreters) and [Mozilla's Rhino](https://github.com/mozilla/rhino). 10 | 11 | Discord Server: [discord.gg/lat](https://discord.gg/lat) 12 | 13 | [![](https://kubejs.com/discord.png)](https://discord.gg/lat) 14 | 15 | # Supported Languages 16 | 17 | ### JavaScript 18 | 19 | Fully supported ES5 (as far as I'm aware), partially supported ES6. 20 | 21 | ### TypeScript 22 | 23 | Not counting JavaScript support, only partial support for types and TS keywords. 24 | 25 | # Running code 26 | 27 | No example yet! But you can take a look at the [test package](/src/test/java/dev/latvian/apps/ichor/test). 28 | 29 | # Credits 30 | 31 | - [game-icons.net](https://game-icons.net/1x1/sbed/fire.html) for icon 32 | - [Crafting Interpreters](https://craftinginterpreters.com/) by Robert Nystrom (Great book, read it if you are interested in making your own language/interpreter) 33 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | import java.time.Instant 2 | 3 | plugins { 4 | id 'java' 5 | id 'maven-publish' 6 | } 7 | 8 | sourceCompatibility = JavaVersion.VERSION_21 9 | targetCompatibility = JavaVersion.VERSION_21 10 | 11 | def ENV = System.getenv() 12 | version = "${project_version}-${ENV.GITHUB_RUN_NUMBER ? 'build.' + ENV.GITHUB_RUN_NUMBER : 'local.' + Instant.now().epochSecond}" 13 | archivesBaseName = 'ichor' 14 | group = 'dev.latvian.apps' 15 | 16 | repositories { 17 | mavenCentral() 18 | } 19 | 20 | dependencies { 21 | compileOnly('org.jetbrains:annotations:23.0.0') 22 | implementation("org.ow2.asm:asm:9.2") 23 | testImplementation('org.junit.jupiter:junit-jupiter-api:5.10.2') 24 | testImplementation('org.junit.jupiter:junit-jupiter-engine:5.10.2') 25 | // testImplementation('junit:junit:4.13.2') 26 | } 27 | 28 | jar { 29 | manifest { 30 | attributes 'Implementation-Version': archiveVersion 31 | } 32 | } 33 | 34 | test { 35 | useJUnitPlatform() 36 | } 37 | 38 | compileJava { 39 | options.encoding = "UTF-8" 40 | options.release.set(21) 41 | } 42 | 43 | java { 44 | sourceCompatibility = targetCompatibility = '21' 45 | withSourcesJar() 46 | } 47 | 48 | publishing { 49 | publications { 50 | mavenIchor(MavenPublication) { 51 | artifactId = 'ichor' 52 | from components.java 53 | } 54 | } 55 | 56 | repositories { 57 | if (ENV.MAVEN_TOKEN) { 58 | maven { 59 | url "https://maven.latvian.dev/releases" 60 | credentials { 61 | username = "lat" 62 | password = "${ENV.MAVEN_TOKEN}" 63 | } 64 | } 65 | } 66 | 67 | if (ENV.SAPS_TOKEN) { 68 | maven { 69 | url "https://maven.saps.dev/releases" 70 | credentials { 71 | username = "latvian" 72 | password = "${ENV.SAPS_TOKEN}" 73 | } 74 | } 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G 2 | org.gradle.daemon=false 3 | project_version=1.0.0 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/latvian-dev/ichor/4bd728dd82859746b6ee4392253dbe25183ea1de/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Dec 18 16:24:02 EET 2019 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /icons/ichor_a_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/latvian-dev/ichor/4bd728dd82859746b6ee4392253dbe25183ea1de/icons/ichor_a_512.png -------------------------------------------------------------------------------- /icons/ichor_github_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/latvian-dev/ichor/4bd728dd82859746b6ee4392253dbe25183ea1de/icons/ichor_github_banner.png -------------------------------------------------------------------------------- /icons/ichor_r_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/latvian-dev/ichor/4bd728dd82859746b6ee4392253dbe25183ea1de/icons/ichor_r_512.png -------------------------------------------------------------------------------- /icons/ichor_s_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/latvian-dev/ichor/4bd728dd82859746b6ee4392253dbe25183ea1de/icons/ichor_s_128.png -------------------------------------------------------------------------------- /icons/ichor_s_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/latvian-dev/ichor/4bd728dd82859746b6ee4392253dbe25183ea1de/icons/ichor_s_512.png -------------------------------------------------------------------------------- /icons/ichor_s_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/latvian-dev/ichor/4bd728dd82859746b6ee4392253dbe25183ea1de/icons/ichor_s_64.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'ichor' 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/Callable.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor; 2 | 3 | import dev.latvian.apps.ichor.util.Empty; 4 | 5 | @FunctionalInterface 6 | public interface Callable { 7 | Object call(Scope scope, Object[] args, boolean hasNew); 8 | 9 | default Object[] evalArgs(Scope scope, Object[] arguments) { 10 | if (arguments.length == 0) { 11 | return Empty.OBJECTS; 12 | } 13 | 14 | var args = arguments; 15 | 16 | for (int i = 0; i < args.length; i++) { 17 | var a = scope.eval(args[i]); 18 | 19 | if (a != args[i]) { 20 | if (args == arguments) { 21 | args = arguments.clone(); 22 | } 23 | 24 | args[i] = a; 25 | } 26 | } 27 | 28 | return args; 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/CallableTypeAdapter.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor; 2 | 3 | import dev.latvian.apps.ichor.util.Empty; 4 | 5 | import java.lang.reflect.InvocationHandler; 6 | import java.lang.reflect.Method; 7 | import java.lang.reflect.Proxy; 8 | 9 | public interface CallableTypeAdapter extends Callable, TypeAdapter, InvocationHandler { 10 | Scope getEvalScope(); 11 | 12 | @Override 13 | @SuppressWarnings("unchecked") 14 | default T adapt(Scope scope, Class type) { 15 | return (T) Proxy.newProxyInstance(scope.root.context.getClassLoader() == null ? type.getClassLoader() : scope.root.context.getClassLoader(), new Class[]{type}, this); 16 | } 17 | 18 | @Override 19 | default Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 20 | if (method.getDeclaringClass() == Object.class) { 21 | return switch (method.getName()) { 22 | case "toString" -> toString(); 23 | case "hashCode" -> hashCode(); 24 | case "equals" -> args != null && args.length >= 1 && proxy == args[0]; 25 | default -> null; 26 | }; 27 | } 28 | 29 | if (method.isDefault()) { 30 | return InvocationHandler.invokeDefault(proxy, method, args); 31 | } 32 | 33 | var scope = getEvalScope(); 34 | return scope.as(call(scope, args == null ? Empty.OBJECTS : args, false), method.getReturnType()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/Context.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | import java.util.concurrent.CompletableFuture; 6 | import java.util.concurrent.Executor; 7 | 8 | public class Context { 9 | private int maxScopeDepth; 10 | private long interpretingTimeout; 11 | private long tokenStreamTimeout; 12 | private ClassLoader classLoader; 13 | private Executor timeoutExecutor, timeoutExecutorAfter; 14 | private DebuggerCallback debuggerCallback; 15 | 16 | public Context() { 17 | maxScopeDepth = 1000; 18 | interpretingTimeout = 30000L; 19 | tokenStreamTimeout = 5000L; 20 | classLoader = null; 21 | timeoutExecutor = null; 22 | timeoutExecutorAfter = null; 23 | } 24 | 25 | public int getMaxScopeDepth() { 26 | return maxScopeDepth; 27 | } 28 | 29 | public void setMaxScopeDepth(int maxScopeDepth) { 30 | this.maxScopeDepth = maxScopeDepth; 31 | } 32 | 33 | public long getInterpretingTimeout() { 34 | return interpretingTimeout; 35 | } 36 | 37 | public void setInterpretingTimeout(long interpretingTimeout) { 38 | this.interpretingTimeout = interpretingTimeout; 39 | } 40 | 41 | public long getTokenStreamTimeout() { 42 | return tokenStreamTimeout; 43 | } 44 | 45 | public void setTokenStreamTimeout(long tokenStreamTimeout) { 46 | this.tokenStreamTimeout = tokenStreamTimeout; 47 | } 48 | 49 | @Nullable 50 | public ClassLoader getClassLoader() { 51 | return classLoader; 52 | } 53 | 54 | public void setClassLoader(ClassLoader cl) { 55 | classLoader = cl; 56 | } 57 | 58 | @Nullable 59 | public Executor getTimeoutExecutor() { 60 | if (timeoutExecutor == null) { 61 | timeoutExecutor = CompletableFuture.completedFuture(null).defaultExecutor(); 62 | } 63 | 64 | return timeoutExecutor; 65 | } 66 | 67 | public void setTimeoutExecutor(Executor executor) { 68 | timeoutExecutor = executor; 69 | } 70 | 71 | @Nullable 72 | public Executor getTimeoutExecutorAfter() { 73 | return timeoutExecutorAfter; 74 | } 75 | 76 | public void setTimeoutExecutorAfter(Executor executor) { 77 | timeoutExecutorAfter = executor; 78 | } 79 | 80 | public void setDebuggerCallback(DebuggerCallback callback) { 81 | debuggerCallback = callback; 82 | } 83 | 84 | public void onDebugger(Scope scope) { 85 | if (debuggerCallback != null) { 86 | debuggerCallback.onDebugger(scope); 87 | } 88 | } 89 | 90 | @Override 91 | public String toString() { 92 | return "Context"; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ContextProperty.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor; 2 | 3 | public record ContextProperty(String name, T defaultValue) { 4 | @Override 5 | public String toString() { 6 | return name; 7 | } 8 | 9 | @Override 10 | public int hashCode() { 11 | return name.hashCode(); 12 | } 13 | 14 | @Override 15 | public boolean equals(Object obj) { 16 | return obj instanceof ContextProperty p && p.name.equals(name); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/DebuggerCallback.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor; 2 | 3 | @FunctionalInterface 4 | public interface DebuggerCallback { 5 | void onDebugger(Scope scope); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/Deletable.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor; 2 | 3 | public interface Deletable { 4 | static void deleteObject(Scope scope, Object o) { 5 | if (o instanceof Deletable) { 6 | ((Deletable) o).onDeleted(scope); 7 | } 8 | } 9 | 10 | void onDeleted(Scope scope); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/Evaluable.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor; 2 | 3 | public interface Evaluable { 4 | Object eval(Scope scope); 5 | 6 | default void evalString(Scope scope, StringBuilder builder) { 7 | var e = this.eval(scope); 8 | 9 | if (e == this) { 10 | builder.append(this); 11 | } else { 12 | scope.asString(e, builder, false); 13 | } 14 | } 15 | 16 | default double evalDouble(Scope scope) { 17 | var e = this.eval(scope); 18 | 19 | if (e == this) { 20 | return Double.NaN; 21 | } else { 22 | return scope.asDouble(e); 23 | } 24 | } 25 | 26 | default int evalInt(Scope scope) { 27 | var d = evalDouble(scope); 28 | return Double.isNaN(d) ? 0 : (int) d; 29 | } 30 | 31 | default boolean evalBoolean(Scope scope) { 32 | var d = evalDouble(scope); 33 | return !Double.isNaN(d) && d != 0D; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/EvaluableType.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor; 2 | 3 | public enum EvaluableType { 4 | UNKNOWN, 5 | BOOLEAN, 6 | NUMBER, 7 | STRING 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/Interpretable.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor; 2 | 3 | import dev.latvian.apps.ichor.error.IchorError; 4 | import dev.latvian.apps.ichor.error.InternalScriptError; 5 | import dev.latvian.apps.ichor.exit.ScopeExit; 6 | import dev.latvian.apps.ichor.token.TokenPos; 7 | import dev.latvian.apps.ichor.token.TokenPosSupplier; 8 | 9 | public interface Interpretable { 10 | Interpretable[] EMPTY_INTERPRETABLE_ARRAY = new Interpretable[0]; 11 | 12 | void interpret(Scope scope); 13 | 14 | default void interpretSafe(Scope scope) { 15 | try { 16 | interpret(scope); 17 | } catch (ScopeExit pass) { 18 | throw pass; 19 | } catch (IchorError pass) { 20 | if (pass.tokenPos == TokenPos.UNKNOWN && this instanceof TokenPosSupplier pos) { 21 | pass.tokenPos = pos.getPos(); 22 | } 23 | 24 | throw pass; 25 | } catch (Throwable ex) { 26 | throw new InternalScriptError(ex).pos(this); 27 | } 28 | } 29 | 30 | default void optimize(Parser parser) { 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/Optimizable.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor; 2 | 3 | public interface Optimizable { 4 | default Object optimize(Parser parser) { 5 | return this; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/RemappedEnum.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor; 2 | 3 | public interface RemappedEnum { 4 | String getRemappedEnumName(Context cx, Scope scope); 5 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ScriptValue.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor; 2 | 3 | import dev.latvian.apps.ichor.prototype.Prototype; 4 | 5 | import java.util.Objects; 6 | 7 | public class ScriptValue { 8 | public final Object value; 9 | public final Prototype prototype; 10 | 11 | public ScriptValue(Object value, Prototype prototype) { 12 | this.value = value; 13 | this.prototype = prototype; 14 | } 15 | 16 | @Override 17 | public String toString() { 18 | return "[" + prototype.getPrototypeName() + " " + value + "]"; 19 | } 20 | 21 | @Override 22 | public int hashCode() { 23 | return Objects.hashCode(value); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/Special.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor; 2 | 3 | import dev.latvian.apps.ichor.prototype.Prototype; 4 | import dev.latvian.apps.ichor.prototype.PrototypeSupplier; 5 | import dev.latvian.apps.ichor.token.Token; 6 | import dev.latvian.apps.ichor.token.TokenPos; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | public class Special implements Token, Evaluable, PrototypeSupplier { 10 | public static final Object NOT_FOUND = new Object(); // Internal use only 11 | public static final Special NULL = new Special("null"); 12 | public static final Special UNDEFINED = new Special("undefined"); 13 | 14 | public static boolean isInvalid(@Nullable Object o) { 15 | return o == null || o instanceof Special; 16 | } 17 | 18 | public final Prototype prototype; 19 | public final ScriptValue scriptValue; 20 | 21 | private Special(String name) { 22 | this.prototype = new Prototype<>(null, name, Void.TYPE); 23 | this.scriptValue = new ScriptValue(null, prototype); 24 | } 25 | 26 | @Override 27 | public Prototype getPrototype(Scope scope) { 28 | return prototype; 29 | } 30 | 31 | @Override 32 | public int hashCode() { 33 | return 0; 34 | } 35 | 36 | @Override 37 | public boolean equals(Object obj) { 38 | return obj == null || obj instanceof Special; 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return prototype.getPrototypeName(); 44 | } 45 | 46 | @Override 47 | public Object eval(Scope scope) { 48 | return this == UNDEFINED ? this : null; 49 | } 50 | 51 | @Override 52 | public boolean evalBoolean(Scope scope) { 53 | return false; 54 | } 55 | 56 | @Override 57 | public double evalDouble(Scope scope) { 58 | return Double.NaN; 59 | } 60 | 61 | @Override 62 | public int evalInt(Scope scope) { 63 | return 0; 64 | } 65 | 66 | @Override 67 | public Evaluable toEvaluable(Parser parser, TokenPos pos) { 68 | return this; 69 | } 70 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/TypeAdapter.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor; 2 | 3 | public interface TypeAdapter { 4 | default boolean canAdapt(Scope scope, Class type) { 5 | if (type != null && type.isInterface()) { 6 | return scope.getClassPrototype(type).isSingleMethodInterface(); 7 | } 8 | 9 | return false; 10 | } 11 | 12 | T adapt(Scope scope, Class type); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/annotation/Hidden.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.annotation; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Use this annotation to hide objects in java classes from javascript.
11 | * If added to a member (field or method), they will act as undefined / non-existant.
12 | * If added to a class, all members will be hidden.
13 | * If added to a constructor, new Type() will be hidden.
14 | * For fields transient keyword can (and is encouraged to) be used instead of this annotation. 15 | */ 16 | @Documented 17 | @Retention(RetentionPolicy.RUNTIME) 18 | @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR}) 19 | public @interface Hidden { 20 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/annotation/Remap.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.annotation; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Allows you to change field or method name 11 | */ 12 | @Documented 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target({ElementType.METHOD, ElementType.FIELD}) 15 | public @interface Remap { 16 | String value(); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/annotation/RemapPrefix.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.annotation; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Repeatable; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * Allows you to change field or method name on class scale with prefix 12 | */ 13 | @Documented 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target({ElementType.TYPE}) 16 | @Repeatable(RemapPrefixRep.class) 17 | public @interface RemapPrefix { 18 | String value(); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/annotation/RemapPrefixRep.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.annotation; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Allows you to change field or method name on class scale with prefix 11 | */ 12 | @Documented 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target({ElementType.TYPE}) 15 | public @interface RemapPrefixRep { 16 | RemapPrefix[] value(); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/AppendableAst.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast; 2 | 3 | public interface AppendableAst { 4 | void append(AstStringBuilder builder); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/Ast.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast; 2 | 3 | import dev.latvian.apps.ichor.token.TokenPos; 4 | import dev.latvian.apps.ichor.token.TokenPosSupplier; 5 | 6 | public abstract class Ast implements AppendableAst, TokenPosSupplier { 7 | public TokenPos pos = TokenPos.UNKNOWN; 8 | 9 | @Override 10 | public String toString() { 11 | var sb = new AstStringBuilder(); 12 | append(sb); 13 | 14 | if (pos != TokenPos.UNKNOWN) { 15 | sb.append(" @ "); 16 | sb.append(pos); 17 | } 18 | 19 | return sb.toString(); 20 | } 21 | 22 | @SuppressWarnings("unchecked") 23 | public T pos(TokenPosSupplier pos) { 24 | this.pos = pos.getPos(); 25 | return (T) this; 26 | } 27 | 28 | @Override 29 | public TokenPos getPos() { 30 | return pos; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstAwait.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.Special; 6 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 7 | import dev.latvian.apps.ichor.error.ScriptError; 8 | 9 | import java.util.concurrent.Future; 10 | 11 | public class AstAwait extends AstExpression { 12 | public Object future; 13 | 14 | public AstAwait(Object future) { 15 | this.future = future; 16 | } 17 | 18 | @Override 19 | public void append(AstStringBuilder builder) { 20 | builder.append("await "); 21 | builder.appendValue(future); 22 | } 23 | 24 | @Override 25 | public Object eval(Scope scope) { 26 | var e = scope.eval(future); 27 | 28 | if (Special.isInvalid(e)) { 29 | return e; 30 | } else if (e instanceof Future f) { 31 | try { 32 | return f.get(); 33 | } catch (Exception ex) { 34 | throw new FutureResolveError(f, ex).pos(this); 35 | } 36 | } 37 | 38 | throw new NotFutureError(e).pos(this); 39 | } 40 | 41 | @Override 42 | public Object optimize(Parser parser) { 43 | future = parser.optimize(future); 44 | return this; 45 | } 46 | 47 | public static class NotFutureError extends ScriptError { 48 | 49 | public final Object value; 50 | 51 | public NotFutureError(Object value) { 52 | super(value + " is not a Promise/Future"); 53 | this.value = value; 54 | } 55 | } 56 | 57 | public static class FutureResolveError extends ScriptError { 58 | public final Future future; 59 | 60 | public FutureResolveError(Future future, Throwable ex) { 61 | super(ex); 62 | this.future = future; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstClassFunction.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Interpretable; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.ast.statement.AstClass; 6 | import dev.latvian.apps.ichor.util.ClassFunctionInstance; 7 | 8 | public class AstClassFunction extends AstFunction { 9 | public enum Type { 10 | CONSTRUCTOR, GETTER, SETTER, METHOD 11 | } 12 | 13 | public final AstClass owner; 14 | public final Type type; 15 | 16 | public AstClassFunction(AstClass owner, AstParam[] params, Interpretable body, int modifiers, Type type) { 17 | super(params, body, modifiers | AstFunction.Mod.CLASS); 18 | this.owner = owner; 19 | this.type = type; 20 | } 21 | 22 | @Override 23 | public ClassFunctionInstance eval(Scope scope) { 24 | return new ClassFunctionInstance(this, scope); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstClassPrototype.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.Special; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | import dev.latvian.apps.ichor.prototype.PrototypeSupplier; 7 | 8 | public class AstClassPrototype extends AstExpression { 9 | public final Object from; 10 | 11 | public AstClassPrototype(Object from) { 12 | this.from = from; 13 | } 14 | 15 | @Override 16 | public Object eval(Scope scope) { 17 | var self = scope.eval(from); 18 | 19 | if (self == null) { 20 | return Special.UNDEFINED; 21 | } else if (self instanceof PrototypeSupplier ps) { 22 | return ps.getPrototype(scope); 23 | } else { 24 | return scope.getClassPrototype(self.getClass()); 25 | } 26 | } 27 | 28 | @Override 29 | public void append(AstStringBuilder builder) { 30 | builder.appendValue(from); 31 | builder.append(".prototype"); 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstDelete.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | 7 | public class AstDelete extends AstExpression { 8 | public final AstGetBase get; 9 | 10 | public AstDelete(AstGetBase get) { 11 | this.get = get; 12 | } 13 | 14 | @Override 15 | public void append(AstStringBuilder builder) { 16 | builder.append("delete "); 17 | get.append(builder); 18 | builder.append(';'); 19 | } 20 | 21 | @Override 22 | public Object eval(Scope scope) { 23 | return get.delete(scope); 24 | } 25 | 26 | @Override 27 | public Object optimize(Parser parser) { 28 | get.optimize(parser); 29 | return this; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstExpression.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Evaluable; 4 | import dev.latvian.apps.ichor.Optimizable; 5 | import dev.latvian.apps.ichor.ast.Ast; 6 | 7 | public abstract class AstExpression extends Ast implements Evaluable, Optimizable { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstFunction.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Interpretable; 4 | import dev.latvian.apps.ichor.Parser; 5 | import dev.latvian.apps.ichor.Scope; 6 | import dev.latvian.apps.ichor.Special; 7 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 8 | import dev.latvian.apps.ichor.util.FunctionInstance; 9 | 10 | public class AstFunction extends AstExpression implements Comparable { 11 | public interface Mod { 12 | int ARROW = 1 << 0; 13 | int CLASS = 1 << 1; 14 | int STATIC = 1 << 2; 15 | int SET = 1 << 3; 16 | int GET = 1 << 4; 17 | int CONSTRUCTOR = 1 << 5; 18 | int ASYNC = 1 << 6; 19 | int VARARGS = 1 << 7; 20 | int STATEMENT = 1 << 8; 21 | int GENERATOR = 1 << 9; 22 | } 23 | 24 | public final AstParam[] params; 25 | public final Interpretable body; 26 | public final int modifiers; 27 | public final int requiredParams; 28 | public String functionName; 29 | 30 | public AstFunction(AstParam[] params, Interpretable body, int modifiers) { 31 | this.params = params; 32 | this.body = body; 33 | this.modifiers = modifiers; 34 | 35 | int requiredParams0 = params.length; 36 | 37 | for (int i = params.length - 1; i >= 0; i--) { 38 | if (params[i].defaultValue != Special.UNDEFINED) { 39 | requiredParams0--; 40 | } else { 41 | break; 42 | } 43 | } 44 | 45 | this.requiredParams = requiredParams0; 46 | } 47 | 48 | public boolean hasMod(int mod) { 49 | return (modifiers & mod) != 0; 50 | } 51 | 52 | @Override 53 | public void append(AstStringBuilder builder) { 54 | if (hasMod(Mod.ASYNC)) { 55 | builder.append("async "); 56 | } 57 | 58 | if (!hasMod(Mod.ARROW)) { 59 | builder.append("function"); 60 | 61 | if (functionName != null) { 62 | builder.append(' '); 63 | builder.append(functionName); 64 | } 65 | } 66 | 67 | builder.append('('); 68 | 69 | for (int i = 0; i < params.length; i++) { 70 | if (i > 0) { 71 | builder.append(','); 72 | } 73 | 74 | if (i == params.length - 1 && hasMod(Mod.VARARGS)) { 75 | builder.append("..."); 76 | } 77 | 78 | params[i].append(builder); 79 | } 80 | 81 | builder.append(")"); 82 | 83 | if (hasMod(Mod.ARROW)) { 84 | builder.append("=>"); 85 | } 86 | 87 | builder.append(body); 88 | } 89 | 90 | @Override 91 | public FunctionInstance eval(Scope scope) { 92 | return new FunctionInstance(this, scope); 93 | } 94 | 95 | @Override 96 | public int compareTo(AstFunction o) { 97 | return Integer.compare(o.params.length, params.length); 98 | } 99 | 100 | @Override 101 | public AstFunction optimize(Parser parser) { 102 | body.optimize(parser); 103 | return this; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstGetBase.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public abstract class AstGetBase extends AstExpression { 6 | public abstract void set(Scope scope, Object value); 7 | 8 | public abstract boolean delete(Scope scope); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstGetByEvaluable.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.Special; 6 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 7 | import dev.latvian.apps.ichor.error.IndexedMemberNotFoundError; 8 | import dev.latvian.apps.ichor.error.NamedMemberNotFoundError; 9 | 10 | public class AstGetByEvaluable extends AstGetFrom { 11 | public Object key; 12 | 13 | public AstGetByEvaluable(Object from, Object key) { 14 | super(from); 15 | this.key = key; 16 | } 17 | 18 | @Override 19 | public void append(AstStringBuilder builder) { 20 | builder.appendValue(from); 21 | builder.append('['); 22 | builder.appendValue(key); 23 | builder.append(']'); 24 | } 25 | 26 | @Override 27 | public Object eval(Scope scope) { 28 | var k = scope.eval(key); 29 | var self = evalSelf(scope); 30 | var p = scope.getPrototype(self); 31 | 32 | Object r; 33 | 34 | if (k instanceof Number n) { 35 | var ki = n.intValue(); 36 | 37 | if (self == p) { 38 | throw new IndexedMemberNotFoundError(ki, p, self).pos(this); 39 | } 40 | 41 | r = p.getLocal(scope, p.cast(self), ki); 42 | 43 | if (r == Special.NOT_FOUND) { 44 | throw new IndexedMemberNotFoundError(ki, p, self).pos(this); 45 | } 46 | } else { 47 | var ks = scope.asString(k, false); 48 | r = p.getInternal(scope, self, ks); 49 | 50 | if (r == Special.NOT_FOUND) { 51 | throw new NamedMemberNotFoundError(ks, p, self).pos(this); 52 | } 53 | } 54 | 55 | return r; 56 | } 57 | 58 | @Override 59 | public void set(Scope scope, Object value) { 60 | var k = scope.eval(key); 61 | var self = evalSelf(scope); 62 | var p = scope.getPrototype(self); 63 | 64 | if (k instanceof Number n) { 65 | var ki = n.intValue(); 66 | 67 | if (self == p) { 68 | throw new IndexedMemberNotFoundError(ki, p, self).pos(this); 69 | } 70 | 71 | if (!p.setLocal(scope, p.cast(self), ki, value)) { 72 | throw new IndexedMemberNotFoundError(ki, p, self).pos(this); 73 | } 74 | } else { 75 | var ks = scope.asString(k, false); 76 | 77 | if (!(self == p ? p.setStatic(scope, ks, value) : p.setLocal(scope, p.cast(self), ks, value))) { 78 | throw new NamedMemberNotFoundError(ks, p, self).pos(this); 79 | } 80 | } 81 | } 82 | 83 | @Override 84 | public boolean delete(Scope scope) { 85 | var k = scope.eval(key); 86 | var self = evalSelf(scope); 87 | var p = scope.getPrototype(self); 88 | 89 | if (k instanceof Number n) { 90 | var ki = n.intValue(); 91 | 92 | if (self == p) { 93 | throw new IndexedMemberNotFoundError(ki, p, self).pos(this); 94 | } 95 | 96 | return p.deleteLocal(scope, p.cast(self), ki); 97 | } else { 98 | var ks = scope.asString(k, false); 99 | 100 | if (self == p) { 101 | throw new NamedMemberNotFoundError(ks, p, self).pos(this); 102 | } 103 | 104 | return p.deleteLocal(scope, p.cast(self), ks); 105 | } 106 | } 107 | 108 | @Override 109 | public Object optimize(Parser parser) { 110 | super.optimize(parser); 111 | key = parser.optimize(key); 112 | return this; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstGetByIndex.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.Special; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | import dev.latvian.apps.ichor.error.IndexedMemberNotFoundError; 7 | 8 | public class AstGetByIndex extends AstGetFrom { 9 | public final int index; 10 | 11 | public AstGetByIndex(Object from, int index) { 12 | super(from); 13 | this.index = index; 14 | } 15 | 16 | @Override 17 | public void append(AstStringBuilder builder) { 18 | builder.appendValue(from); 19 | builder.append('['); 20 | builder.appendValue(index); 21 | builder.append(']'); 22 | } 23 | 24 | @Override 25 | public Object eval(Scope scope) { 26 | var self = evalSelf(scope); 27 | var p = scope.getPrototype(self); 28 | 29 | if (self == p) { 30 | throw new IndexedMemberNotFoundError(index, p, self).pos(this); 31 | } 32 | 33 | var r = p.getLocal(scope, p.cast(self), index); 34 | 35 | if (r == Special.NOT_FOUND) { 36 | throw new IndexedMemberNotFoundError(index, p, self).pos(this); 37 | } 38 | 39 | return r; 40 | } 41 | 42 | @Override 43 | public void set(Scope scope, Object value) { 44 | var self = evalSelf(scope); 45 | var p = scope.getPrototype(self); 46 | 47 | if (self == p) { 48 | throw new IndexedMemberNotFoundError(index, p, self).pos(this); 49 | } 50 | 51 | if (!p.setLocal(scope, p.cast(self), index, value)) { 52 | throw new IndexedMemberNotFoundError(index, p, self).pos(this); 53 | } 54 | } 55 | 56 | @Override 57 | public boolean delete(Scope scope) { 58 | var self = evalSelf(scope); 59 | var p = scope.getPrototype(self); 60 | 61 | if (self == p) { 62 | throw new IndexedMemberNotFoundError(index, p, self).pos(this); 63 | } 64 | 65 | return p.deleteLocal(scope, p.cast(self), index); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstGetByName.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Callable; 4 | import dev.latvian.apps.ichor.Parser; 5 | import dev.latvian.apps.ichor.Scope; 6 | import dev.latvian.apps.ichor.Special; 7 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 8 | import dev.latvian.apps.ichor.error.NamedMemberNotFoundError; 9 | import dev.latvian.apps.ichor.prototype.Prototype; 10 | 11 | import java.util.regex.Pattern; 12 | 13 | public class AstGetByName extends AstGetFrom { 14 | private static final Pattern PLAIN_PATTERN = Pattern.compile("^[a-zA-Z_$][\\w$]*$"); 15 | 16 | public final String name; 17 | 18 | public AstGetByName(Object from, String name) { 19 | super(from); 20 | this.name = name; 21 | } 22 | 23 | @Override 24 | public void append(AstStringBuilder builder) { 25 | builder.appendValue(from); 26 | 27 | if (PLAIN_PATTERN.matcher(name).find()) { 28 | builder.append('.'); 29 | builder.append(name); 30 | } else { 31 | builder.append("['"); 32 | builder.append(name); 33 | builder.append("']"); 34 | } 35 | } 36 | 37 | @Override 38 | public Object eval(Scope scope) { 39 | var self = evalSelf(scope); 40 | var p = scope.getPrototype(self); 41 | var r = p.getInternal(scope, self, name); 42 | 43 | if (r == Special.NOT_FOUND) { 44 | throw new NamedMemberNotFoundError(name, p, self).pos(this); 45 | } 46 | 47 | return r; 48 | } 49 | 50 | @Override 51 | public void set(Scope scope, Object value) { 52 | var self = evalSelf(scope); 53 | var p = scope.getPrototype(self); 54 | 55 | if (!(self == p ? p.setStatic(scope, name, value) : p.setLocal(scope, p.cast(self), name, value))) { 56 | throw new NamedMemberNotFoundError(name, p, self).pos(this); 57 | } 58 | } 59 | 60 | @Override 61 | public boolean delete(Scope scope) { 62 | var self = evalSelf(scope); 63 | var p = scope.getPrototype(self); 64 | 65 | if (self == p) { 66 | throw new NamedMemberNotFoundError(name, p, self).pos(this); 67 | } 68 | 69 | return p.deleteLocal(scope, p.cast(self), name); 70 | } 71 | 72 | @Override 73 | public Object optimize(Parser parser) { 74 | from = parser.optimize(from); 75 | 76 | // Is this correct? So far seems to work 77 | if (from instanceof Prototype p) { 78 | var m = p.getStatic(parser.getRootScope(), name); 79 | 80 | if (m == Special.NOT_FOUND) { 81 | throw new NamedMemberNotFoundError(name, p, p).pos(this); 82 | } 83 | 84 | // Disabled for functions for now, potential issues with static java member scoping 85 | if (!(m instanceof Callable)) { 86 | return m; 87 | } 88 | } 89 | 90 | return this; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstGetByNameOptional.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.Special; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | 7 | public class AstGetByNameOptional extends AstGetByName { 8 | public AstGetByNameOptional(Object from, String name) { 9 | super(from, name); 10 | } 11 | 12 | @Override 13 | public void append(AstStringBuilder builder) { 14 | builder.appendValue(from); 15 | builder.append("?."); 16 | builder.append(name); 17 | } 18 | 19 | @Override 20 | public Object eval(Scope scope) { 21 | var self = evalSelf(scope); 22 | var p = scope.getPrototype(self); 23 | var r = p.getInternal(scope, self, name); 24 | 25 | if (Special.isInvalid(r)) { 26 | return Special.UNDEFINED; 27 | } 28 | 29 | return r; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstGetFrom.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | public abstract class AstGetFrom extends AstGetBase { 8 | public Object from; 9 | 10 | public AstGetFrom(Object from) { 11 | this.from = from; 12 | } 13 | 14 | @Nullable 15 | public Object evalSelf(Scope scope) { 16 | return scope.eval(from); 17 | } 18 | 19 | @Override 20 | public Object optimize(Parser parser) { 21 | from = parser.optimize(from); 22 | return this; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstGetScopeMember.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.Special; 6 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 7 | 8 | public class AstGetScopeMember extends AstGetBase { 9 | public final String name; 10 | 11 | public AstGetScopeMember(String name) { 12 | this.name = name; 13 | } 14 | 15 | @Override 16 | public void append(AstStringBuilder builder) { 17 | builder.append(name); 18 | } 19 | 20 | @Override 21 | public Object eval(Scope scope) { 22 | return scope.getMember(name); 23 | } 24 | 25 | @Override 26 | public void set(Scope scope, Object value) { 27 | scope.setMember(name, value); 28 | } 29 | 30 | @Override 31 | public boolean delete(Scope scope) { 32 | scope.deleteDeclaredMember(name); 33 | return true; 34 | } 35 | 36 | @Override 37 | public Object optimize(Parser parser) { 38 | var slot = parser.getRootScope().getDeclaredMember(name); 39 | 40 | if (slot != null && slot.value != Special.UNDEFINED && slot.isRoot()) { 41 | return slot.value; 42 | } 43 | 44 | return this; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstList.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Evaluable; 4 | import dev.latvian.apps.ichor.Parser; 5 | import dev.latvian.apps.ichor.Scope; 6 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 7 | import dev.latvian.apps.ichor.error.ScriptError; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | public class AstList extends AstExpression { 13 | public static class SpreadError extends ScriptError { 14 | public SpreadError() { 15 | super("Spread used on non-array"); 16 | } 17 | } 18 | 19 | public final List values; 20 | 21 | public AstList(List v) { 22 | values = v; 23 | } 24 | 25 | @Override 26 | public void append(AstStringBuilder builder) { 27 | builder.append('['); 28 | 29 | for (int i = 0; i < values.size(); i++) { 30 | if (i > 0) { 31 | builder.append(','); 32 | } 33 | 34 | builder.appendValue(values.get(i)); 35 | } 36 | 37 | builder.append(']'); 38 | } 39 | 40 | @Override 41 | public Object eval(Scope scope) { 42 | var list = new ArrayList<>(values.size()); 43 | 44 | for (var o : values) { 45 | if (o instanceof AstSpread spread) { 46 | var s = scope.eval(spread.value); 47 | 48 | if (s instanceof Iterable itr) { 49 | for (var o1 : itr) { 50 | list.add(o1); 51 | } 52 | } else { 53 | throw new SpreadError().pos(pos); 54 | } 55 | } else { 56 | list.add(scope.eval(o)); 57 | } 58 | } 59 | 60 | return list; 61 | } 62 | 63 | @Override 64 | public Object optimize(Parser parser) { 65 | for (var o : values) { 66 | if (o instanceof Evaluable) { 67 | return this; 68 | } 69 | } 70 | 71 | return values; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstMap.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Evaluable; 4 | import dev.latvian.apps.ichor.Parser; 5 | import dev.latvian.apps.ichor.Scope; 6 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 7 | import dev.latvian.apps.ichor.error.ScriptError; 8 | 9 | import java.util.LinkedHashMap; 10 | import java.util.Map; 11 | 12 | public class AstMap extends AstExpression { 13 | public static class SpreadError extends ScriptError { 14 | public SpreadError() { 15 | super("Spread used on non-object"); 16 | } 17 | } 18 | 19 | public final Map values; 20 | 21 | public AstMap(Map values) { 22 | this.values = values; 23 | } 24 | 25 | private static boolean isValidIdentifier(String s) { 26 | if (s.isEmpty()) { 27 | return false; 28 | } 29 | 30 | var chars = s.toCharArray(); 31 | 32 | if (!Character.isJavaIdentifierStart(chars[0])) { 33 | return false; 34 | } 35 | 36 | for (int i = 1; i < s.length(); i++) { 37 | if (!Character.isJavaIdentifierPart(chars[i])) { 38 | return false; 39 | } 40 | } 41 | 42 | return true; 43 | } 44 | 45 | @Override 46 | public void append(AstStringBuilder builder) { 47 | builder.append('{'); 48 | boolean first = true; 49 | 50 | for (var entry : values.entrySet()) { 51 | if (first) { 52 | first = false; 53 | } else { 54 | builder.append(','); 55 | } 56 | 57 | if (isValidIdentifier(entry.getKey())) { 58 | builder.append(entry.getKey()); 59 | } else { 60 | builder.appendValue(entry.getKey()); 61 | } 62 | 63 | builder.append(':'); 64 | builder.appendValue(entry.getValue()); 65 | } 66 | 67 | builder.append('}'); 68 | } 69 | 70 | @Override 71 | public Object eval(Scope scope) { 72 | var map = new LinkedHashMap<>(values.size()); 73 | 74 | for (var entry : values.entrySet()) { 75 | var o = entry.getValue(); 76 | 77 | if (o instanceof AstSpread spread) { 78 | var s = scope.eval(spread.value); 79 | 80 | if (s instanceof Map map1) { 81 | map.putAll(map1); 82 | } else { 83 | throw new SpreadError().pos(pos); 84 | } 85 | } else { 86 | map.put(entry.getKey(), scope.eval(o)); 87 | } 88 | } 89 | 90 | return map; 91 | } 92 | 93 | @Override 94 | public Object optimize(Parser parser) { 95 | for (var entry : values.entrySet()) { 96 | if (entry.getValue() instanceof Evaluable) { 97 | return this; 98 | } 99 | } 100 | 101 | return values; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstObjectPrototype.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.Special; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | import dev.latvian.apps.ichor.prototype.PrototypeSupplier; 7 | 8 | public class AstObjectPrototype extends AstExpression { 9 | public final Object from; 10 | 11 | public AstObjectPrototype(Object from) { 12 | this.from = from; 13 | } 14 | 15 | @Override 16 | public Object eval(Scope scope) { 17 | var self = scope.eval(from); 18 | 19 | if (self == null) { 20 | return Special.UNDEFINED; 21 | } else if (self instanceof PrototypeSupplier ps) { 22 | return ps.getPrototype(scope); 23 | } else { 24 | return scope.getPrototype(self); 25 | } 26 | } 27 | 28 | @Override 29 | public void append(AstStringBuilder builder) { 30 | builder.appendValue(from); 31 | builder.append(".__proto__"); 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstParam.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Special; 4 | import dev.latvian.apps.ichor.ast.AppendableAst; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | 7 | public class AstParam implements AppendableAst { 8 | public final String name; 9 | public AstType type; 10 | public Object defaultValue; 11 | 12 | public AstParam(String name) { 13 | this.name = name; 14 | this.type = null; 15 | this.defaultValue = Special.UNDEFINED; 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return name; 21 | } 22 | 23 | @Override 24 | public void append(AstStringBuilder builder) { 25 | builder.append(name); 26 | 27 | if (type != null) { 28 | builder.append(':'); 29 | builder.append(type); 30 | } 31 | 32 | if (defaultValue != Special.UNDEFINED) { 33 | builder.append('='); 34 | builder.appendValue(defaultValue); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstSet.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | 7 | public class AstSet extends AstExpression { 8 | public final AstGetBase get; 9 | public Object value; 10 | 11 | public AstSet(AstGetBase get, Object value) { 12 | this.get = get; 13 | this.value = value; 14 | } 15 | 16 | @Override 17 | public void append(AstStringBuilder builder) { 18 | get.append(builder); 19 | builder.append('='); 20 | builder.appendValue(value); 21 | } 22 | 23 | @Override 24 | public Object eval(Scope scope) { 25 | var v = scope.eval(value); 26 | get.set(scope, v); 27 | return v; 28 | } 29 | 30 | @Override 31 | public Object optimize(Parser parser) { 32 | value = parser.optimize(value); 33 | return this; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstSpread.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 4 | 5 | public class AstSpread extends AstTempExpression { 6 | public Object value; 7 | 8 | public AstSpread(Object v) { 9 | value = v; 10 | } 11 | 12 | @Override 13 | public void append(AstStringBuilder builder) { 14 | builder.append("..."); 15 | builder.appendValue(value); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstTempExpression.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.Special; 5 | 6 | public abstract class AstTempExpression extends AstExpression { 7 | @Override 8 | public Object eval(Scope scope) { 9 | return Special.NOT_FOUND; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstTernary.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | 7 | public class AstTernary extends AstExpression { 8 | public Object condition; 9 | public Object ifTrue; 10 | public Object ifFalse; 11 | 12 | @Override 13 | public void append(AstStringBuilder builder) { 14 | builder.append('('); 15 | builder.appendValue(condition); 16 | builder.append('?'); 17 | builder.appendValue(ifTrue); 18 | builder.append(':'); 19 | builder.appendValue(ifFalse); 20 | builder.append(')'); 21 | } 22 | 23 | @Override 24 | public Object eval(Scope scope) { 25 | return scope.asBoolean(condition) ? scope.eval(ifTrue) : scope.eval(ifFalse); 26 | } 27 | 28 | @Override 29 | public Object optimize(Parser parser) { 30 | condition = parser.optimize(condition); 31 | ifTrue = parser.optimize(ifTrue); 32 | ifFalse = parser.optimize(ifFalse); 33 | return condition instanceof Boolean b ? b ? ifTrue : ifFalse : this; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstType.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.ast.AppendableAst; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | 7 | public abstract class AstType implements AppendableAst { 8 | @FunctionalInterface 9 | public interface CastFunc { 10 | CastFunc NONE = (scope, from) -> from; 11 | 12 | Object cast(Scope scope, Object from); 13 | } 14 | 15 | public static class Generic extends AstType { 16 | public static final Generic ANY = new Generic("any", CastFunc.NONE); 17 | public static final Generic VOID = new Generic("void", CastFunc.NONE); 18 | public static final Generic BOOLEAN = new Generic("boolean", Scope::asBoolean); 19 | public static final Generic NUMBER = new Generic("number", Scope::asNumber); 20 | public static final Generic STRING = new Generic("string", (scope, from) -> scope.asString(from, false)); 21 | public static final Generic FUNCTION = new Generic("function", CastFunc.NONE); 22 | public static final Generic OBJECT = new Generic("object", CastFunc.NONE); 23 | public static final Generic BIGINT = new Generic("bigint", CastFunc.NONE); 24 | public static final Generic ARRAY = new Generic("Array", CastFunc.NONE); 25 | 26 | public final String name; 27 | public final CastFunc cast; 28 | 29 | public Generic(String name, CastFunc cast) { 30 | this.name = name; 31 | this.cast = cast; 32 | } 33 | 34 | @Override 35 | public void append(AstStringBuilder builder) { 36 | builder.append(name); 37 | } 38 | 39 | @Override 40 | public Object cast(Scope scope, Object from) { 41 | return cast.cast(scope, from); 42 | } 43 | } 44 | 45 | public static class Array extends AstType { 46 | public final AstType type; 47 | 48 | public Array(AstType type) { 49 | this.type = type; 50 | } 51 | 52 | @Override 53 | public void append(AstStringBuilder builder) { 54 | type.append(builder); 55 | builder.append('['); 56 | builder.append(']'); 57 | } 58 | } 59 | 60 | public static class Or extends AstType { 61 | public final AstType a; 62 | public final AstType b; 63 | 64 | public Or(AstType a, AstType b) { 65 | this.a = a; 66 | this.b = b; 67 | } 68 | 69 | @Override 70 | public void append(AstStringBuilder builder) { 71 | a.append(builder); 72 | builder.append('|'); 73 | b.append(builder); 74 | } 75 | } 76 | 77 | public static class Typed extends AstType { 78 | public final AstType type; 79 | public final AstType[] subTypes; 80 | 81 | public Typed(AstType type, AstType[] subTypes) { 82 | this.type = type; 83 | this.subTypes = subTypes; 84 | } 85 | 86 | @Override 87 | public void append(AstStringBuilder builder) { 88 | type.append(builder); 89 | builder.append('<'); 90 | 91 | for (int i = 0; i < subTypes.length; i++) { 92 | if (i > 0) { 93 | builder.append(','); 94 | } 95 | 96 | subTypes[i].append(builder); 97 | } 98 | 99 | builder.append('>'); 100 | } 101 | } 102 | 103 | public Object cast(Scope scope, Object from) { 104 | return from; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/AstTypeOf.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression; 2 | 3 | import dev.latvian.apps.ichor.Callable; 4 | import dev.latvian.apps.ichor.Parser; 5 | import dev.latvian.apps.ichor.Scope; 6 | import dev.latvian.apps.ichor.Special; 7 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 8 | 9 | import java.math.BigInteger; 10 | 11 | public class AstTypeOf extends AstExpression { 12 | public Object of; 13 | 14 | public AstTypeOf(Object of) { 15 | this.of = of; 16 | } 17 | 18 | @Override 19 | public Object eval(Scope scope) { 20 | var o = scope.eval(of); 21 | 22 | if (o == Special.UNDEFINED) { 23 | return "undefined"; 24 | } else if (o instanceof String) { 25 | return "string"; 26 | } else if (o instanceof Number) { 27 | return "number"; 28 | } else if (o instanceof Boolean) { 29 | return "boolean"; 30 | } else if (o instanceof BigInteger) { 31 | return "bigint"; 32 | } else if (o instanceof Callable) { 33 | return "function"; 34 | } else { 35 | return "object"; 36 | } 37 | } 38 | 39 | @Override 40 | public void append(AstStringBuilder builder) { 41 | builder.append("typeof "); 42 | builder.appendValue(of); 43 | } 44 | 45 | @Override 46 | public Object optimize(Parser parser) { 47 | of = parser.optimize(of); 48 | 49 | if (of == Special.UNDEFINED) { 50 | return "undefined"; 51 | } else if (of instanceof String) { 52 | return "string"; 53 | } else if (of instanceof Number) { 54 | return "number"; 55 | } else if (of instanceof Boolean) { 56 | return "boolean"; 57 | } else if (of instanceof BigInteger) { 58 | return "bigint"; 59 | } else { 60 | return this; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstAdd.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.error.ScriptError; 6 | 7 | public class AstAdd extends AstBinary { 8 | public static class InvalidAdditionError extends ScriptError { 9 | public final Object left, right; 10 | 11 | public InvalidAdditionError(Object left, Object right) { 12 | super("Can't add " + left + " + " + right); 13 | this.left = left; 14 | this.right = right; 15 | } 16 | } 17 | 18 | @Override 19 | public void appendSymbol(StringBuilder builder) { 20 | builder.append('+'); 21 | } 22 | 23 | @Override 24 | public Object eval(Scope scope) { 25 | var l = scope.eval(left); 26 | var r = scope.eval(right); 27 | 28 | if (l instanceof CharSequence || l instanceof Character || r instanceof CharSequence || r instanceof Character) { 29 | var sb = new StringBuilder(); 30 | scope.asString(l, sb, false); 31 | scope.asString(r, sb, false); 32 | return sb.toString(); 33 | } else if (l instanceof Number && r instanceof Number) { 34 | return ((Number) l).doubleValue() + ((Number) r).doubleValue(); 35 | } else { 36 | throw new InvalidAdditionError(l, r).pos(pos); 37 | } 38 | } 39 | 40 | @Override 41 | public double evalDouble(Scope scope) { 42 | return scope.asDouble(left) + scope.asDouble(right); 43 | } 44 | 45 | @Override 46 | public Object optimize(Parser parser) { 47 | super.optimize(parser); 48 | 49 | if (left instanceof Number l && right instanceof Number r) { 50 | return l.doubleValue() + r.doubleValue(); 51 | } else if (left instanceof CharSequence l && right instanceof CharSequence r) { 52 | return l.toString() + r; 53 | } 54 | 55 | return this; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstAnd.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class AstAnd extends AstBinaryBoolean { 6 | @Override 7 | public void appendSymbol(StringBuilder builder) { 8 | builder.append("&&"); 9 | } 10 | 11 | @Override 12 | public boolean evalBoolean(Scope scope) { 13 | return scope.asBoolean(left) && scope.asBoolean(right); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstBinary.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 5 | import dev.latvian.apps.ichor.ast.expression.AstExpression; 6 | 7 | public abstract class AstBinary extends AstExpression { 8 | public Object left; 9 | public Object right; 10 | 11 | @Override 12 | public final void append(AstStringBuilder builder) { 13 | builder.append('('); 14 | builder.appendValue(left); 15 | appendSymbol(builder.builder); 16 | builder.appendValue(right); 17 | builder.append(')'); 18 | } 19 | 20 | public abstract void appendSymbol(StringBuilder builder); 21 | 22 | @Override 23 | public Object optimize(Parser parser) { 24 | left = parser.optimize(left); 25 | right = parser.optimize(right); 26 | return this; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstBinaryBoolean.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public abstract class AstBinaryBoolean extends AstBinary { 6 | @Override 7 | public Object eval(Scope scope) { 8 | return evalBoolean(scope); 9 | } 10 | 11 | @Override 12 | public double evalDouble(Scope scope) { 13 | return evalBoolean(scope) ? 1D : 0D; 14 | } 15 | 16 | @Override 17 | public int evalInt(Scope scope) { 18 | return evalBoolean(scope) ? 1 : 0; 19 | } 20 | 21 | @Override 22 | public abstract boolean evalBoolean(Scope scope); 23 | 24 | @Override 25 | public void evalString(Scope scope, StringBuilder builder) { 26 | builder.append(evalBoolean(scope)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstBitwiseAnd.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class AstBitwiseAnd extends AstBinary { 6 | @Override 7 | public void appendSymbol(StringBuilder builder) { 8 | builder.append('&'); 9 | } 10 | 11 | @Override 12 | public Object eval(Scope scope) { 13 | return evalInt(scope); 14 | } 15 | 16 | @Override 17 | public double evalDouble(Scope scope) { 18 | return evalInt(scope); 19 | } 20 | 21 | @Override 22 | public int evalInt(Scope scope) { 23 | return scope.asInt(left) & scope.asInt(right); 24 | } 25 | 26 | @Override 27 | public boolean evalBoolean(Scope scope) { 28 | return scope.asBoolean(left) & scope.asBoolean(right); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstBitwiseOr.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class AstBitwiseOr extends AstBinary { 6 | @Override 7 | public void appendSymbol(StringBuilder builder) { 8 | builder.append('|'); 9 | } 10 | 11 | @Override 12 | public Object eval(Scope scope) { 13 | return evalInt(scope); 14 | } 15 | 16 | @Override 17 | public double evalDouble(Scope scope) { 18 | return evalInt(scope); 19 | } 20 | 21 | @Override 22 | public int evalInt(Scope scope) { 23 | return scope.asInt(left) | scope.asInt(right); 24 | } 25 | 26 | @Override 27 | public boolean evalBoolean(Scope scope) { 28 | return scope.asBoolean(left) | scope.asBoolean(right); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstDiv.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | 6 | public class AstDiv extends AstBinary { 7 | @Override 8 | public void appendSymbol(StringBuilder builder) { 9 | builder.append('/'); 10 | } 11 | 12 | @Override 13 | public Object eval(Scope scope) { 14 | return evalDouble(scope); 15 | } 16 | 17 | @Override 18 | public double evalDouble(Scope scope) { 19 | return scope.asDouble(left) / scope.asDouble(right); 20 | } 21 | 22 | @Override 23 | public Object optimize(Parser parser) { 24 | super.optimize(parser); 25 | 26 | if (left instanceof Number l && right instanceof Number r) { 27 | return l.doubleValue() / r.doubleValue(); 28 | } 29 | 30 | return this; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstEq.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class AstEq extends AstBinaryBoolean { 6 | @Override 7 | public void appendSymbol(StringBuilder builder) { 8 | builder.append("=="); 9 | } 10 | 11 | @Override 12 | public boolean evalBoolean(Scope scope) { 13 | return scope.equals(scope.eval(left), scope.eval(right), false); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstGt.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class AstGt extends AstBinaryBoolean { 6 | @Override 7 | public void appendSymbol(StringBuilder builder) { 8 | builder.append('>'); 9 | } 10 | 11 | @Override 12 | public boolean evalBoolean(Scope scope) { 13 | return scope.compareTo(scope.eval(left), scope.eval(right)) > 0; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstGte.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class AstGte extends AstBinaryBoolean { 6 | @Override 7 | public void appendSymbol(StringBuilder builder) { 8 | builder.append(">="); 9 | } 10 | 11 | @Override 12 | public boolean evalBoolean(Scope scope) { 13 | return scope.compareTo(scope.eval(left), scope.eval(right)) >= 0; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstIn.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class AstIn extends AstBinaryBoolean { 6 | @Override 7 | public void appendSymbol(StringBuilder builder) { 8 | builder.append(" in "); 9 | } 10 | 11 | @Override 12 | public boolean evalBoolean(Scope scope) { 13 | //TODO: Implement 14 | return false; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstInstanceOf.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class AstInstanceOf extends AstBinaryBoolean { 6 | @Override 7 | public void appendSymbol(StringBuilder builder) { 8 | builder.append(" instanceof "); 9 | } 10 | 11 | @Override 12 | public boolean evalBoolean(Scope scope) { 13 | //TODO: Implement 14 | return false; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstLsh.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class AstLsh extends AstBinary { 6 | @Override 7 | public void appendSymbol(StringBuilder builder) { 8 | builder.append("<<"); 9 | } 10 | 11 | @Override 12 | public Object eval(Scope scope) { 13 | return evalInt(scope); 14 | } 15 | 16 | @Override 17 | public double evalDouble(Scope scope) { 18 | return evalInt(scope); 19 | } 20 | 21 | @Override 22 | public int evalInt(Scope scope) { 23 | return scope.asInt(left) << scope.asInt(right); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstLt.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class AstLt extends AstBinaryBoolean { 6 | @Override 7 | public void appendSymbol(StringBuilder builder) { 8 | builder.append('<'); 9 | } 10 | 11 | @Override 12 | public boolean evalBoolean(Scope scope) { 13 | return scope.compareTo(scope.eval(left), scope.eval(right)) < 0; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstLte.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class AstLte extends AstBinaryBoolean { 6 | @Override 7 | public void appendSymbol(StringBuilder builder) { 8 | builder.append("<="); 9 | } 10 | 11 | @Override 12 | public boolean evalBoolean(Scope scope) { 13 | return scope.compareTo(scope.eval(left), scope.eval(right)) <= 0; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstMod.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class AstMod extends AstBinary { 6 | @Override 7 | public void appendSymbol(StringBuilder builder) { 8 | builder.append('%'); 9 | } 10 | 11 | @Override 12 | public Object eval(Scope scope) { 13 | return evalDouble(scope); 14 | } 15 | 16 | @Override 17 | public double evalDouble(Scope scope) { 18 | return scope.asDouble(left) % scope.asDouble(right); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstMul.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | 6 | public class AstMul extends AstBinary { 7 | @Override 8 | public void appendSymbol(StringBuilder builder) { 9 | builder.append('*'); 10 | } 11 | 12 | @Override 13 | public Object eval(Scope scope) { 14 | return evalDouble(scope); 15 | } 16 | 17 | @Override 18 | public double evalDouble(Scope scope) { 19 | return scope.asDouble(left) * scope.asDouble(right); 20 | } 21 | 22 | @Override 23 | public Object optimize(Parser parser) { 24 | super.optimize(parser); 25 | 26 | if (left instanceof Number l && right instanceof Number r) { 27 | return l.doubleValue() * r.doubleValue(); 28 | } 29 | 30 | return this; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstNc.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.Special; 5 | 6 | public class AstNc extends AstBinary { 7 | @Override 8 | public void appendSymbol(StringBuilder builder) { 9 | builder.append("??"); 10 | } 11 | 12 | @Override 13 | public Object eval(Scope scope) { 14 | var l = scope.eval(left); 15 | return Special.isInvalid(l) ? scope.eval(right) : l; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstNeq.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class AstNeq extends AstBinaryBoolean { 6 | @Override 7 | public void appendSymbol(StringBuilder builder) { 8 | builder.append("!="); 9 | } 10 | 11 | @Override 12 | public boolean evalBoolean(Scope scope) { 13 | return !scope.equals(scope.eval(left), scope.eval(right), false); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstOr.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class AstOr extends AstBinaryBoolean { 6 | @Override 7 | public void appendSymbol(StringBuilder builder) { 8 | builder.append("||"); 9 | } 10 | 11 | @Override 12 | public boolean evalBoolean(Scope scope) { 13 | return scope.asBoolean(left) || scope.asBoolean(right); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstPow.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | 6 | public class AstPow extends AstBinary { 7 | @Override 8 | public void appendSymbol(StringBuilder builder) { 9 | builder.append("**"); 10 | } 11 | 12 | @Override 13 | public Object eval(Scope scope) { 14 | return evalDouble(scope); 15 | } 16 | 17 | @Override 18 | public double evalDouble(Scope scope) { 19 | return Math.pow(scope.asDouble(left), scope.asDouble(right)); 20 | } 21 | 22 | @Override 23 | public Object optimize(Parser parser) { 24 | super.optimize(parser); 25 | 26 | if (left instanceof Double l && right instanceof Double r) { 27 | return Math.pow(l, r); 28 | } 29 | 30 | return this; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstRsh.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class AstRsh extends AstBinary { 6 | @Override 7 | public void appendSymbol(StringBuilder builder) { 8 | builder.append(">>"); 9 | } 10 | 11 | @Override 12 | public Object eval(Scope scope) { 13 | return evalInt(scope); 14 | } 15 | 16 | @Override 17 | public double evalDouble(Scope scope) { 18 | return evalInt(scope); 19 | } 20 | 21 | @Override 22 | public int evalInt(Scope scope) { 23 | return scope.asInt(left) >> scope.asInt(right); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstSeq.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class AstSeq extends AstBinaryBoolean { 6 | @Override 7 | public void appendSymbol(StringBuilder builder) { 8 | builder.append("==="); 9 | } 10 | 11 | @Override 12 | public boolean evalBoolean(Scope scope) { 13 | return scope.equals(scope.eval(left), scope.eval(right), true); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstSneq.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class AstSneq extends AstBinaryBoolean { 6 | @Override 7 | public void appendSymbol(StringBuilder builder) { 8 | builder.append("!=="); 9 | } 10 | 11 | @Override 12 | public boolean evalBoolean(Scope scope) { 13 | return !scope.equals(scope.eval(left), scope.eval(right), true); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstSub.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | 6 | public class AstSub extends AstBinary { 7 | @Override 8 | public void appendSymbol(StringBuilder builder) { 9 | builder.append('-'); 10 | } 11 | 12 | @Override 13 | public Object eval(Scope scope) { 14 | return evalDouble(scope); 15 | } 16 | 17 | @Override 18 | public double evalDouble(Scope scope) { 19 | return scope.asDouble(left) - scope.asDouble(right); 20 | } 21 | 22 | @Override 23 | public Object optimize(Parser parser) { 24 | super.optimize(parser); 25 | 26 | if (left instanceof Number l && right instanceof Number r) { 27 | return l.doubleValue() - r.doubleValue(); 28 | } 29 | 30 | return this; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstUrsh.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class AstUrsh extends AstBinary { 6 | @Override 7 | public void appendSymbol(StringBuilder builder) { 8 | builder.append(">>>"); 9 | } 10 | 11 | @Override 12 | public Object eval(Scope scope) { 13 | return evalInt(scope); 14 | } 15 | 16 | @Override 17 | public double evalDouble(Scope scope) { 18 | return evalInt(scope); 19 | } 20 | 21 | @Override 22 | public int evalInt(Scope scope) { 23 | return scope.asInt(left) >>> scope.asInt(right); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/binary/AstXor.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.binary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class AstXor extends AstBinary { 6 | @Override 7 | public void appendSymbol(StringBuilder builder) { 8 | builder.append('^'); 9 | } 10 | 11 | @Override 12 | public Object eval(Scope scope) { 13 | return evalInt(scope); 14 | } 15 | 16 | @Override 17 | public double evalDouble(Scope scope) { 18 | return evalInt(scope); 19 | } 20 | 21 | @Override 22 | public int evalInt(Scope scope) { 23 | return scope.asInt(left) ^ scope.asInt(right); 24 | } 25 | 26 | @Override 27 | public boolean evalBoolean(Scope scope) { 28 | return scope.asBoolean(left) ^ scope.asBoolean(right); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/unary/AstAdd1L.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.unary; 2 | 3 | public class AstAdd1L extends AstAdditive1 { 4 | @Override 5 | public boolean isAdd() { 6 | return true; 7 | } 8 | 9 | @Override 10 | public boolean isLeft() { 11 | return true; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/unary/AstAdd1R.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.unary; 2 | 3 | public class AstAdd1R extends AstAdditive1 { 4 | @Override 5 | public boolean isAdd() { 6 | return true; 7 | } 8 | 9 | @Override 10 | public boolean isLeft() { 11 | return false; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/unary/AstAdditive1.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.unary; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 5 | import dev.latvian.apps.ichor.ast.expression.AstGetBase; 6 | 7 | public abstract class AstAdditive1 extends AstUnary { 8 | public abstract boolean isAdd(); 9 | 10 | public abstract boolean isLeft(); 11 | 12 | @Override 13 | public void append(AstStringBuilder builder) { 14 | if (isLeft()) { 15 | builder.append(isAdd() ? "++" : "--"); 16 | builder.appendValue(node); 17 | } else { 18 | builder.appendValue(node); 19 | builder.append(isAdd() ? "++" : "--"); 20 | } 21 | } 22 | 23 | @Override 24 | public Object eval(Scope scope) { 25 | return evalDouble(scope); 26 | } 27 | 28 | @Override 29 | public double evalDouble(Scope scope) { 30 | double o = scope.asDouble(node); 31 | double n = isAdd() ? o + 1D : o - 1D; 32 | ((AstGetBase) node).set(scope, n); 33 | return isLeft() ? n : o; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/unary/AstBitwiseNot.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.unary; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | 7 | public class AstBitwiseNot extends AstUnary { 8 | @Override 9 | public void append(AstStringBuilder builder) { 10 | builder.append('~'); 11 | builder.appendValue(node); 12 | } 13 | 14 | @Override 15 | public Object eval(Scope scope) { 16 | return evalInt(scope); 17 | } 18 | 19 | @Override 20 | public double evalDouble(Scope scope) { 21 | return evalInt(scope); 22 | } 23 | 24 | @Override 25 | public int evalInt(Scope scope) { 26 | return ~scope.asInt(node); 27 | } 28 | 29 | @Override 30 | public Object optimize(Parser parser) { 31 | super.optimize(parser); 32 | 33 | if (node instanceof Number n) { 34 | return ~((int) n.intValue()); 35 | } 36 | 37 | return this; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/unary/AstNegate.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.unary; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | 7 | public class AstNegate extends AstUnary { 8 | @Override 9 | public void append(AstStringBuilder builder) { 10 | builder.append('-'); 11 | builder.appendValue(node); 12 | } 13 | 14 | @Override 15 | public Object eval(Scope scope) { 16 | return evalDouble(scope); 17 | } 18 | 19 | @Override 20 | public double evalDouble(Scope scope) { 21 | return -scope.asDouble(node); 22 | } 23 | 24 | @Override 25 | public int evalInt(Scope scope) { 26 | return -scope.asInt(node); 27 | } 28 | 29 | @Override 30 | public Object optimize(Parser parser) { 31 | super.optimize(parser); 32 | 33 | if (node instanceof Number n) { 34 | return -n.doubleValue(); 35 | } 36 | 37 | return this; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/unary/AstNot.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.unary; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | 7 | public class AstNot extends AstUnary { 8 | @Override 9 | public void append(AstStringBuilder builder) { 10 | builder.append('!'); 11 | builder.appendValue(node); 12 | } 13 | 14 | @Override 15 | public Object eval(Scope scope) { 16 | return evalBoolean(scope); 17 | } 18 | 19 | @Override 20 | public double evalDouble(Scope scope) { 21 | return evalInt(scope); 22 | } 23 | 24 | @Override 25 | public int evalInt(Scope scope) { 26 | return scope.asBoolean(node) ? 0 : 1; 27 | } 28 | 29 | @Override 30 | public boolean evalBoolean(Scope scope) { 31 | return !scope.asBoolean(node); 32 | } 33 | 34 | @Override 35 | public Object optimize(Parser parser) { 36 | super.optimize(parser); 37 | 38 | if (node instanceof Boolean n) { 39 | return !n; 40 | } 41 | 42 | return this; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/unary/AstPositive.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.unary; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | 7 | public class AstPositive extends AstUnary { 8 | @Override 9 | public void append(AstStringBuilder builder) { 10 | builder.append('+'); 11 | builder.appendValue(node); 12 | } 13 | 14 | @Override 15 | public Object eval(Scope scope) { 16 | return evalDouble(scope); 17 | } 18 | 19 | @Override 20 | public double evalDouble(Scope scope) { 21 | return scope.asDouble(node); 22 | } 23 | 24 | @Override 25 | public int evalInt(Scope scope) { 26 | return scope.asInt(node); 27 | } 28 | 29 | @Override 30 | public Object optimize(Parser parser) { 31 | if (node instanceof Number) { 32 | return node; 33 | } 34 | 35 | return parser.optimize(node); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/unary/AstSub1L.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.unary; 2 | 3 | public class AstSub1L extends AstAdditive1 { 4 | @Override 5 | public boolean isAdd() { 6 | return false; 7 | } 8 | 9 | @Override 10 | public boolean isLeft() { 11 | return true; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/unary/AstSub1R.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.unary; 2 | 3 | public class AstSub1R extends AstAdditive1 { 4 | @Override 5 | public boolean isAdd() { 6 | return false; 7 | } 8 | 9 | @Override 10 | public boolean isLeft() { 11 | return false; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/expression/unary/AstUnary.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.expression.unary; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.ast.expression.AstExpression; 5 | 6 | public abstract class AstUnary extends AstExpression { 7 | public Object node; 8 | 9 | @Override 10 | public Object optimize(Parser parser) { 11 | node = parser.optimize(node); 12 | return this; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstBlock.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Interpretable; 4 | import dev.latvian.apps.ichor.Parser; 5 | import dev.latvian.apps.ichor.Scope; 6 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 7 | import dev.latvian.apps.ichor.exit.BreakExit; 8 | import dev.latvian.apps.ichor.exit.ExitType; 9 | import dev.latvian.apps.ichor.exit.ReturnExit; 10 | 11 | public class AstBlock extends AstLabeledStatement { 12 | public Interpretable[] interpretable; 13 | public boolean forceReturn; 14 | 15 | @Override 16 | public void append(AstStringBuilder builder) { 17 | if (!label.isEmpty()) { 18 | builder.append(label); 19 | builder.append(':'); 20 | } 21 | 22 | builder.append('{'); 23 | 24 | for (var statement : interpretable) { 25 | builder.append(statement); 26 | } 27 | 28 | builder.append('}'); 29 | } 30 | 31 | @Override 32 | public boolean handle(ExitType type) { 33 | return type == ExitType.BREAK && !label.isEmpty(); 34 | } 35 | 36 | @Override 37 | public void interpret(Scope scope) { 38 | var s = scope.push(); 39 | 40 | for (var statement : interpretable) { 41 | try { 42 | statement.interpretSafe(s); 43 | } catch (BreakExit exit) { 44 | if (exit.stop == this) { 45 | break; 46 | } else { 47 | throw exit; 48 | } 49 | } 50 | } 51 | 52 | if (forceReturn) { 53 | throw ReturnExit.DEFAULT_RETURN; 54 | } 55 | } 56 | 57 | @Override 58 | public void optimize(Parser parser) { 59 | for (var statement : interpretable) { 60 | statement.optimize(parser); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstBreak.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 5 | import dev.latvian.apps.ichor.exit.BreakExit; 6 | 7 | public class AstBreak extends AstStatement { 8 | public final LabeledStatement stop; 9 | 10 | public AstBreak(LabeledStatement stop) { 11 | this.stop = stop; 12 | } 13 | 14 | @Override 15 | public void append(AstStringBuilder builder) { 16 | builder.append("break"); 17 | 18 | if (stop != null) { 19 | builder.append(" "); 20 | builder.append(stop.getLabel()); 21 | } 22 | 23 | builder.append(';'); 24 | } 25 | 26 | @Override 27 | public void interpret(Scope scope) { 28 | throw new BreakExit(stop); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstClass.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | import dev.latvian.apps.ichor.ast.expression.AstClassFunction; 7 | import dev.latvian.apps.ichor.util.ClassPrototype; 8 | 9 | import java.util.HashMap; 10 | import java.util.LinkedHashSet; 11 | import java.util.Map; 12 | 13 | public class AstClass extends AstStatement { 14 | public final String name; 15 | public Object parent; 16 | public AstClassFunction constructor; 17 | public final Map methods; 18 | public final Map getters; 19 | public final Map setters; 20 | 21 | public AstClass(String name) { 22 | this.name = name; 23 | this.parent = null; 24 | this.constructor = null; 25 | this.methods = new HashMap<>(); 26 | this.getters = new HashMap<>(); 27 | this.setters = new HashMap<>(); 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | if (parent == null) { 33 | return "class " + name; 34 | } else { 35 | return "class " + name + " extends " + parent; 36 | } 37 | } 38 | 39 | @Override 40 | public void append(AstStringBuilder builder) { 41 | builder.append("class "); 42 | builder.append(name); 43 | 44 | if (parent != null) { 45 | builder.append(" extends "); 46 | builder.appendValue(parent); 47 | } 48 | 49 | builder.append(" {"); 50 | 51 | var keys = new LinkedHashSet(); 52 | keys.addAll(methods.keySet()); 53 | keys.addAll(getters.keySet()); 54 | keys.addAll(setters.keySet()); 55 | 56 | if (keys.isEmpty()) { 57 | builder.append("...}"); 58 | } else { 59 | builder.append(String.join(",", keys)); 60 | builder.append("}"); 61 | } 62 | } 63 | 64 | @Override 65 | public void interpret(Scope scope) { 66 | scope.addImmutable(name, new ClassPrototype(this, scope)); 67 | } 68 | 69 | @Override 70 | public void optimize(Parser parser) { 71 | super.optimize(parser); 72 | parent = parser.optimize(parent); 73 | 74 | if (constructor != null) { 75 | constructor.optimize(parser); 76 | } 77 | 78 | for (var entry : methods.entrySet()) { 79 | entry.getValue().optimize(parser); 80 | } 81 | 82 | for (var entry : getters.entrySet()) { 83 | entry.getValue().optimize(parser); 84 | } 85 | 86 | for (var entry : setters.entrySet()) { 87 | entry.getValue().optimize(parser); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstContinue.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 5 | import dev.latvian.apps.ichor.exit.ContinueExit; 6 | 7 | public class AstContinue extends AstStatement { 8 | public final LabeledStatement stop; 9 | 10 | public AstContinue(LabeledStatement stop) { 11 | this.stop = stop; 12 | } 13 | 14 | @Override 15 | public void append(AstStringBuilder builder) { 16 | builder.append("continue"); 17 | 18 | if (stop != null) { 19 | builder.append(" "); 20 | builder.append(stop.getLabel()); 21 | } 22 | 23 | builder.append(';'); 24 | } 25 | 26 | @Override 27 | public void interpret(Scope scope) { 28 | throw new ContinueExit(stop); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstDebugger.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 5 | 6 | public class AstDebugger extends AstStatement { 7 | @Override 8 | public void append(AstStringBuilder builder) { 9 | builder.append("debugger"); 10 | } 11 | 12 | @Override 13 | public void interpret(Scope scope) { 14 | scope.root.context.onDebugger(scope); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstDeclareStatement.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.ast.statement.decl.AstDeclaration; 4 | import dev.latvian.apps.ichor.token.DeclaringToken; 5 | 6 | public abstract class AstDeclareStatement extends AstStatement { 7 | public static class Part { 8 | public static final Part[] EMPTY = new Part[0]; 9 | 10 | public final AstDeclaration declaration; 11 | public Object value; 12 | 13 | public Part(AstDeclaration declaration, Object value) { 14 | this.declaration = declaration; 15 | this.value = value; 16 | } 17 | } 18 | 19 | public final DeclaringToken assignToken; 20 | 21 | public AstDeclareStatement(DeclaringToken assignToken) { 22 | this.assignToken = assignToken; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstDoWhile.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 5 | import dev.latvian.apps.ichor.exit.BreakExit; 6 | import dev.latvian.apps.ichor.exit.ContinueExit; 7 | 8 | public class AstDoWhile extends AstWhile { 9 | @Override 10 | public void append(AstStringBuilder builder) { 11 | if (!label.isEmpty()) { 12 | builder.append(label); 13 | builder.append(':'); 14 | } 15 | 16 | builder.append("do "); 17 | 18 | if (body != null) { 19 | builder.append(body); 20 | } else { 21 | builder.append(';'); 22 | } 23 | 24 | builder.append("while ("); 25 | builder.appendValue(condition); 26 | builder.append(')'); 27 | } 28 | 29 | @Override 30 | public void interpret(Scope scope) { 31 | do { 32 | if (body != null) { 33 | try { 34 | body.interpretSafe(scope); 35 | } catch (BreakExit exit) { 36 | if (exit.stop == this) { 37 | break; 38 | } else { 39 | throw exit; 40 | } 41 | } catch (ContinueExit exit) { 42 | if (exit.stop != this) { 43 | throw exit; 44 | } 45 | } 46 | } 47 | } 48 | while (scope.asBoolean(condition)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstEmptyBlock.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 5 | import dev.latvian.apps.ichor.exit.ReturnExit; 6 | 7 | public class AstEmptyBlock extends AstStatement { 8 | public final boolean forceReturn; 9 | 10 | public AstEmptyBlock(boolean forceReturn) { 11 | this.forceReturn = forceReturn; 12 | } 13 | 14 | @Override 15 | public void append(AstStringBuilder builder) { 16 | builder.append('{'); 17 | builder.append('}'); 18 | } 19 | 20 | @Override 21 | public void interpret(Scope scope) { 22 | if (forceReturn) { 23 | throw ReturnExit.DEFAULT_RETURN; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstExport.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 5 | 6 | public class AstExport extends AstStatement { 7 | public final Object statement; 8 | 9 | public AstExport(Object s) { 10 | statement = s; 11 | } 12 | 13 | @Override 14 | public void append(AstStringBuilder builder) { 15 | builder.append("export "); 16 | builder.appendValue(statement); 17 | } 18 | 19 | @Override 20 | public void interpret(Scope scope) { 21 | // TODO: Implement 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstExpressionStatement.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | 7 | public class AstExpressionStatement extends AstStatement { 8 | public Object expression; 9 | 10 | public AstExpressionStatement(Object expression) { 11 | this.expression = expression; 12 | } 13 | 14 | @Override 15 | public void append(AstStringBuilder builder) { 16 | builder.appendValue(expression); 17 | builder.append(';'); 18 | } 19 | 20 | @Override 21 | public void interpret(Scope scope) { 22 | scope.eval(expression); 23 | } 24 | 25 | @Override 26 | public void optimize(Parser parser) { 27 | expression = parser.optimize(expression); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstFor.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Interpretable; 4 | import dev.latvian.apps.ichor.Parser; 5 | import dev.latvian.apps.ichor.Scope; 6 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 7 | import dev.latvian.apps.ichor.exit.BreakExit; 8 | import dev.latvian.apps.ichor.exit.ContinueExit; 9 | 10 | public class AstFor extends AstLabeledStatement { 11 | public Interpretable initializer; 12 | public Object condition; 13 | public Interpretable increment; 14 | public Interpretable body; 15 | 16 | @Override 17 | public void append(AstStringBuilder builder) { 18 | if (!label.isEmpty()) { 19 | builder.append(label); 20 | builder.append(':'); 21 | } 22 | 23 | builder.append("for("); 24 | 25 | if (!(initializer instanceof AstEmptyBlock)) { 26 | builder.append(initializer); 27 | } 28 | 29 | if (!(initializer instanceof AstDeclareStatement)) { 30 | builder.append(';'); 31 | } 32 | 33 | if (condition != null) { 34 | builder.appendValue(condition); 35 | } 36 | 37 | builder.append(';'); 38 | 39 | if (!(increment instanceof AstEmptyBlock)) { 40 | builder.append(increment); 41 | } 42 | 43 | builder.append(')'); 44 | 45 | if (body != null) { 46 | builder.append(body); 47 | } else { 48 | builder.append(';'); 49 | } 50 | } 51 | 52 | @Override 53 | public void interpret(Scope scope) { 54 | var s = scope.push(); 55 | 56 | for (initializer.interpretSafe(scope); condition == null || scope.asBoolean(condition); increment.interpretSafe(scope)) { 57 | try { 58 | if (body != null) { 59 | body.interpretSafe(s); 60 | } 61 | } catch (BreakExit exit) { 62 | if (exit.stop == this) { 63 | break; 64 | } else { 65 | throw exit; 66 | } 67 | } catch (ContinueExit exit) { 68 | if (exit.stop != this) { 69 | throw exit; 70 | } 71 | } 72 | } 73 | } 74 | 75 | @Override 76 | public void optimize(Parser parser) { 77 | initializer.optimize(parser); 78 | condition = parser.optimize(condition); 79 | increment.optimize(parser); 80 | body.optimize(parser); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstForIn.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.util.IchorUtils; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Collection; 9 | import java.util.Iterator; 10 | 11 | public class AstForIn extends AstForOf { 12 | @Override 13 | protected String appendKeyword() { 14 | return " in "; 15 | } 16 | 17 | @Override 18 | @Nullable 19 | protected Iterator getIterable(Scope scope, Object self) { 20 | if (self instanceof Collection c) { 21 | var keys = new Object[c.size()]; 22 | 23 | for (int i = 0; i < keys.length; i++) { 24 | keys[i] = i; 25 | } 26 | 27 | return IchorUtils.iteratorOf(keys); 28 | } else if (self instanceof Iterable itr) { 29 | var keys = new ArrayList(); 30 | 31 | int i = 0; 32 | 33 | for (var ignored : itr) { 34 | keys.add(i++); 35 | } 36 | 37 | return keys.iterator(); 38 | } else { 39 | var p = scope.getPrototype(self); 40 | return IchorUtils.iteratorOf(p.keys(scope, p.cast(self))); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstForOf.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Interpretable; 4 | import dev.latvian.apps.ichor.Parser; 5 | import dev.latvian.apps.ichor.Scope; 6 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 7 | import dev.latvian.apps.ichor.ast.statement.decl.AstDeclaration; 8 | import dev.latvian.apps.ichor.exit.BreakExit; 9 | import dev.latvian.apps.ichor.exit.ContinueExit; 10 | import dev.latvian.apps.ichor.token.DeclaringToken; 11 | import dev.latvian.apps.ichor.util.IchorUtils; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.util.Iterator; 15 | 16 | public class AstForOf extends AstLabeledStatement { 17 | public DeclaringToken assignToken; 18 | public AstDeclaration declaration; 19 | public Object from; 20 | public Interpretable body; 21 | 22 | protected String appendKeyword() { 23 | return " of "; 24 | } 25 | 26 | @Override 27 | public void append(AstStringBuilder builder) { 28 | if (!label.isEmpty()) { 29 | builder.append(label); 30 | builder.append(':'); 31 | } 32 | 33 | builder.append("for("); 34 | builder.append(assignToken); 35 | builder.append(' '); 36 | builder.append(declaration); 37 | builder.append(' '); 38 | builder.append(appendKeyword()); 39 | builder.append(' '); 40 | builder.appendValue(from); 41 | 42 | builder.append(')'); 43 | 44 | if (body != null) { 45 | builder.append(body); 46 | } else { 47 | builder.append("{}"); 48 | } 49 | } 50 | 51 | @Override 52 | public void interpret(Scope scope) { 53 | var self = scope.eval(from); 54 | var itr = getIterable(scope, self); 55 | 56 | while (itr != null && itr.hasNext()) { 57 | var it = itr.next(); 58 | 59 | try { 60 | var s = scope.push(); 61 | declaration.declare(s, assignToken.flags, it); 62 | body.interpretSafe(s); 63 | } catch (BreakExit exit) { 64 | if (exit.stop == this) { 65 | break; 66 | } else { 67 | throw exit; 68 | } 69 | } catch (ContinueExit exit) { 70 | if (exit.stop != this) { 71 | throw exit; 72 | } 73 | } 74 | } 75 | } 76 | 77 | @Nullable 78 | protected Iterator getIterable(Scope scope, Object self) { 79 | var itr = IchorUtils.iteratorOf(self); 80 | 81 | if (itr != null) { 82 | return itr; 83 | } 84 | 85 | var p = scope.getPrototype(self); 86 | return IchorUtils.iteratorOf(p.values(scope, p.cast(self))); 87 | } 88 | 89 | @Override 90 | public void optimize(Parser parser) { 91 | from = parser.optimize(from); 92 | body.optimize(parser); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstFunctionDeclareStatement.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | import dev.latvian.apps.ichor.ast.expression.AstFunction; 7 | import dev.latvian.apps.ichor.token.Keyword; 8 | 9 | public class AstFunctionDeclareStatement extends AstDeclareStatement { 10 | public final AstFunction function; 11 | 12 | public AstFunctionDeclareStatement(AstFunction function) { 13 | super(Keyword.CONST); 14 | this.function = function; 15 | } 16 | 17 | @Override 18 | public void interpret(Scope scope) { 19 | scope.addImmutable(function.functionName, scope.eval(function)); 20 | } 21 | 22 | @Override 23 | public void append(AstStringBuilder builder) { 24 | builder.append(function); 25 | } 26 | 27 | @Override 28 | public void optimize(Parser parser) { 29 | function.optimize(parser); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstIf.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Interpretable; 4 | import dev.latvian.apps.ichor.Parser; 5 | import dev.latvian.apps.ichor.Scope; 6 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 7 | import dev.latvian.apps.ichor.exit.BreakExit; 8 | import dev.latvian.apps.ichor.exit.ExitType; 9 | 10 | public class AstIf extends AstLabeledStatement { 11 | public Object condition; 12 | public Interpretable trueBody; 13 | public Interpretable falseBody; 14 | 15 | @Override 16 | public void append(AstStringBuilder builder) { 17 | if (!label.isEmpty()) { 18 | builder.append(label); 19 | builder.append(':'); 20 | } 21 | 22 | builder.append("if ("); 23 | builder.appendValue(condition); 24 | builder.append(") "); 25 | 26 | if (trueBody != null) { 27 | builder.append(trueBody); 28 | } else { 29 | builder.append(';'); 30 | } 31 | 32 | if (falseBody != null) { 33 | builder.append(" else "); 34 | builder.append(falseBody); 35 | } 36 | } 37 | 38 | @Override 39 | public boolean handle(ExitType type) { 40 | return type == ExitType.BREAK && !label.isEmpty(); 41 | } 42 | 43 | @Override 44 | public void interpret(Scope scope) { 45 | try { 46 | if (scope.asBoolean(condition)) { 47 | if (trueBody != null) { 48 | trueBody.interpretSafe(scope); 49 | } 50 | } else if (falseBody != null) { 51 | falseBody.interpretSafe(scope); 52 | } 53 | } catch (BreakExit exit) { 54 | if (exit.stop != this) { 55 | throw exit; 56 | } 57 | } 58 | } 59 | 60 | @Override 61 | public void optimize(Parser parser) { 62 | condition = parser.optimize(condition); 63 | 64 | if (trueBody != null) { 65 | trueBody.optimize(parser); 66 | } 67 | 68 | if (falseBody != null) { 69 | falseBody.optimize(parser); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstInterpretableGroup.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Interpretable; 4 | import dev.latvian.apps.ichor.Parser; 5 | import dev.latvian.apps.ichor.Scope; 6 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 7 | 8 | import java.util.Collection; 9 | import java.util.List; 10 | 11 | public class AstInterpretableGroup extends AstStatement { 12 | public static Interpretable optimized(List statements) { 13 | return statements.size() == 1 ? statements.get(0) : new AstInterpretableGroup(statements); 14 | } 15 | 16 | public final Interpretable[] interpretable; 17 | 18 | public AstInterpretableGroup(Interpretable... statements) { 19 | this.interpretable = statements; 20 | } 21 | 22 | public AstInterpretableGroup(Collection statements) { 23 | this(statements.toArray(Interpretable.EMPTY_INTERPRETABLE_ARRAY)); 24 | } 25 | 26 | @Override 27 | public void append(AstStringBuilder builder) { 28 | for (var statement : interpretable) { 29 | builder.append(statement); 30 | } 31 | } 32 | 33 | @Override 34 | public void interpret(Scope scope) { 35 | for (var statement : interpretable) { 36 | statement.interpretSafe(scope); 37 | } 38 | } 39 | 40 | @Override 41 | public void optimize(Parser parser) { 42 | for (var statement : interpretable) { 43 | statement.optimize(parser); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstLabeledStatement.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | public abstract class AstLabeledStatement extends AstStatement implements LabeledStatement { 4 | public String label = ""; 5 | 6 | @Override 7 | public String getLabel() { 8 | return label; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstMultiDeclareStatement.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | import dev.latvian.apps.ichor.token.DeclaringToken; 7 | 8 | public class AstMultiDeclareStatement extends AstDeclareStatement { 9 | public final Part[] parts; 10 | 11 | public AstMultiDeclareStatement(DeclaringToken assignToken, Part[] parts) { 12 | super(assignToken); 13 | this.parts = parts; 14 | } 15 | 16 | @Override 17 | public void append(AstStringBuilder builder) { 18 | builder.append(assignToken); 19 | builder.append(' '); 20 | 21 | for (int i = 0; i < parts.length; i++) { 22 | if (i > 0) { 23 | builder.append(','); 24 | } 25 | 26 | builder.append(parts[i]); 27 | } 28 | 29 | builder.append(';'); 30 | } 31 | 32 | @Override 33 | public void interpret(Scope scope) { 34 | for (var p : parts) { 35 | p.declaration.declare(scope, assignToken.flags, scope.eval(p.value)); 36 | } 37 | } 38 | 39 | @Override 40 | public void optimize(Parser parser) { 41 | for (var p : parts) { 42 | p.value = parser.optimize(p.value); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstReturn.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.Special; 6 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 7 | import dev.latvian.apps.ichor.exit.ReturnExit; 8 | 9 | public class AstReturn extends AstStatement { 10 | public Object value; 11 | 12 | public AstReturn(Object value) { 13 | this.value = value; 14 | } 15 | 16 | @Override 17 | public void append(AstStringBuilder builder) { 18 | builder.append("return "); 19 | builder.appendValue(value); 20 | } 21 | 22 | @Override 23 | public void interpret(Scope scope) { 24 | var result = scope.eval(value); 25 | throw result == Special.UNDEFINED ? ReturnExit.DEFAULT_RETURN : new ReturnExit(result); 26 | } 27 | 28 | @Override 29 | public void optimize(Parser parser) { 30 | value = parser.optimize(value); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstSingleDeclareStatement.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | import dev.latvian.apps.ichor.ast.statement.decl.AstDeclaration; 7 | import dev.latvian.apps.ichor.token.DeclaringToken; 8 | 9 | public class AstSingleDeclareStatement extends AstDeclareStatement { 10 | public final AstDeclaration declaration; 11 | public Object value; 12 | 13 | public AstSingleDeclareStatement(DeclaringToken assignToken, AstDeclaration declaration, Object value) { 14 | super(assignToken); 15 | this.declaration = declaration; 16 | this.value = value; 17 | } 18 | 19 | @Override 20 | public void append(AstStringBuilder builder) { 21 | builder.append(assignToken); 22 | builder.append(' '); 23 | declaration.append(builder); 24 | builder.append('='); 25 | builder.appendValue(value); 26 | builder.append(';'); 27 | } 28 | 29 | @Override 30 | public void interpret(Scope scope) { 31 | declaration.declare(scope, assignToken.flags, scope.eval(value)); 32 | } 33 | 34 | @Override 35 | public void optimize(Parser parser) { 36 | value = parser.optimize(value); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstStatement.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Interpretable; 4 | import dev.latvian.apps.ichor.ast.Ast; 5 | 6 | public abstract class AstStatement extends Ast implements Interpretable { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstSuperStatement.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.error.ScriptError; 5 | import dev.latvian.apps.ichor.util.ClassPrototype; 6 | 7 | public class AstSuperStatement extends AstThisStatement { 8 | public static class InvalidCallError extends ScriptError { 9 | public InvalidCallError() { 10 | super("You can only call super() from a constructor"); 11 | } 12 | } 13 | 14 | public AstSuperStatement(Object[] a) { 15 | super(a); 16 | } 17 | 18 | @Override 19 | public String getStatementName() { 20 | return "super"; 21 | } 22 | 23 | @Override 24 | public void interpret(Scope scope) { 25 | if (scope.scopeThis instanceof ClassPrototype.Instance c) { 26 | c.interpretConstructorSuper(arguments); 27 | } else { 28 | throw new InvalidCallError(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstSwitch.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Interpretable; 4 | import dev.latvian.apps.ichor.Parser; 5 | import dev.latvian.apps.ichor.Scope; 6 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 7 | import dev.latvian.apps.ichor.exit.BreakExit; 8 | import dev.latvian.apps.ichor.exit.ExitType; 9 | 10 | // TODO: Create optimized version for constant string, number and enum cases 11 | public class AstSwitch extends AstStatement implements LabeledStatement { 12 | public static final class AstCase { 13 | public static final AstCase[] EMPTY = new AstCase[0]; 14 | 15 | public Object value; 16 | public Interpretable body; 17 | 18 | public AstCase(Object value, Interpretable body) { 19 | this.value = value; 20 | this.body = body; 21 | } 22 | } 23 | 24 | public Object expression; 25 | public AstCase[] cases; 26 | public AstCase defaultCase; 27 | 28 | @Override 29 | public void append(AstStringBuilder builder) { 30 | builder.append("switch ("); 31 | builder.appendValue(expression); 32 | builder.append(") {"); 33 | 34 | for (AstCase c : cases) { 35 | builder.append("case "); 36 | builder.appendValue(c.value); 37 | builder.append(':'); 38 | builder.append(c.body); 39 | } 40 | 41 | if (defaultCase != null) { 42 | builder.append("default:"); 43 | builder.append(defaultCase.body); 44 | } 45 | 46 | builder.append('}'); 47 | } 48 | 49 | @Override 50 | public boolean handle(ExitType type) { 51 | return type == ExitType.BREAK; 52 | } 53 | 54 | @Override 55 | public void interpret(Scope scope) { 56 | for (AstCase c : cases) { 57 | if (scope.equals(expression, c.value, true)) { 58 | try { 59 | c.body.interpretSafe(scope); 60 | } catch (BreakExit exit) { 61 | if (exit.stop == this) { 62 | return; 63 | } else { 64 | throw exit; 65 | } 66 | } 67 | } 68 | } 69 | 70 | if (defaultCase != null) { 71 | try { 72 | defaultCase.body.interpretSafe(scope); 73 | } catch (BreakExit ignored) { 74 | } 75 | } 76 | } 77 | 78 | @Override 79 | public void optimize(Parser parser) { 80 | expression = parser.optimize(expression); 81 | 82 | for (var c : cases) { 83 | c.value = parser.optimize(c.value); 84 | c.body.optimize(parser); 85 | } 86 | 87 | if (defaultCase != null) { 88 | defaultCase.value = parser.optimize(defaultCase.value); 89 | defaultCase.body.optimize(parser); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstThisStatement.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | import dev.latvian.apps.ichor.error.ScriptError; 7 | 8 | public class AstThisStatement extends AstStatement { 9 | public static class InvalidCallError extends ScriptError { 10 | public InvalidCallError() { 11 | super("You can only call this() from a constructor"); 12 | } 13 | } 14 | 15 | public final Object[] arguments; 16 | 17 | public AstThisStatement(Object[] a) { 18 | this.arguments = a; 19 | } 20 | 21 | public String getStatementName() { 22 | return "this"; 23 | } 24 | 25 | @Override 26 | public void append(AstStringBuilder builder) { 27 | builder.append(getStatementName()); 28 | builder.append('('); 29 | 30 | for (int i = 0; i < arguments.length; i++) { 31 | if (i > 0) { 32 | builder.append(','); 33 | } 34 | 35 | builder.appendValue(arguments[i]); 36 | } 37 | 38 | builder.append(')'); 39 | } 40 | 41 | @Override 42 | public void interpret(Scope scope) { 43 | throw new InvalidCallError(); 44 | } 45 | 46 | @Override 47 | public void optimize(Parser parser) { 48 | for (int i = 0; i < arguments.length; i++) { 49 | arguments[i] = parser.optimize(arguments[i]); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstThrow.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | import dev.latvian.apps.ichor.error.ScriptThrowError; 7 | 8 | public class AstThrow extends AstStatement { 9 | public Object exception; 10 | 11 | public AstThrow(Object exception) { 12 | this.exception = exception; 13 | } 14 | 15 | @Override 16 | public void append(AstStringBuilder builder) { 17 | builder.append("throw("); 18 | builder.appendValue(exception); 19 | builder.append(')'); 20 | } 21 | 22 | @Override 23 | public void interpret(Scope scope) { 24 | var e = scope.eval(exception); 25 | 26 | if (e instanceof Throwable t) { 27 | throw new ScriptThrowError(t).pos(this); 28 | } else { 29 | throw new ScriptThrowError(String.valueOf(e)).pos(this); 30 | } 31 | } 32 | 33 | @Override 34 | public void optimize(Parser parser) { 35 | exception = parser.optimize(exception); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstTry.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Interpretable; 4 | import dev.latvian.apps.ichor.Parser; 5 | import dev.latvian.apps.ichor.Scope; 6 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | public class AstTry extends AstStatement { 10 | public record AstCatch(String name, Interpretable body) { 11 | } 12 | 13 | public final Interpretable tryBlock; 14 | public final AstCatch catchBlock; 15 | public final Interpretable finallyBlock; 16 | 17 | public AstTry(Interpretable tryBlock, @Nullable AstCatch catchBlock, @Nullable Interpretable finallyBlock) { 18 | this.tryBlock = tryBlock; 19 | this.catchBlock = catchBlock; 20 | this.finallyBlock = finallyBlock; 21 | } 22 | 23 | @Override 24 | public void append(AstStringBuilder builder) { 25 | builder.append("try "); 26 | if (tryBlock != null) { 27 | builder.append(tryBlock); 28 | } else { 29 | builder.append("{}"); 30 | } 31 | 32 | if (catchBlock != null) { 33 | builder.append(" catch ("); 34 | builder.append(catchBlock.name); 35 | builder.append(") "); 36 | 37 | if (catchBlock.body != null) { 38 | builder.append(catchBlock.body); 39 | } else { 40 | builder.append("{}"); 41 | } 42 | } 43 | 44 | if (finallyBlock != null) { 45 | builder.append(" finally "); 46 | builder.append(finallyBlock); 47 | } 48 | } 49 | 50 | @Override 51 | public void interpret(Scope scope) { 52 | try { 53 | if (tryBlock != null) { 54 | tryBlock.interpretSafe(scope); 55 | } 56 | } catch (Exception ex) { 57 | if (catchBlock != null && catchBlock.body != null) { 58 | var s = scope.push(); 59 | s.addMutable(catchBlock.name, ex); 60 | catchBlock.body.interpretSafe(s); 61 | } 62 | } finally { 63 | if (finallyBlock != null) { 64 | finallyBlock.interpretSafe(scope); 65 | } 66 | } 67 | } 68 | 69 | @Override 70 | public void optimize(Parser parser) { 71 | if (tryBlock != null) { 72 | tryBlock.optimize(parser); 73 | } 74 | 75 | if (catchBlock != null && catchBlock.body != null) { 76 | catchBlock.body.optimize(parser); 77 | } 78 | 79 | if (finallyBlock != null) { 80 | finallyBlock.optimize(parser); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstWhile.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Interpretable; 4 | import dev.latvian.apps.ichor.Parser; 5 | import dev.latvian.apps.ichor.Scope; 6 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 7 | import dev.latvian.apps.ichor.exit.BreakExit; 8 | import dev.latvian.apps.ichor.exit.ContinueExit; 9 | 10 | public class AstWhile extends AstLabeledStatement { 11 | public Object condition; 12 | public Interpretable body; 13 | 14 | @Override 15 | public void append(AstStringBuilder builder) { 16 | builder.append("while ("); 17 | builder.appendValue(condition); 18 | builder.append(')'); 19 | 20 | if (body != null) { 21 | builder.append(body); 22 | } else { 23 | builder.append(';'); 24 | } 25 | } 26 | 27 | @Override 28 | public void interpret(Scope scope) { 29 | while (scope.asBoolean(condition)) { 30 | if (body != null) { 31 | try { 32 | body.interpretSafe(scope); 33 | } catch (BreakExit exit) { 34 | if (exit.stop == this) { 35 | break; 36 | } else { 37 | throw exit; 38 | } 39 | } catch (ContinueExit exit) { 40 | if (exit.stop != this) { 41 | throw exit; 42 | } 43 | } 44 | } 45 | } 46 | } 47 | 48 | @Override 49 | public void optimize(Parser parser) { 50 | condition = parser.optimize(condition); 51 | body.optimize(parser); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/AstYield.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Parser; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | import dev.latvian.apps.ichor.error.WIPFeatureError; 7 | 8 | public class AstYield extends AstStatement { 9 | public final boolean generator; 10 | public Object value; 11 | 12 | public AstYield(boolean generator, Object value) { 13 | this.generator = generator; 14 | this.value = value; 15 | } 16 | 17 | @Override 18 | public void append(AstStringBuilder builder) { 19 | builder.append("yield "); 20 | 21 | if (generator) { 22 | builder.append('*'); 23 | } 24 | 25 | builder.appendValue(value); 26 | } 27 | 28 | @Override 29 | public void interpret(Scope scope) { 30 | throw new WIPFeatureError(); 31 | } 32 | 33 | @Override 34 | public void optimize(Parser parser) { 35 | value = parser.optimize(value); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/LabeledStatement.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement; 2 | 3 | import dev.latvian.apps.ichor.Interpretable; 4 | import dev.latvian.apps.ichor.exit.ExitType; 5 | 6 | public interface LabeledStatement extends Interpretable { 7 | default String getLabel() { 8 | return ""; 9 | } 10 | 11 | default boolean handle(ExitType type) { 12 | return type == ExitType.BREAK || type == ExitType.CONTINUE; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/decl/AstDeclaration.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement.decl; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.ast.AppendableAst; 5 | 6 | public interface AstDeclaration extends AppendableAst { 7 | AstDeclaration[] EMPTY = new AstDeclaration[0]; 8 | 9 | void declare(Scope scope, byte flags, Object of); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/decl/DestructuredArray.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement.decl; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Collection; 8 | 9 | public record DestructuredArray(AstDeclaration[] parts, String rest, int restIndex) implements AstDeclaration { 10 | public static final DestructuredArray EMPTY_ARRAY = new DestructuredArray(AstDeclaration.EMPTY, "", 0); 11 | 12 | @Override 13 | public void append(AstStringBuilder builder) { 14 | builder.append('['); 15 | 16 | for (int i = 0; i < parts.length; i++) { 17 | if (i > 0) { 18 | builder.append(','); 19 | } 20 | 21 | parts[i].append(builder); 22 | } 23 | 24 | if (!rest.isEmpty()) { 25 | if (parts.length > 0) { 26 | builder.append(','); 27 | } 28 | 29 | builder.append("..."); 30 | builder.append(rest); 31 | } 32 | 33 | builder.append(']'); 34 | } 35 | 36 | @Override 37 | public void declare(Scope scope, byte flags, Object value) { 38 | var p = scope.getPrototype(value); 39 | 40 | for (var decl : parts) { 41 | decl.declare(scope, flags, value); 42 | } 43 | 44 | if (!rest.isEmpty()) { 45 | int len = value instanceof Collection c ? c.size() : p.getLength(scope, value); 46 | var restArr = new ArrayList<>(); 47 | 48 | for (int i = restIndex; i < len; i++) { 49 | restArr.add(p.getLocal(scope, p.cast(value), i)); 50 | } 51 | 52 | scope.add(rest, restArr, flags); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/decl/DestructuredArrayName.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement.decl; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.Special; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | import dev.latvian.apps.ichor.error.IndexedMemberNotFoundError; 7 | 8 | public record DestructuredArrayName(String name, int index) implements AstDeclaration { 9 | @Override 10 | public void append(AstStringBuilder builder) { 11 | builder.append(name); 12 | } 13 | 14 | @Override 15 | public void declare(Scope scope, byte flags, Object value) { 16 | var p = scope.getPrototype(value); 17 | var v = p.getLocal(scope, p.cast(value), index); 18 | 19 | if (v != Special.NOT_FOUND) { 20 | scope.add(name, v, flags); 21 | } else { 22 | throw new IndexedMemberNotFoundError(index, p, value); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/decl/DestructuredObject.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement.decl; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 5 | 6 | import java.util.LinkedHashMap; 7 | import java.util.Map; 8 | import java.util.Set; 9 | 10 | public record DestructuredObject(AstDeclaration[] parts, String rest, Set ignoredRest) implements AstDeclaration { 11 | public static final DestructuredObject EMPTY_OBJECT = new DestructuredObject(AstDeclaration.EMPTY, "", Set.of()); 12 | 13 | @Override 14 | public void append(AstStringBuilder builder) { 15 | builder.append('{'); 16 | 17 | for (int i = 0; i < parts.length; i++) { 18 | if (i > 0) { 19 | builder.append(','); 20 | } 21 | 22 | parts[i].append(builder); 23 | } 24 | 25 | if (!rest.isEmpty()) { 26 | if (parts.length > 0) { 27 | builder.append(','); 28 | } 29 | 30 | builder.append("..."); 31 | builder.append(rest); 32 | } 33 | 34 | builder.append('}'); 35 | } 36 | 37 | @Override 38 | public void declare(Scope scope, byte flags, Object value) { 39 | var p = scope.getPrototype(value); 40 | 41 | for (var decl : parts) { 42 | decl.declare(scope, flags, value); 43 | } 44 | 45 | if (!rest.isEmpty()) { 46 | // TODO: special case Map for efficiency 47 | var keys = p.keys(scope, p.cast(value)); 48 | 49 | if (keys != null) { 50 | var restObj = new LinkedHashMap(keys.size() - ignoredRest.size()); 51 | 52 | for (var k : keys) { 53 | var ks = k.toString(); 54 | 55 | if (!ignoredRest.contains(ks)) { 56 | restObj.put(ks, p.getInternal(scope, value, ks)); 57 | } 58 | } 59 | 60 | scope.add(rest, restObj, flags); 61 | } else { 62 | scope.add(rest, Map.of(), flags); 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/decl/DestructuredObjectName.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement.decl; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.Special; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | import dev.latvian.apps.ichor.error.NamedMemberNotFoundError; 7 | 8 | public final class DestructuredObjectName implements AstDeclaration { 9 | public final String name; 10 | public String rename; 11 | public Object defaultValue; 12 | 13 | public DestructuredObjectName(String name) { 14 | this.name = name; 15 | this.rename = name; 16 | this.defaultValue = Special.UNDEFINED; 17 | } 18 | 19 | @Override 20 | public void append(AstStringBuilder builder) { 21 | builder.append(name); 22 | 23 | if (!name.equals(rename)) { 24 | builder.append(':'); 25 | builder.append(rename); 26 | } 27 | } 28 | 29 | @Override 30 | public void declare(Scope scope, byte flags, Object value) { 31 | var p = scope.getPrototype(value); 32 | var v = p.getInternal(scope, value, name); 33 | 34 | if (v != Special.NOT_FOUND) { 35 | scope.add(rename, v, flags); 36 | } else if (defaultValue != Special.UNDEFINED) { 37 | scope.add(rename, defaultValue, flags); 38 | } else { 39 | throw new NamedMemberNotFoundError(name, p, value); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/decl/NameDeclaration.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement.decl; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 5 | import dev.latvian.apps.ichor.ast.expression.AstType; 6 | 7 | public class NameDeclaration implements AstDeclaration { 8 | public final String name; 9 | public AstType type; 10 | 11 | public NameDeclaration(String name) { 12 | this.name = name; 13 | this.type = null; 14 | } 15 | 16 | @Override 17 | public String toString() { 18 | return name; 19 | } 20 | 21 | @Override 22 | public void append(AstStringBuilder builder) { 23 | builder.append(name); 24 | 25 | if (type != null) { 26 | builder.append(':'); 27 | builder.append(type); 28 | } 29 | } 30 | 31 | @Override 32 | public void declare(Scope scope, byte flags, Object value) { 33 | if (type != null) { 34 | scope.add(name, type.cast(scope, value), flags); 35 | } else { 36 | scope.add(name, value, flags); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/ast/statement/decl/NestedDestructuredPart.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.ast.statement.decl; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.Special; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | import dev.latvian.apps.ichor.error.NamedMemberNotFoundError; 7 | 8 | public final class NestedDestructuredPart implements AstDeclaration { 9 | private final String name; 10 | private final AstDeclaration part; 11 | 12 | public NestedDestructuredPart(String name, AstDeclaration part) { 13 | this.name = name; 14 | this.part = part; 15 | } 16 | 17 | @Override 18 | public void append(AstStringBuilder builder) { 19 | builder.append(name); 20 | builder.append(':'); 21 | part.append(builder); 22 | } 23 | 24 | @Override 25 | public void declare(Scope scope, byte flags, Object value) { 26 | var p = scope.getPrototype(value); 27 | var o = p.getInternal(scope, value, name); 28 | 29 | if (o != Special.NOT_FOUND) { 30 | part.declare(scope, flags, o); 31 | } else { 32 | throw new NamedMemberNotFoundError(name, p, value); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/error/ArgumentCountMismatchError.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.error; 2 | 3 | public class ArgumentCountMismatchError extends ScriptError { 4 | public final int requiredCount; 5 | public final int actualCount; 6 | 7 | public ArgumentCountMismatchError(int expectedCount, int actualCount) { 8 | super("Invalid number of arguments: Expected " + expectedCount + ", got " + actualCount); 9 | this.requiredCount = expectedCount; 10 | this.actualCount = actualCount; 11 | } 12 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/error/CastError.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.error; 2 | 3 | public class CastError extends ScriptError { 4 | public final Object object; 5 | public final String type; 6 | 7 | public CastError(Object object, String type) { 8 | super("Cannot cast " + object + " (" + object.getClass().getName() + ")" + " to " + type); 9 | this.object = object; 10 | this.type = type; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/error/ConstantReassignError.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.error; 2 | 3 | public class ConstantReassignError extends ScriptError { 4 | public final String name; 5 | 6 | public ConstantReassignError(String name) { 7 | super("Can't reassign constant " + name); 8 | this.name = name; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/error/ConstructorError.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.error; 2 | 3 | import dev.latvian.apps.ichor.prototype.Prototype; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | public class ConstructorError extends ScriptError { 7 | public final Prototype prototype; 8 | 9 | public ConstructorError(@Nullable Prototype prototype) { 10 | super(prototype == null ? "Cannot construct function" : ("Cannot construct " + prototype)); 11 | this.prototype = prototype; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/error/IchorError.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.error; 2 | 3 | import dev.latvian.apps.ichor.token.TokenPos; 4 | import dev.latvian.apps.ichor.token.TokenPosSupplier; 5 | import dev.latvian.apps.ichor.util.PrintWrapper; 6 | 7 | import java.io.PrintStream; 8 | import java.io.PrintWriter; 9 | 10 | public abstract class IchorError extends RuntimeException { 11 | public TokenPos tokenPos = TokenPos.UNKNOWN; 12 | 13 | public IchorError(String message) { 14 | super(message); 15 | } 16 | 17 | public IchorError(String message, Throwable cause) { 18 | super(message, cause); 19 | } 20 | 21 | public IchorError(Throwable cause) { 22 | super(cause); 23 | } 24 | 25 | public IchorError pos(TokenPosSupplier pos) { 26 | tokenPos = pos.getPos(); 27 | return this; 28 | } 29 | 30 | public IchorError pos(Object pos) { 31 | if (pos instanceof TokenPosSupplier supplier) { 32 | tokenPos = supplier.getPos(); 33 | } 34 | 35 | return this; 36 | } 37 | 38 | public String getCode() { 39 | return ""; 40 | } 41 | 42 | @Override 43 | public String getMessage() { 44 | var m = super.getMessage(); 45 | 46 | if (tokenPos != TokenPos.UNKNOWN) { 47 | return m == null || m.isEmpty() ? tokenPos.toString() : (tokenPos + ": " + m); 48 | } 49 | 50 | return m; 51 | } 52 | 53 | public void printPrettyError(PrintWrapper print) { 54 | synchronized (print.lock()) { 55 | var c = getCode(); 56 | 57 | if (!c.isEmpty()) { 58 | print.println("| " + (tokenPos + ": " + getMessage()).trim()); 59 | print.println("| " + c); 60 | print.println("| " + (tokenPos.col() > 2 ? " ".repeat(tokenPos.col() - 2) : "") + '^'); 61 | } 62 | } 63 | } 64 | 65 | @Override 66 | public void printStackTrace(PrintStream s) { 67 | printPrettyError(PrintWrapper.of(s)); 68 | super.printStackTrace(s); 69 | } 70 | 71 | @Override 72 | public void printStackTrace(PrintWriter s) { 73 | printPrettyError(PrintWrapper.of(s)); 74 | super.printStackTrace(s); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/error/IndexedMemberNotFoundError.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.error; 2 | 3 | import dev.latvian.apps.ichor.prototype.Prototype; 4 | 5 | public class IndexedMemberNotFoundError extends ScriptError { 6 | public final int index; 7 | public final Prototype prototype; 8 | public final Object self; 9 | 10 | public IndexedMemberNotFoundError(int index, Prototype prototype, Object self) { 11 | super("Member with index " + index + " of " + self + " of type '" + prototype + "' not found"); 12 | this.index = index; 13 | this.prototype = prototype; 14 | this.self = self; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/error/InternalScriptError.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.error; 2 | 3 | public class InternalScriptError extends ScriptError { 4 | public InternalScriptError(Throwable cause) { 5 | super("Internal error", cause); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/error/NamedMemberNotFoundError.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.error; 2 | 3 | import dev.latvian.apps.ichor.prototype.Prototype; 4 | 5 | public class NamedMemberNotFoundError extends ScriptError { 6 | public final String name; 7 | public final Prototype prototype; 8 | public final Object self; 9 | 10 | public NamedMemberNotFoundError(String name, Prototype prototype, Object self) { 11 | super("Member '" + name + "' of " + self + " of type '" + prototype + "' not found"); 12 | this.name = name; 13 | this.prototype = prototype; 14 | this.self = self; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/error/ParseError.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.error; 2 | 3 | import dev.latvian.apps.ichor.token.TokenPosSupplier; 4 | 5 | public class ParseError extends IchorError { 6 | public ParseError(TokenPosSupplier pos, ParseErrorMessage message) { 7 | super(message.getMessage()); 8 | tokenPos = pos.getPos(); 9 | } 10 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/error/ParseErrorMessage.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.error; 2 | 3 | @FunctionalInterface 4 | public interface ParseErrorMessage { 5 | String getMessage(); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/error/RedeclarationError.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.error; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class RedeclarationError extends ScriptError { 6 | public final String name; 7 | public final Scope scope; 8 | 9 | public RedeclarationError(String name, Scope scope) { 10 | super("Member '" + name + "' already declared"); 11 | this.name = name; 12 | this.scope = scope; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/error/ScopeDepthError.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.error; 2 | 3 | public class ScopeDepthError extends ScriptError { 4 | public final int maxDepth; 5 | 6 | public ScopeDepthError(int maxDepth) { 7 | super("Scope depth is > " + maxDepth); 8 | this.maxDepth = maxDepth; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/error/ScopeMemberNotFoundError.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.error; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public class ScopeMemberNotFoundError extends ScriptError { 6 | public final String name; 7 | public final Scope scope; 8 | 9 | public ScopeMemberNotFoundError(String name, Scope scope) { 10 | super("Member '" + name + "' not found"); 11 | this.name = name; 12 | this.scope = scope; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/error/ScriptError.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.error; 2 | 3 | public abstract class ScriptError extends IchorError { 4 | public ScriptError(String message) { 5 | super(message); 6 | } 7 | 8 | public ScriptError(String message, Throwable cause) { 9 | super(message, cause); 10 | } 11 | 12 | public ScriptError(Throwable cause) { 13 | super(cause); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/error/ScriptThrowError.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.error; 2 | 3 | public class ScriptThrowError extends ScriptError { 4 | public ScriptThrowError(String message) { 5 | super(message); 6 | } 7 | 8 | public ScriptThrowError(Throwable cause) { 9 | super(cause); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/error/ScriptTimedOutError.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.error; 2 | 3 | public class ScriptTimedOutError extends ScriptError { 4 | public ScriptTimedOutError() { 5 | super("Script timed out"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/error/TokenStreamError.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.error; 2 | 3 | import dev.latvian.apps.ichor.token.TokenPos; 4 | 5 | public class TokenStreamError extends IchorError { 6 | public final String code; 7 | 8 | public TokenStreamError(TokenPos tokenPos, String message, String code) { 9 | super(message); 10 | this.tokenPos = tokenPos; 11 | this.code = code; 12 | } 13 | 14 | @Override 15 | public String getCode() { 16 | return code; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/error/WIPFeatureError.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.error; 2 | 3 | public class WIPFeatureError extends ScriptError { 4 | public WIPFeatureError() { 5 | super("This feature hasn't been implemented yet"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/exit/BreakExit.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.exit; 2 | 3 | import dev.latvian.apps.ichor.Interpretable; 4 | 5 | public class BreakExit extends LabelExit { 6 | public BreakExit(Interpretable stop) { 7 | super("break statement is not supported here", stop); 8 | } 9 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/exit/ContinueExit.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.exit; 2 | 3 | import dev.latvian.apps.ichor.Interpretable; 4 | 5 | public class ContinueExit extends LabelExit { 6 | public ContinueExit(Interpretable stop) { 7 | super("continue statement is not supported here", stop); 8 | } 9 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/exit/EndOfFileExit.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.exit; 2 | 3 | public class EndOfFileExit extends RuntimeException { 4 | public EndOfFileExit() { 5 | super("EOF", null, false, false); 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/exit/ExitType.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.exit; 2 | 3 | public enum ExitType { 4 | RETURN("return"), 5 | BREAK("break"), 6 | CONTINUE("continue"); 7 | 8 | public final String name; 9 | 10 | ExitType(String n) { 11 | name = n; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/exit/LabelExit.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.exit; 2 | 3 | import dev.latvian.apps.ichor.Interpretable; 4 | 5 | public class LabelExit extends ScopeExit { 6 | public final Interpretable stop; 7 | 8 | public LabelExit(String msg, Interpretable stop) { 9 | super(msg, stop); 10 | this.stop = stop; 11 | } 12 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/exit/ReturnExit.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.exit; 2 | 3 | import dev.latvian.apps.ichor.Special; 4 | 5 | public class ReturnExit extends ScopeExit { 6 | public static final ReturnExit DEFAULT_RETURN = new ReturnExit(Special.UNDEFINED); 7 | 8 | public ReturnExit(Object value) { 9 | super("return statement is not supported here", value); 10 | } 11 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/exit/ScopeExit.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.exit; 2 | 3 | public class ScopeExit extends RuntimeException { 4 | public final Object value; 5 | 6 | public ScopeExit(String msg, Object v) { 7 | super(msg, null, false, false); 8 | value = v; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/java/AnnotatedElementPrototype.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.java; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.prototype.Prototype; 5 | import dev.latvian.apps.ichor.util.Functions; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import java.lang.reflect.AnnotatedElement; 9 | 10 | @SuppressWarnings("unchecked") 11 | public class AnnotatedElementPrototype extends Prototype { 12 | private static final Functions.Bound GET_ANNOTATION = (scope, cl, args) -> cl.getAnnotation(scope.asClass(args[0])); 13 | private static final Functions.Bound GET_DECLARED_ANNOTATION = (scope, cl, args) -> cl.getDeclaredAnnotation(scope.asClass(args[0])); 14 | private static final Functions.Bound GET_ANNOTATIONS_BY_TYPE = (scope, cl, args) -> cl.getAnnotationsByType(scope.asClass(args[0])); 15 | private static final Functions.Bound GET_DECLARED_ANNOTATIONS_BY_TYPE = (scope, cl, args) -> cl.getDeclaredAnnotationsByType(scope.asClass(args[0])); 16 | 17 | public AnnotatedElementPrototype(Scope cx) { 18 | super(cx, AnnotatedElement.class); 19 | } 20 | 21 | @Override 22 | @Nullable 23 | public Object getLocal(Scope scope, AnnotatedElement self, String name) { 24 | return switch (name) { 25 | case "annotations" -> self.getAnnotations(); 26 | case "declaredAnnotations" -> self.getDeclaredAnnotations(); 27 | case "getAnnotation" -> GET_ANNOTATION.with(self); 28 | case "getDeclaredAnnotation" -> GET_DECLARED_ANNOTATION.with(self); 29 | case "getAnnotationsByType" -> GET_ANNOTATIONS_BY_TYPE.with(self); 30 | case "getDeclaredAnnotationsByType" -> GET_DECLARED_ANNOTATIONS_BY_TYPE.with(self); 31 | default -> super.getLocal(scope, self, name); 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/java/BooleanPrototype.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.java; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.prototype.Prototype; 5 | import dev.latvian.apps.ichor.util.IchorUtils; 6 | 7 | public class BooleanPrototype extends Prototype { 8 | public BooleanPrototype(Scope cx) { 9 | super(cx, Boolean.class); 10 | } 11 | 12 | @Override 13 | public Object call(Scope scope, Object[] args, boolean hasNew) { 14 | return args.length == 0 ? Boolean.FALSE : scope.asBoolean(args[0]); 15 | } 16 | 17 | @Override 18 | public Number asNumber(Scope scope, Boolean self) { 19 | return self ? IchorUtils.ONE : IchorUtils.ZERO; 20 | } 21 | 22 | @Override 23 | public Boolean asBoolean(Scope scope, Boolean self) { 24 | return self; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/java/JavaClassPrototype.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.java; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.prototype.Prototype; 5 | import dev.latvian.apps.ichor.util.Functions; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import java.lang.reflect.AnnotatedElement; 9 | 10 | public class JavaClassPrototype extends Prototype> { 11 | private static final Functions.Bound> IS_INSTANCE = (scope, cl, args) -> cl.isInstance(args[0]); 12 | private static final Functions.Bound> IS_ASSIGNABLE_FROM = (scope, cl, args) -> cl.isAssignableFrom(scope.asClass(args[0])); 13 | 14 | public JavaClassPrototype(Scope cx) { 15 | super(cx, "JavaClass", Class.class); 16 | } 17 | 18 | @Override 19 | protected void initMembers() { 20 | } 21 | 22 | @Override 23 | protected void initParents() { 24 | parent(initialScope.getClassPrototype(AnnotatedElement.class)); 25 | } 26 | 27 | @Override 28 | @Nullable 29 | public Object getLocal(Scope scope, Class self, String name) { 30 | return switch (name) { 31 | case "name" -> self.getName(); 32 | case "superclass" -> self.getSuperclass(); 33 | case "interface" -> self.isInterface(); 34 | case "array" -> self.isArray(); 35 | case "enum" -> self.isEnum(); 36 | case "primitive" -> self.isPrimitive(); 37 | case "annotation" -> self.isAnnotation(); 38 | case "synthetic" -> self.isSynthetic(); 39 | case "package" -> self.getPackageName(); 40 | case "descriptor" -> self.descriptorString(); 41 | case "simpleName" -> self.getSimpleName(); 42 | case "componentType" -> self.getComponentType(); 43 | case "canonicalName" -> self.getCanonicalName(); 44 | case "typeName" -> self.getTypeName(); 45 | case "interfaces" -> self.getInterfaces(); 46 | case "modifiers" -> self.getModifiers(); 47 | case "declaredClasses" -> self.getDeclaredClasses(); 48 | case "declaringClass" -> self.getDeclaringClass(); 49 | case "enclosingClass" -> self.getEnclosingClass(); 50 | case "isInstance" -> IS_INSTANCE.with(self); 51 | case "isAssignableFrom" -> IS_ASSIGNABLE_FROM.with(self); 52 | default -> super.getLocal(scope, self, name); 53 | }; 54 | } 55 | 56 | @Override 57 | public boolean asString(Scope scope, Class self, StringBuilder builder, boolean escape) { 58 | builder.append(self.getName()); 59 | return true; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/java/LocalJavaMembers.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.java; 2 | 3 | import dev.latvian.apps.ichor.CallableTypeAdapter; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.Special; 6 | import dev.latvian.apps.ichor.error.InternalScriptError; 7 | import dev.latvian.apps.ichor.prototype.PrototypeProperty; 8 | import dev.latvian.apps.ichor.util.Empty; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | public record LocalJavaMembers(JavaMembers members) implements PrototypeProperty { 12 | public record CallWrapper(Scope evalScope, Object self, JavaMembers members) implements CallableTypeAdapter { 13 | @Override 14 | public Object call(Scope scope, Object[] args, boolean hasNew) { 15 | return members.call(scope, args, self); 16 | } 17 | 18 | @Override 19 | public Scope getEvalScope() { 20 | return evalScope; 21 | } 22 | 23 | @Override 24 | public String toString() { 25 | return "Proxy[" + self + "]"; 26 | } 27 | 28 | @Override 29 | public int hashCode() { 30 | return self.hashCode(); 31 | } 32 | } 33 | 34 | @Override 35 | public Object get(Scope scope, Object self) { 36 | try { 37 | if (members.beanGet != null) { 38 | return members.beanGet.invoke(self, Empty.OBJECTS); 39 | } 40 | 41 | if (members.field != null) { 42 | return members.field.get(self); 43 | } 44 | } catch (Exception ex) { 45 | throw new InternalScriptError(ex); 46 | } 47 | 48 | if (members.methods != null) { 49 | return new CallWrapper(scope, self, members); 50 | } 51 | 52 | return Special.NOT_FOUND; 53 | } 54 | 55 | @Override 56 | public boolean set(Scope scope, Object self, @Nullable Object value) { 57 | try { 58 | if (members.beanSet != null) { 59 | members.beanSet.invoke(self, scope.as(value, members.beanSetType)); 60 | return true; 61 | } 62 | 63 | if (members.field != null) { 64 | members.field.set(self, scope.as(value, members.field.getType())); 65 | return true; 66 | } 67 | } catch (Exception ex) { 68 | throw new InternalScriptError(ex); 69 | } 70 | 71 | return false; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/java/StaticJavaMembers.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.java; 2 | 3 | import dev.latvian.apps.ichor.CallableTypeAdapter; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.Special; 6 | import dev.latvian.apps.ichor.error.InternalScriptError; 7 | import dev.latvian.apps.ichor.prototype.PrototypeStaticProperty; 8 | import dev.latvian.apps.ichor.util.Empty; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | public record StaticJavaMembers(JavaMembers members) implements PrototypeStaticProperty { 12 | public record CallWrapper(Scope evalScope, JavaMembers members) implements CallableTypeAdapter { 13 | @Override 14 | public Object call(Scope scope, Object[] args, boolean hasNew) { 15 | return members.call(scope, args, null); 16 | } 17 | 18 | @Override 19 | public Scope getEvalScope() { 20 | return evalScope; 21 | } 22 | 23 | @Override 24 | public String toString() { 25 | return "StaticProxy[" + members.prototype + "]"; 26 | } 27 | 28 | @Override 29 | public int hashCode() { 30 | return members.prototype.hashCode(); 31 | } 32 | } 33 | 34 | @Override 35 | public Object get(Scope scope) { 36 | try { 37 | if (members.beanGet != null) { 38 | return members.beanGet.invoke(null, Empty.OBJECTS); 39 | } 40 | 41 | if (members.field != null) { 42 | return members.field.get(null); 43 | } 44 | } catch (Exception ex) { 45 | throw new InternalScriptError(ex); 46 | } 47 | 48 | if (members.methods != null) { 49 | return new CallWrapper(scope, members); 50 | } 51 | 52 | return Special.NOT_FOUND; 53 | } 54 | 55 | @Override 56 | public boolean set(Scope scope, @Nullable Object value) { 57 | try { 58 | if (members.beanSet != null) { 59 | members.beanSet.invoke(null, scope.as(value, members.beanSetType)); 60 | return true; 61 | } 62 | 63 | if (members.field != null) { 64 | members.field.set(null, scope.as(value, members.field.getType())); 65 | return true; 66 | } 67 | } catch (Exception ex) { 68 | throw new InternalScriptError(ex); 69 | } 70 | 71 | return false; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/prototype/PrototypeConstant.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.prototype; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | public record PrototypeConstant(Object value) implements PrototypeStaticProperty { 6 | @Override 7 | public Object get(Scope scope) { 8 | return value; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/prototype/PrototypeConstructor.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.prototype; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | @FunctionalInterface 6 | public interface PrototypeConstructor { 7 | Object construct(Scope scope, Object[] args, boolean hasNew); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/prototype/PrototypeFunction.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.prototype; 2 | 3 | import dev.latvian.apps.ichor.Callable; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.error.ConstructorError; 6 | 7 | @FunctionalInterface 8 | public interface PrototypeFunction extends PrototypeProperty { 9 | record CallWrapper(Object self, Scope evalScope, PrototypeFunction function) implements Callable { 10 | @Override 11 | public Object call(Scope callScope, Object[] args, boolean hasNew) { 12 | if (hasNew) { 13 | throw new ConstructorError(null); 14 | } 15 | 16 | return function.call(evalScope, self, args); 17 | } 18 | } 19 | 20 | Object call(Scope scope, Object self, Object[] args); 21 | 22 | @Override 23 | default Object get(Scope scope, Object self) { 24 | return new CallWrapper(self, scope, this); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/prototype/PrototypeProperty.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.prototype; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | @FunctionalInterface 7 | public interface PrototypeProperty { 8 | Object get(Scope scope, Object self); 9 | 10 | default boolean set(Scope scope, Object self, @Nullable Object value) { 11 | return false; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/prototype/PrototypeStaticFunction.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.prototype; 2 | 3 | import dev.latvian.apps.ichor.Callable; 4 | import dev.latvian.apps.ichor.Scope; 5 | 6 | @FunctionalInterface 7 | public interface PrototypeStaticFunction extends PrototypeStaticProperty, Callable { 8 | Object call(Scope scope, Object[] args); 9 | 10 | @Override 11 | default Object call(Scope scope, Object[] args, boolean hasNew) { 12 | return call(scope, args); 13 | } 14 | 15 | @Override 16 | default Object get(Scope scope) { 17 | return this; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/prototype/PrototypeStaticProperty.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.prototype; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | @FunctionalInterface 7 | public interface PrototypeStaticProperty { 8 | Object get(Scope scope); 9 | 10 | default boolean set(Scope scope, @Nullable Object value) { 11 | return false; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/prototype/PrototypeSupplier.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.prototype; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | @FunctionalInterface 6 | public interface PrototypeSupplier { 7 | Prototype getPrototype(Scope scope); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/prototype/PrototypeTypeAdapter.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.prototype; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.Special; 5 | 6 | @FunctionalInterface 7 | public interface PrototypeTypeAdapter { 8 | Object adapt(Scope scope, T self, Class toType); 9 | 10 | record Fallback(PrototypeTypeAdapter main, PrototypeTypeAdapter fallback) implements PrototypeTypeAdapter { 11 | @Override 12 | public Object adapt(Scope scope, T self, Class toType) { 13 | var r = main.adapt(scope, self, toType); 14 | return r != Special.NOT_FOUND ? r : fallback.adapt(scope, self, toType); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/slot/EmptySlotMap.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.slot; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | import java.util.Set; 6 | 7 | public class EmptySlotMap implements SlotMap { 8 | public static final EmptySlotMap INSTANCE = new EmptySlotMap(); 9 | 10 | @Override 11 | @Nullable 12 | public Slot getSlot(String name) { 13 | return null; 14 | } 15 | 16 | @Override 17 | public void setSlot(Slot slot) { 18 | } 19 | 20 | @Override 21 | public void removeSlot(String name) { 22 | } 23 | 24 | @Override 25 | public Set getSlotNames() { 26 | return Set.of(); 27 | } 28 | 29 | @Override 30 | public SlotMap upgradeSlotMap() { 31 | return new SlotArrayMap(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/slot/Slot.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.slot; 2 | 3 | import dev.latvian.apps.ichor.Special; 4 | 5 | public class Slot { 6 | public static final byte DEFAULT = 0; 7 | public static final byte IMMUTABLE = 1; 8 | public static final byte ROOT = 2; 9 | 10 | public final String name; 11 | public Object value; 12 | public byte flags; 13 | 14 | public Slot(String name) { 15 | this.name = name; 16 | this.value = Special.UNDEFINED; 17 | this.flags = 0; 18 | } 19 | 20 | @Override 21 | public String toString() { 22 | return "[Slot " + name + " = " + value + "]"; 23 | } 24 | 25 | public boolean isImmutable() { 26 | return (flags & IMMUTABLE) != 0; 27 | } 28 | 29 | public boolean isRoot() { 30 | return (flags & ROOT) != 0; 31 | } 32 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/slot/SlotArrayMap.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.slot; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | import java.util.ArrayList; 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | 9 | public class SlotArrayMap extends ArrayList implements SlotMap { 10 | public SlotArrayMap() { 11 | super(4); 12 | } 13 | 14 | @Override 15 | @Nullable 16 | public Slot getSlot(String name) { 17 | for (var slot : this) { 18 | if (slot.name.equals(name)) { 19 | return slot; 20 | } 21 | } 22 | 23 | return null; 24 | } 25 | 26 | @Override 27 | public void setSlot(Slot slot) { 28 | for (int i = 0; i < size(); i++) { 29 | if (get(i).name.equals(slot.name)) { 30 | set(i, slot); 31 | return; 32 | } 33 | } 34 | 35 | add(slot); 36 | } 37 | 38 | @Override 39 | public void removeSlot(String name) { 40 | var itr = iterator(); 41 | 42 | while (itr.hasNext()) { 43 | if (itr.next().name.equals(name)) { 44 | itr.remove(); 45 | return; 46 | } 47 | } 48 | } 49 | 50 | @Override 51 | public Set getSlotNames() { 52 | var set = new HashSet(size()); 53 | 54 | for (var slot : this) { 55 | set.add(slot.name); 56 | } 57 | 58 | return set; 59 | } 60 | 61 | @Override 62 | public SlotMap upgradeSlotMap() { 63 | if (size() == 32) { 64 | var map = new SlotHashMap(); 65 | 66 | for (var slot : this) { 67 | map.setSlot(slot); 68 | } 69 | 70 | return map; 71 | } 72 | 73 | return this; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/slot/SlotHashMap.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.slot; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | import java.util.HashMap; 6 | import java.util.Set; 7 | 8 | public class SlotHashMap extends HashMap implements SlotMap { 9 | public SlotHashMap() { 10 | super(17); 11 | } 12 | 13 | @Override 14 | @Nullable 15 | public Slot getSlot(String name) { 16 | return get(name); 17 | } 18 | 19 | @Override 20 | public void setSlot(Slot slot) { 21 | put(slot.name, slot); 22 | } 23 | 24 | @Override 25 | public void removeSlot(String name) { 26 | remove(name); 27 | } 28 | 29 | @Override 30 | public Set getSlotNames() { 31 | return keySet(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/slot/SlotMap.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.slot; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | import java.util.Set; 6 | 7 | public interface SlotMap { 8 | @Nullable 9 | Slot getSlot(String name); 10 | 11 | void setSlot(Slot slot); 12 | 13 | void removeSlot(String name); 14 | 15 | Set getSlotNames(); 16 | 17 | default SlotMap upgradeSlotMap() { 18 | return this; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/token/DeclaringToken.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.token; 2 | 3 | public class DeclaringToken extends KeywordToken { 4 | public final byte flags; 5 | 6 | public DeclaringToken(String name, byte flags) { 7 | super(name); 8 | this.flags = flags; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/token/IdentifierToken.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.token; 2 | 3 | import java.util.Objects; 4 | 5 | public class IdentifierToken implements Token { 6 | public final String name; 7 | 8 | public IdentifierToken(String name) { 9 | this.name = name; 10 | } 11 | 12 | @Override 13 | public String toString() { 14 | return name; 15 | } 16 | 17 | @Override 18 | public boolean equals(Object obj) { 19 | return name.equals(String.valueOf(obj)); 20 | } 21 | 22 | @Override 23 | public int hashCode() { 24 | return Objects.hash(name); 25 | } 26 | 27 | public boolean isIdentifier() { 28 | return true; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/token/InKeywordToken.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.token; 2 | 3 | import dev.latvian.apps.ichor.ast.expression.binary.AstBinary; 4 | import dev.latvian.apps.ichor.ast.expression.binary.AstIn; 5 | 6 | public class InKeywordToken extends KeywordToken { 7 | public InKeywordToken() { 8 | super("in"); 9 | identifier(); 10 | } 11 | 12 | @Override 13 | public AstBinary createBinaryAst(PositionedToken pos) { 14 | return new AstIn(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/token/InstanceofKeywordToken.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.token; 2 | 3 | import dev.latvian.apps.ichor.ast.expression.binary.AstBinary; 4 | import dev.latvian.apps.ichor.ast.expression.binary.AstInstanceOf; 5 | 6 | public class InstanceofKeywordToken extends KeywordToken { 7 | public InstanceofKeywordToken() { 8 | super("instanceof"); 9 | } 10 | 11 | @Override 12 | public AstBinary createBinaryAst(PositionedToken pos) { 13 | return new AstInstanceOf(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/token/KeywordToken.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.token; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | public class KeywordToken extends IdentifierToken { 6 | private boolean isIdentifier; 7 | private boolean isLiteralPre; 8 | private Token insertToken; 9 | 10 | public KeywordToken(String name) { 11 | super(name); 12 | isIdentifier = false; 13 | isLiteralPre = false; 14 | } 15 | 16 | @Override 17 | public boolean isIdentifier() { 18 | return isIdentifier; 19 | } 20 | 21 | public KeywordToken identifier() { 22 | isIdentifier = true; 23 | return this; 24 | } 25 | 26 | @Override 27 | public boolean isLiteralPre() { 28 | return isLiteralPre; 29 | } 30 | 31 | public KeywordToken insertToken(Token token) { 32 | insertToken = token; 33 | return this; 34 | } 35 | 36 | @Override 37 | @Nullable 38 | public Token getTokenBeforeNewline() { 39 | return insertToken; 40 | } 41 | 42 | public KeywordToken literalPre() { 43 | isLiteralPre = true; 44 | return this; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/token/PositionedToken.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.token; 2 | 3 | import dev.latvian.apps.ichor.Special; 4 | import dev.latvian.apps.ichor.error.ParseError; 5 | import dev.latvian.apps.ichor.error.ParseErrorMessage; 6 | 7 | public class PositionedToken implements TokenPosSupplier { 8 | public static final PositionedToken NONE = new PositionedToken(Special.NOT_FOUND, TokenPos.UNKNOWN); 9 | 10 | static { 11 | NONE.prev = NONE; 12 | NONE.next = NONE; 13 | } 14 | 15 | public final Object token; 16 | public final TokenPos pos; 17 | public PositionedToken prev; 18 | public PositionedToken next; 19 | 20 | public PositionedToken(Object t, TokenPos p) { 21 | token = t; 22 | pos = p; 23 | } 24 | 25 | public boolean isIdentifier() { 26 | return token instanceof IdentifierToken t && t.isIdentifier(); 27 | } 28 | 29 | public String identifier(ParseErrorMessage error) { 30 | if (token instanceof IdentifierToken t && t.isIdentifier()) { 31 | return t.name; 32 | } else { 33 | throw new ParseError(pos, error); 34 | } 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return token + " @ " + pos; 40 | } 41 | 42 | @Override 43 | public TokenPos getPos() { 44 | return pos; 45 | } 46 | 47 | public boolean exists() { 48 | return token != Special.NOT_FOUND; 49 | } 50 | 51 | public boolean is(Token t) { 52 | return token == t; 53 | } 54 | 55 | public boolean is(Token[] tokens) { 56 | for (var t : tokens) { 57 | if (token == t) { 58 | return true; 59 | } 60 | } 61 | 62 | return false; 63 | } 64 | 65 | public String toRecursiveString() { 66 | StringBuilder builder = new StringBuilder(); 67 | var t = this; 68 | 69 | while (t.exists()) { 70 | builder.append(t); 71 | t = t.next; 72 | } 73 | 74 | return builder.toString(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/token/Token.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.token; 2 | 3 | import dev.latvian.apps.ichor.Evaluable; 4 | import dev.latvian.apps.ichor.Parser; 5 | import dev.latvian.apps.ichor.ast.expression.binary.AstBinary; 6 | import dev.latvian.apps.ichor.ast.expression.unary.AstUnary; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | public interface Token { 10 | default boolean isLiteralPre() { 11 | return false; 12 | } 13 | 14 | @Nullable 15 | default Token getTokenBeforeNewline() { 16 | return null; 17 | } 18 | 19 | @Nullable 20 | default Evaluable toEvaluable(Parser parser, TokenPos pos) { 21 | return null; 22 | } 23 | 24 | @Nullable 25 | default AstUnary createUnaryAst(PositionedToken pos) { 26 | return null; 27 | } 28 | 29 | @Nullable 30 | default AstBinary createBinaryAst(PositionedToken pos) { 31 | return null; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/token/TokenPos.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.token; 2 | 3 | public record TokenPos(TokenSource source, int row, int col) implements TokenPosSupplier { 4 | public static final TokenPos UNKNOWN = new TokenPos(null, 0, 0); 5 | 6 | @Override 7 | public String toString() { 8 | if (this == UNKNOWN) { 9 | return "?:?"; 10 | } else { 11 | var s = source == null ? null : source.getSourceName(); 12 | 13 | if (s == null || s.isEmpty()) { 14 | return row + ":" + col; 15 | } else { 16 | return s + ":" + row + ":" + col; 17 | } 18 | } 19 | } 20 | 21 | @Override 22 | public TokenPos getPos() { 23 | return this; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/token/TokenPosSupplier.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.token; 2 | 3 | public interface TokenPosSupplier { 4 | TokenPos getPos(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/token/TokenSource.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.token; 2 | 3 | public interface TokenSource { 4 | String getSourceName(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/type/ArrayJS.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.type; 2 | 3 | import dev.latvian.apps.ichor.Callable; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.prototype.Prototype; 6 | import dev.latvian.apps.ichor.util.Functions; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | 12 | public class ArrayJS extends Prototype { 13 | public static final Callable FROM = Functions.WIP; 14 | public static final Callable IS_ARRAY = Functions.WIP; 15 | public static final Callable OF = Functions.WIP; 16 | 17 | public ArrayJS(Scope cx) { 18 | super(cx, "Array", ArrayJS.class); 19 | } 20 | 21 | @Override 22 | public Object call(Scope scope, Object[] args, boolean hasNew) { 23 | return args.length == 0 ? new ArrayList<>() : new ArrayList<>(Arrays.asList(args)); 24 | } 25 | 26 | @Override 27 | @Nullable 28 | public Object getStatic(Scope scope, String name) { 29 | return switch (name) { 30 | case "from" -> FROM; 31 | case "isArray" -> IS_ARRAY; 32 | case "of" -> OF; 33 | default -> super.getStatic(scope, name); 34 | }; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/type/CollectionJS.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.type; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.prototype.Prototype; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import java.util.Arrays; 8 | import java.util.Collection; 9 | import java.util.List; 10 | 11 | @SuppressWarnings({"rawtypes"}) 12 | public class CollectionJS extends Prototype { 13 | public CollectionJS(Scope cx) { 14 | super(cx, Collection.class); 15 | } 16 | 17 | @Override 18 | @Nullable 19 | public Object getLocal(Scope scope, Collection self, String name) { 20 | return switch (name) { 21 | case "length" -> self.size(); 22 | case "forEach" -> IterableJS.FOR_EACH.with(self); 23 | default -> super.getLocal(scope, self, name); 24 | }; 25 | } 26 | 27 | @Override 28 | public int getLength(Scope scope, Object self) { 29 | return ((Collection) self).size(); 30 | } 31 | 32 | @Override 33 | public Collection keys(Scope scope, Collection self) { 34 | var keys = new Object[self.size()]; 35 | 36 | for (int i = 0; i < self.size(); i++) { 37 | keys[i] = i; 38 | } 39 | 40 | return Arrays.asList(keys); 41 | } 42 | 43 | @Override 44 | public Collection values(Scope scope, Collection self) { 45 | return self; 46 | } 47 | 48 | @Override 49 | public Collection entries(Scope scope, Collection self) { 50 | var entries = new Object[self.size()]; 51 | 52 | int i = 0; 53 | 54 | for (var o : self) { 55 | entries[i] = List.of(i, o); 56 | i++; 57 | } 58 | 59 | return Arrays.asList(entries); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/type/IterableJS.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.type; 2 | 3 | import dev.latvian.apps.ichor.Callable; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.prototype.Prototype; 6 | import dev.latvian.apps.ichor.util.Functions; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Collection; 11 | import java.util.List; 12 | 13 | @SuppressWarnings({"rawtypes"}) 14 | public class IterableJS extends Prototype { 15 | public static final Functions.Bound FOR_EACH = (scope, self, args) -> { 16 | var func = (Callable) args[0]; 17 | int i = 0; 18 | 19 | for (var o : self) { 20 | func.call(scope, new Object[]{o, i, self}, false); 21 | i++; 22 | } 23 | 24 | return self; 25 | }; 26 | 27 | public IterableJS(Scope cx) { 28 | super(cx, Iterable.class); 29 | } 30 | 31 | @Override 32 | public boolean asString(Scope scope, Iterable self, StringBuilder builder, boolean escape) { 33 | builder.append('['); 34 | 35 | boolean first = true; 36 | 37 | for (var o : self) { 38 | if (first) { 39 | first = false; 40 | } else { 41 | builder.append(','); 42 | builder.append(' '); 43 | } 44 | 45 | scope.asString(o, builder, true); 46 | } 47 | 48 | builder.append(']'); 49 | return true; 50 | } 51 | 52 | @Override 53 | @Nullable 54 | public Object getLocal(Scope scope, Iterable self, String name) { 55 | return switch (name) { 56 | case "length" -> { 57 | int i = 0; 58 | for (var ignored : self) { 59 | i++; 60 | } 61 | yield i; 62 | } 63 | case "forEach" -> FOR_EACH.with(self); 64 | default -> super.getLocal(scope, self, name); 65 | }; 66 | } 67 | 68 | @Override 69 | public Collection keys(Scope scope, Iterable self) { 70 | var keys = new ArrayList(); 71 | 72 | int i = 0; 73 | 74 | for (var ignored : self) { 75 | keys.add(i++); 76 | } 77 | 78 | return keys; 79 | } 80 | 81 | @Override 82 | public Collection values(Scope scope, Iterable self) { 83 | var values = new ArrayList<>(); 84 | 85 | for (var o : self) { 86 | values.add(o); 87 | } 88 | 89 | return values; 90 | } 91 | 92 | @Override 93 | public Collection entries(Scope scope, Iterable self) { 94 | var entries = new ArrayList>(); 95 | 96 | int i = 0; 97 | 98 | for (var o : self) { 99 | entries.add(List.of(i++, o)); 100 | } 101 | 102 | return entries; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/type/ListJS.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.type; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.prototype.Prototype; 5 | import dev.latvian.apps.ichor.util.Functions; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import java.util.List; 9 | 10 | @SuppressWarnings({"rawtypes", "unchecked"}) 11 | public class ListJS extends Prototype { 12 | public static final Functions.Bound PUSH = (scope, self, args) -> { 13 | int len = self.size(); 14 | 15 | for (int i = 0; i < args.length; i++) { 16 | self.add(len + i, args[i]); 17 | } 18 | 19 | return self; 20 | }; 21 | 22 | public static final Functions.Bound POP = (scope, self, args) -> self.remove(self.size() - 1); 23 | public static final Functions.Bound SHIFT = (scope, self, args) -> self.remove(0); 24 | 25 | public static final Functions.Bound UNSHIFT = (scope, self, args) -> { 26 | for (int i = 0; i < args.length; i++) { 27 | self.add(i, args[i]); 28 | } 29 | 30 | return self; 31 | }; 32 | 33 | public ListJS(Scope cx) { 34 | super(cx, List.class); 35 | } 36 | 37 | @Override 38 | @Nullable 39 | public Object getLocal(Scope scope, List self, String name) { 40 | return switch (name) { 41 | case "length" -> self.size(); 42 | case "push" -> PUSH.with(self); 43 | case "pop" -> POP.with(self); 44 | case "shift" -> SHIFT.with(self); 45 | case "unshift" -> UNSHIFT.with(self); 46 | case "forEach" -> IterableJS.FOR_EACH.with(self); 47 | default -> super.getStatic(scope, name); 48 | }; 49 | } 50 | 51 | @Override 52 | public int getLength(Scope scope, Object self) { 53 | return ((List) self).size(); 54 | } 55 | 56 | @Override 57 | @Nullable 58 | public Object getLocal(Scope scope, List self, int index) { 59 | return self.get(index); 60 | } 61 | 62 | @Override 63 | public boolean setLocal(Scope scope, List self, int index, @Nullable Object value) { 64 | self.set(index, cast(value)); 65 | return true; 66 | } 67 | 68 | @Override 69 | public boolean deleteLocal(Scope scope, List self, int index) { 70 | self.remove(index); 71 | return true; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/type/MapJS.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.type; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.Special; 5 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 6 | import dev.latvian.apps.ichor.prototype.Prototype; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.util.Arrays; 10 | import java.util.Collection; 11 | import java.util.LinkedHashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | public class MapJS extends Prototype> { 16 | public MapJS(Scope cx) { 17 | super(cx, "Map", Map.class); 18 | } 19 | 20 | @Override 21 | public Object call(Scope scope, Object[] args, boolean hasNew) { 22 | return new LinkedHashMap<>(); 23 | } 24 | 25 | @Override 26 | public boolean asString(Scope scope, Map self, StringBuilder builder, boolean escape) { 27 | builder.append('{'); 28 | 29 | boolean first = true; 30 | 31 | for (var entry : self.entrySet()) { 32 | if (first) { 33 | first = false; 34 | } else { 35 | builder.append(','); 36 | builder.append(' '); 37 | } 38 | 39 | AstStringBuilder.wrapKey(String.valueOf(entry.getKey()), builder); 40 | builder.append(':'); 41 | builder.append(' '); 42 | scope.asString(entry.getValue(), builder, true); 43 | } 44 | 45 | builder.append('}'); 46 | return true; 47 | } 48 | 49 | @Override 50 | @Nullable 51 | public Object getLocal(Scope scope, Map self, String name) { 52 | var o = self.get(name); 53 | 54 | if (o != null) { 55 | return o == Special.NULL ? null : o; 56 | } 57 | 58 | return super.getLocal(scope, self, name); 59 | } 60 | 61 | @Override 62 | public int getLength(Scope scope, Object self) { 63 | return ((Map) self).size(); 64 | } 65 | 66 | @Override 67 | public boolean setLocal(Scope scope, Map self, String name, @Nullable Object value) { 68 | self.put(cast(name), cast(value == null ? Special.NULL : value)); // FIXME: Casting 69 | return true; 70 | } 71 | 72 | @Override 73 | public boolean deleteLocal(Scope scope, Map self, String name) { 74 | return self.remove(name) != null; 75 | } 76 | 77 | @Override 78 | public Collection keys(Scope scope, Map self) { 79 | return self.keySet(); 80 | } 81 | 82 | @Override 83 | public Collection values(Scope scope, Map self) { 84 | return self.values(); 85 | } 86 | 87 | @Override 88 | public Collection entries(Scope scope, Map self) { 89 | if (self.isEmpty()) { 90 | return List.of(); 91 | } else if (self.size() == 1) { 92 | var entry = self.entrySet().iterator().next(); 93 | return List.of(List.of(entry.getKey(), entry.getValue())); 94 | } else { 95 | var entries = new Object[self.size()]; 96 | int i = 0; 97 | 98 | for (var entry : self.entrySet()) { 99 | entries[i] = List.of(entry.getKey(), entry.getValue()); 100 | i++; 101 | } 102 | 103 | return Arrays.asList(entries); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/type/MathJS.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.type; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.prototype.Prototype; 5 | import dev.latvian.apps.ichor.util.IchorUtils; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | public class MathJS extends Prototype { 9 | public MathJS(Scope cx) { 10 | super(cx, "Math", MathJS.class); 11 | } 12 | 13 | @Override 14 | @Nullable 15 | public Object getStatic(Scope scope, String name) { 16 | return switch (name) { 17 | case "PI" -> IchorUtils.PI; 18 | case "E" -> IchorUtils.E; 19 | case "LN10" -> IchorUtils.LN10; 20 | case "LN2" -> IchorUtils.LN2; 21 | case "LOG2E" -> IchorUtils.LOG2E; 22 | case "LOG10E" -> IchorUtils.LOG10E; 23 | case "SQRT1_2" -> IchorUtils.SQRT1_2; 24 | case "SQRT2" -> IchorUtils.SQRT2; 25 | case "abs" -> IchorUtils.ABS; 26 | case "acos" -> IchorUtils.ACOS; 27 | case "asin" -> IchorUtils.ASIN; 28 | case "atan" -> IchorUtils.ATAN; 29 | case "atan2" -> IchorUtils.ATAN2; 30 | case "ceil" -> IchorUtils.CEIL; 31 | case "cos" -> IchorUtils.COS; 32 | case "exp" -> IchorUtils.EXP; 33 | case "floor" -> IchorUtils.FLOOR; 34 | case "log" -> IchorUtils.LOG; 35 | case "max" -> IchorUtils.MAX; 36 | case "min" -> IchorUtils.MIN; 37 | case "pow" -> IchorUtils.POW; 38 | case "random" -> IchorUtils.RANDOM; 39 | case "round" -> IchorUtils.ROUND; 40 | case "sin" -> IchorUtils.SIN; 41 | case "sqrt" -> IchorUtils.SQRT; 42 | case "tan" -> IchorUtils.TAN; 43 | case "cbrt" -> IchorUtils.CBRT; 44 | case "cosh" -> IchorUtils.COSH; 45 | case "expm1" -> IchorUtils.EXPM1; 46 | case "hypot" -> IchorUtils.HYPOT; 47 | case "log1p" -> IchorUtils.LOG1P; 48 | case "log10" -> IchorUtils.LOG10; 49 | case "sinh" -> IchorUtils.SINH; 50 | case "tanh" -> IchorUtils.TANH; 51 | case "imul" -> IchorUtils.IMUL; 52 | case "trunc" -> IchorUtils.TRUNC; 53 | case "acosh" -> IchorUtils.ACOSH; 54 | case "asinh" -> IchorUtils.ASINH; 55 | case "atanh" -> IchorUtils.ATANH; 56 | case "sign" -> IchorUtils.SIGN; 57 | case "log2" -> IchorUtils.LOG2; 58 | case "fround" -> IchorUtils.FROUND; 59 | case "clz32" -> IchorUtils.CLZ32; 60 | default -> super.getStatic(scope, name); 61 | }; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/type/ObjectJS.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.type; 2 | 3 | import dev.latvian.apps.ichor.Callable; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.prototype.Prototype; 6 | import dev.latvian.apps.ichor.util.Functions; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.util.LinkedHashMap; 10 | import java.util.List; 11 | 12 | public class ObjectJS extends Prototype { 13 | public static final Callable ASSIGN = Functions.of2((scope, arg1, arg2) -> { 14 | var o = scope.asMap(arg1); 15 | //noinspection unchecked 16 | o.putAll(scope.asMap(arg2)); 17 | return o; 18 | }); 19 | 20 | public static final Callable CREATE = Functions.WIP; 21 | public static final Callable DEFINE_PROPERTIES = Functions.WIP; 22 | public static final Callable DEFINE_PROPERTY = Functions.WIP; 23 | 24 | public static final Callable ENTRIES = Functions.of1((scope, arg) -> { 25 | var p = scope.getPrototype(arg); 26 | var c = p.entries(scope, p.cast(arg)); 27 | return c == null ? List.of() : c; 28 | }); 29 | 30 | public static final Callable KEYS = Functions.of1((scope, arg) -> { 31 | var p = scope.getPrototype(arg); 32 | var c = p.keys(scope, p.cast(arg)); 33 | return c == null ? List.of() : c; 34 | }); 35 | 36 | public static final Callable VALUES = Functions.of1((scope, arg) -> { 37 | var p = scope.getPrototype(arg); 38 | var c = p.values(scope, p.cast(arg)); 39 | return c == null ? List.of() : c; 40 | }); 41 | 42 | public static final Callable GET_PROTOTYPE_OF = Functions.of1(Scope::getPrototype); 43 | 44 | public ObjectJS(Scope cx) { 45 | super(cx, "Object", ObjectJS.class); 46 | } 47 | 48 | @Override 49 | public Object call(Scope scope, Object[] args, boolean hasNew) { 50 | return new LinkedHashMap<>(); 51 | } 52 | 53 | @Override 54 | @Nullable 55 | public Object getStatic(Scope scope, String name) { 56 | return switch (name) { 57 | case "assign" -> ASSIGN; 58 | case "create" -> CREATE; 59 | case "defineProperties" -> DEFINE_PROPERTIES; 60 | case "defineProperty" -> DEFINE_PROPERTY; 61 | case "entries" -> ENTRIES; 62 | case "keys" -> KEYS; 63 | case "values" -> VALUES; 64 | case "getPrototypeOf" -> GET_PROTOTYPE_OF; 65 | default -> super.getStatic(scope, name); 66 | }; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/type/RegExpJS.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.type; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.error.ArgumentCountMismatchError; 5 | import dev.latvian.apps.ichor.prototype.Prototype; 6 | 7 | import java.util.regex.Pattern; 8 | 9 | public class RegExpJS extends Prototype { 10 | public RegExpJS(Scope cx) { 11 | super(cx, "RegExp", Pattern.class); 12 | } 13 | 14 | @Override 15 | public Object call(Scope scope, Object[] args, boolean hasNew) { 16 | if (args.length == 0) { 17 | throw new ArgumentCountMismatchError(1, 0); 18 | } 19 | 20 | int flags = 0; 21 | 22 | if (args.length >= 2) { 23 | for (char c : scope.asString(args[1], false).toCharArray()) { 24 | switch (c) { 25 | case 'd' -> flags |= Pattern.UNIX_LINES; 26 | case 'i' -> flags |= Pattern.CASE_INSENSITIVE; 27 | case 'x' -> flags |= Pattern.COMMENTS; 28 | case 'm' -> flags |= Pattern.MULTILINE; 29 | case 's' -> flags |= Pattern.DOTALL; 30 | case 'u' -> flags |= Pattern.UNICODE_CASE; 31 | case 'U' -> flags |= Pattern.UNICODE_CHARACTER_CLASS; 32 | case 'g' -> throw new IllegalArgumentException("g flag is not supported!"); 33 | } 34 | } 35 | } 36 | 37 | return Pattern.compile(scope.asString(args[0], false), flags); 38 | } 39 | 40 | @Override 41 | public boolean asString(Scope scope, Pattern self, StringBuilder builder, boolean escape) { 42 | builder.append('/'); 43 | builder.append(self.pattern()); 44 | builder.append('/'); 45 | 46 | var flags = self.flags(); 47 | 48 | if ((flags & Pattern.UNIX_LINES) != 0) { 49 | builder.append('d'); 50 | } 51 | 52 | if ((flags & Pattern.CASE_INSENSITIVE) != 0) { 53 | builder.append('i'); 54 | } 55 | 56 | if ((flags & Pattern.COMMENTS) != 0) { 57 | builder.append('x'); 58 | } 59 | 60 | if ((flags & Pattern.MULTILINE) != 0) { 61 | builder.append('m'); 62 | } 63 | 64 | if ((flags & Pattern.DOTALL) != 0) { 65 | builder.append('s'); 66 | } 67 | 68 | if ((flags & Pattern.UNICODE_CASE) != 0) { 69 | builder.append('u'); 70 | } 71 | 72 | if ((flags & Pattern.UNICODE_CHARACTER_CLASS) != 0) { 73 | builder.append('U'); 74 | } 75 | 76 | return true; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/type/SetJS.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.type; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.Special; 5 | import dev.latvian.apps.ichor.prototype.Prototype; 6 | import dev.latvian.apps.ichor.util.Functions; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.util.Arrays; 10 | import java.util.Collection; 11 | import java.util.LinkedHashSet; 12 | import java.util.List; 13 | import java.util.Set; 14 | 15 | @SuppressWarnings({"rawtypes", "unchecked"}) 16 | public class SetJS extends Prototype { 17 | public static final Functions.Bound ADD = (scope, self, args) -> { 18 | self.add(args[0]); 19 | return self; 20 | }; 21 | 22 | public static final Functions.Bound CLEAR = (scope, self, args) -> { 23 | self.clear(); 24 | return Special.UNDEFINED; 25 | }; 26 | 27 | public static final Functions.Bound DELETE = (scope, self, args) -> self.remove(args[0]); 28 | 29 | public static final Functions.Bound ENTRIES = (scope, self, args) -> { 30 | var entries = new List[self.size()]; 31 | int i = 0; 32 | 33 | for (var entry : self) { 34 | entries[i] = List.of(entry, entry); 35 | i++; 36 | } 37 | 38 | return Arrays.asList(entries).iterator(); 39 | }; 40 | 41 | public static final Functions.Bound HAS = (scope, self, args) -> self.contains(args[0]); 42 | public static final Functions.Bound VALUES = (scope, self, args) -> self.iterator(); 43 | 44 | public SetJS(Scope cx) { 45 | super(cx, "Set", Set.class); 46 | } 47 | 48 | @Override 49 | public Object call(Scope scope, Object[] args, boolean hasNew) { 50 | return new LinkedHashSet<>(); 51 | } 52 | 53 | @Override 54 | public Collection keys(Scope scope, Set self) { 55 | return self; 56 | } 57 | 58 | @Override 59 | public Collection values(Scope scope, Set self) { 60 | return self; 61 | } 62 | 63 | @Override 64 | @Nullable 65 | public Object getLocal(Scope scope, Set self, String name) { 66 | return switch (name) { 67 | case "length", "size" -> self.size(); 68 | case "add" -> ADD.with(self); 69 | case "clear" -> CLEAR.with(self); 70 | case "delete" -> DELETE.with(self); 71 | case "entries" -> ENTRIES.with(self); 72 | case "has" -> HAS.with(self); 73 | case "keys", "values" -> VALUES.with(self); 74 | case "forEach" -> IterableJS.FOR_EACH.with(self); 75 | default -> super.getStatic(scope, name); 76 | }; 77 | } 78 | 79 | @Override 80 | public int getLength(Scope scope, Object self) { 81 | return ((Set) self).size(); 82 | } 83 | 84 | @Override 85 | public Collection entries(Scope scope, Set self) { 86 | if (self.isEmpty()) { 87 | return List.of(); 88 | } else if (self.size() == 1) { 89 | var value = self.iterator().next(); 90 | return List.of(List.of(value, value)); 91 | } else { 92 | var entries = new List[self.size()]; 93 | int i = 0; 94 | 95 | for (var entry : self) { 96 | entries[i] = List.of(entry, entry); 97 | i++; 98 | } 99 | 100 | return Arrays.asList(entries); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/util/AssignType.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.util; 2 | 3 | public enum AssignType { 4 | NONE, 5 | MUTABLE, 6 | IMMUTABLE; 7 | 8 | public boolean isSet() { 9 | return this != NONE; 10 | } 11 | 12 | public boolean isMutable() { 13 | return this == MUTABLE; 14 | } 15 | 16 | public boolean isImmutable() { 17 | return this == IMMUTABLE; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/util/CharacterScanner.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.util; 2 | 3 | @FunctionalInterface 4 | public interface CharacterScanner { 5 | CharacterScanner NO_INPUT = () -> { 6 | throw new IllegalStateException("No input"); 7 | }; 8 | 9 | CharacterScanner STDIN = () -> { 10 | try { 11 | return (char) System.in.read(); 12 | } catch (Exception ex) { 13 | throw new IllegalStateException(ex); 14 | } 15 | }; 16 | 17 | char nextChar(); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/util/ClassFunctionInstance.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.util; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.ast.expression.AstClassFunction; 5 | 6 | public class ClassFunctionInstance extends FunctionInstance { 7 | 8 | public ClassFunctionInstance(AstClassFunction function, Scope evalScope) { 9 | super(function, evalScope); 10 | } 11 | 12 | /* 13 | @Override 14 | public String getPrototypeName() { 15 | if (functionName == null) { 16 | functionName = ""; 17 | } 18 | 19 | return functionName; 20 | } 21 | */ 22 | 23 | @Override 24 | public Object call(Scope callScope, Object[] args, boolean hasNew) { 25 | if (function == ((AstClassFunction) function).owner.constructor) { 26 | return super.call(callScope, args, false); 27 | } else { 28 | return super.call(callScope, args, hasNew); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/util/ClassPrototype.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.util; 2 | 3 | import dev.latvian.apps.ichor.Callable; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.ast.statement.AstClass; 6 | import dev.latvian.apps.ichor.slot.Slot; 7 | 8 | import java.util.Arrays; 9 | 10 | public final class ClassPrototype implements Callable { 11 | public final AstClass astClass; 12 | public final Scope classEvalScope; 13 | 14 | public ClassPrototype(AstClass astClass, Scope classEvalScope) { 15 | this.astClass = astClass; 16 | this.classEvalScope = classEvalScope; 17 | } 18 | 19 | @Override 20 | public Object call(Scope callScope, Object[] args, boolean hasNew) { 21 | var instance = new Instance(this); 22 | 23 | if (astClass.constructor != null) { 24 | var ctor = astClass.constructor.eval(callScope); 25 | ctor.call(callScope, args, true); 26 | } 27 | 28 | return instance; 29 | } 30 | 31 | public static final class Instance extends Scope { 32 | public final ClassPrototype prototype; 33 | 34 | public Instance(ClassPrototype prototype) { 35 | super(prototype.classEvalScope.push()); 36 | setScopeThis(this); 37 | this.prototype = prototype; 38 | 39 | for (var func : prototype.astClass.methods.values()) { 40 | add(func.functionName, new ClassFunctionInstance(func, this), Slot.DEFAULT); 41 | } 42 | } 43 | 44 | public void interpretConstructorSuper(Object[] args) { 45 | System.out.println(Arrays.toString(args)); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/util/Empty.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.util; 2 | 3 | import dev.latvian.apps.ichor.ast.expression.AstParam; 4 | import dev.latvian.apps.ichor.ast.expression.AstType; 5 | import dev.latvian.apps.ichor.prototype.Prototype; 6 | import dev.latvian.apps.ichor.token.PositionedToken; 7 | 8 | import java.util.function.Consumer; 9 | 10 | public class Empty { 11 | public static final Object[] OBJECTS = new Object[0]; 12 | public static final Class[] CLASSES = new Class[0]; 13 | public static final String[] STRINGS = new String[0]; 14 | public static final AstParam[] AST_PARAMS = new AstParam[0]; 15 | public static final PositionedToken[] POSITIONED_TOKENS = new PositionedToken[0]; 16 | public static final AstType[] AST_TYPES = new AstType[0]; 17 | public static final Prototype[] PROTOTYPES = new Prototype[0]; 18 | 19 | public static final Consumer CONSUMER = o -> { 20 | }; 21 | 22 | public static Consumer consumer() { 23 | return (Consumer) CONSUMER; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/util/EvaluableConstant.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.util; 2 | 3 | import dev.latvian.apps.ichor.Evaluable; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.Special; 6 | 7 | public record EvaluableConstant(Object value) implements Evaluable { 8 | @Override 9 | public Object eval(Scope scope) { 10 | return value == Special.NULL ? null : value; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/util/FunctionInstance.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.util; 2 | 3 | import dev.latvian.apps.ichor.CallableTypeAdapter; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.Special; 6 | import dev.latvian.apps.ichor.ast.expression.AstFunction; 7 | import dev.latvian.apps.ichor.error.ArgumentCountMismatchError; 8 | import dev.latvian.apps.ichor.error.ConstructorError; 9 | import dev.latvian.apps.ichor.exit.ReturnExit; 10 | 11 | import java.util.concurrent.CompletableFuture; 12 | 13 | public class FunctionInstance implements CallableTypeAdapter { 14 | public final AstFunction function; 15 | public final Scope evalScope; 16 | 17 | public FunctionInstance(AstFunction function, Scope evalScope) { 18 | this.function = function; 19 | this.evalScope = evalScope; 20 | } 21 | 22 | @Override 23 | public Object call(Scope callScope, Object[] args, boolean hasNew) { 24 | if (hasNew) { 25 | throw new ConstructorError(null); 26 | } else if (args.length < function.requiredParams) { 27 | throw new ArgumentCountMismatchError(function.requiredParams, args.length).pos(function.pos); 28 | } 29 | 30 | var s = evalScope.push(this); 31 | 32 | try { 33 | s.scopeArguments = new Object[function.params.length]; 34 | 35 | if (!(function.hasMod(AstFunction.Mod.ARROW) || function.hasMod(AstFunction.Mod.CLASS))) { 36 | s.setScopeThis(s); 37 | } 38 | 39 | for (int i = 0; i < function.params.length; i++) { 40 | Object value; 41 | 42 | if (i >= args.length) { 43 | if (function.params[i].defaultValue == Special.UNDEFINED) { 44 | value = Special.UNDEFINED; 45 | } else { 46 | value = s.eval(function.params[i].defaultValue); 47 | } 48 | } else { 49 | value = args[i]; 50 | } 51 | 52 | s.scopeArguments[i] = value; 53 | s.addMutable(function.params[i].name, value); 54 | } 55 | 56 | function.body.interpretSafe(s); 57 | } catch (ReturnExit exit) { 58 | if (function.hasMod(AstFunction.Mod.ASYNC)) { 59 | return CompletableFuture.completedFuture(exit.value); 60 | } 61 | 62 | return exit.value; 63 | } 64 | 65 | return Special.UNDEFINED; 66 | } 67 | 68 | @Override 69 | public Scope getEvalScope() { 70 | return evalScope; 71 | } 72 | 73 | @Override 74 | public String toString() { 75 | return "FunctionProxy[" + function + "]"; 76 | } 77 | 78 | @Override 79 | public int hashCode() { 80 | return function.hashCode(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/util/Functions.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.util; 2 | 3 | import dev.latvian.apps.ichor.Callable; 4 | import dev.latvian.apps.ichor.Scope; 5 | import dev.latvian.apps.ichor.error.ArgumentCountMismatchError; 6 | import dev.latvian.apps.ichor.error.WIPFeatureError; 7 | 8 | public class Functions { 9 | public static final Callable WIP = (scope, args, hasNew) -> { 10 | throw new WIPFeatureError(); 11 | }; 12 | 13 | public static Callable ofN(ArgN function) { 14 | return function; 15 | } 16 | 17 | public static Callable of1(Arg1 function) { 18 | return function; 19 | } 20 | 21 | public static Callable of2(Arg2 function) { 22 | return function; 23 | } 24 | 25 | public static Callable of3(Arg3 function) { 26 | return function; 27 | } 28 | 29 | @FunctionalInterface 30 | public interface Bound { 31 | Object call(Scope scope, T self, Object[] args); 32 | 33 | default Callable with(T self) { 34 | return new BoundCallable<>(self, this); 35 | } 36 | } 37 | 38 | public record BoundCallable(T self, Bound function) implements Callable { 39 | @Override 40 | public Object call(Scope scope, Object[] args, boolean hasNew) { 41 | return function.call(scope, self, args); 42 | } 43 | } 44 | 45 | @FunctionalInterface 46 | public interface ArgN extends Callable { 47 | Object call(Scope scope, Object[] args); 48 | 49 | @Override 50 | default Object call(Scope scope, Object[] args, boolean hasNew) { 51 | return call(scope, args); 52 | } 53 | } 54 | 55 | @FunctionalInterface 56 | public interface Arg1 extends Callable { 57 | Object call(Scope scope, Object arg); 58 | 59 | @Override 60 | default Object call(Scope scope, Object[] args, boolean hasNew) { 61 | if (args.length < 1) { 62 | throw new ArgumentCountMismatchError(1, args.length); 63 | } 64 | 65 | return call(scope, args[0]); 66 | } 67 | } 68 | 69 | @FunctionalInterface 70 | public interface Arg2 extends Callable { 71 | Object call(Scope scope, Object arg1, Object arg2); 72 | 73 | @Override 74 | default Object call(Scope scope, Object[] args, boolean hasNew) { 75 | if (args.length < 2) { 76 | throw new ArgumentCountMismatchError(2, args.length); 77 | } 78 | 79 | return call(scope, args[0], args[1]); 80 | } 81 | } 82 | 83 | @FunctionalInterface 84 | public interface Arg3 extends Callable { 85 | Object call(Scope scope, Object arg1, Object arg2, Object arg3); 86 | 87 | @Override 88 | default Object call(Scope scope, Object[] args, boolean hasNew) { 89 | if (args.length < 3) { 90 | throw new ArgumentCountMismatchError(3, args.length); 91 | } 92 | 93 | return call(scope, args[0], args[1], args[2]); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/util/JavaArray.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.util; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | import dev.latvian.apps.ichor.TypeAdapter; 5 | 6 | import java.lang.reflect.Array; 7 | import java.util.AbstractList; 8 | import java.util.Arrays; 9 | import java.util.Collection; 10 | import java.util.List; 11 | 12 | public class JavaArray extends AbstractList implements TypeAdapter { 13 | @SuppressWarnings({"rawtypes", "unchecked"}) 14 | public static List of(Object array) { 15 | if (array instanceof List list) { 16 | return list; 17 | } else if (array instanceof Object[] arr) { 18 | return arr.length == 0 ? List.of() : Arrays.asList(arr); 19 | } 20 | 21 | return new JavaArray(array); 22 | } 23 | 24 | public final Object array; 25 | private int size = -1; 26 | 27 | public JavaArray(Object array) { 28 | this.array = array; 29 | } 30 | 31 | @Override 32 | public Object get(int index) { 33 | return Array.get(array, index); 34 | } 35 | 36 | @Override 37 | public Object set(int index, Object element) { 38 | var old = get(index); 39 | Array.set(array, index, element); 40 | return old; 41 | } 42 | 43 | @Override 44 | public int size() { 45 | if (size == -1) { 46 | size = Array.getLength(array); 47 | } 48 | 49 | return size; 50 | } 51 | 52 | @Override 53 | @SuppressWarnings("unchecked") 54 | public T adapt(Scope scope, Class type) { 55 | if (type == array.getClass()) { 56 | return (T) array; 57 | } else if (type.isArray()) { 58 | var cType = type.getComponentType(); 59 | var arr = Array.newInstance(cType, size()); 60 | 61 | for (int i = 0; i < size(); i++) { 62 | Array.set(arr, i, scope.as(get(i), cType)); 63 | } 64 | 65 | return (T) arr; 66 | } 67 | 68 | return null; 69 | } 70 | 71 | public static Object adaptToArray(Scope scope, Iterable itr, Class toType) { 72 | var cType = toType.getComponentType(); 73 | 74 | if (itr instanceof List list) { 75 | var arr = Array.newInstance(cType, list.size()); 76 | 77 | for (int i = 0; i < list.size(); i++) { 78 | Array.set(arr, i, scope.as(list.get(i), cType)); 79 | } 80 | 81 | return arr; 82 | } else if (itr instanceof Collection collection) { 83 | var arr = Array.newInstance(cType, collection.size()); 84 | int index = 0; 85 | 86 | for (var o1 : collection) { 87 | Array.set(arr, index, scope.as(o1, cType)); 88 | index++; 89 | } 90 | 91 | return arr; 92 | } else { 93 | int size = 0; 94 | 95 | for (var ignore : itr) { 96 | size++; 97 | } 98 | 99 | var arr = Array.newInstance(cType, size); 100 | int index = 0; 101 | 102 | for (var o1 : itr) { 103 | Array.set(arr, index, scope.as(o1, cType)); 104 | index++; 105 | } 106 | 107 | return arr; 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/util/NamedSignature.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.util; 2 | 3 | public record NamedSignature(String name, Signature signature) { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/util/NamedTokenSource.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.util; 2 | 3 | import dev.latvian.apps.ichor.token.TokenSource; 4 | 5 | public record NamedTokenSource(String name) implements TokenSource { 6 | @Override 7 | public String getSourceName() { 8 | return name; 9 | } 10 | 11 | @Override 12 | public String toString() { 13 | return name; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/util/PrintWrapper.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.util; 2 | 3 | import java.io.PrintStream; 4 | import java.io.PrintWriter; 5 | 6 | public interface PrintWrapper { 7 | static PrintWrapper of(PrintStream s) { 8 | return new WrappedPrintStream(s); 9 | } 10 | 11 | static PrintWrapper of(PrintWriter w) { 12 | return new WrappedPrintWriter(w); 13 | } 14 | 15 | /** 16 | * Returns the object to be locked when using this StreamOrWriter 17 | */ 18 | Object lock(); 19 | 20 | /** 21 | * Prints the specified string as a line on this StreamOrWriter 22 | */ 23 | void println(Object o); 24 | 25 | record WrappedPrintStream(PrintStream printStream) implements PrintWrapper { 26 | @Override 27 | public Object lock() { 28 | return printStream; 29 | } 30 | 31 | @Override 32 | public void println(Object o) { 33 | printStream.println(o); 34 | } 35 | } 36 | 37 | record WrappedPrintWriter(PrintWriter printWriter) implements PrintWrapper { 38 | @Override 39 | public Object lock() { 40 | return printWriter; 41 | } 42 | 43 | @Override 44 | public void println(Object o) { 45 | printWriter.println(o); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/main/java/dev/latvian/apps/ichor/util/ScopeWrapper.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.util; 2 | 3 | import dev.latvian.apps.ichor.Scope; 4 | 5 | // FIXME 6 | public class ScopeWrapper { 7 | private final Scope scope; 8 | 9 | public ScopeWrapper(Scope s) { 10 | scope = s; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/dev/latvian/apps/ichor/test/AdvancedTestUtils.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.test; 2 | 3 | import dev.latvian.apps.ichor.ast.AstStringBuilder; 4 | 5 | import java.util.Map; 6 | import java.util.function.Consumer; 7 | import java.util.function.Supplier; 8 | 9 | public final class AdvancedTestUtils { 10 | private final TestConsole console; 11 | public final Short short1 = 30; 12 | public final short short2 = 40; 13 | 14 | public AdvancedTestUtils(TestConsole console) { 15 | this.console = console; 16 | } 17 | 18 | public interface FloatSupplier { 19 | Float supplyFloat(); 20 | } 21 | 22 | public void runnable(Runnable runnable) { 23 | runnable.run(); 24 | } 25 | 26 | public void consumer(float x, Consumer consumer) { 27 | consumer.accept(AstStringBuilder.wrapNumber(x) + " x hello"); 28 | } 29 | 30 | public double supplier(Supplier supplier) { 31 | return supplier.get().doubleValue(); 32 | } 33 | 34 | public void testFloat(float value) { 35 | console.log("Float value: " + AstStringBuilder.wrapNumber(value)); 36 | } 37 | 38 | public void testFloatSupplier(FloatSupplier func) { 39 | console.log("Float value: " + AstStringBuilder.wrapNumber(func.supplyFloat())); 40 | System.out.printf("Func object: %s #%08X\n", func, func.hashCode()); 41 | System.out.printf("Func class: %s #%08X\n", func.getClass().getName(), func.getClass().hashCode()); 42 | } 43 | 44 | public void testMap(Map map) { 45 | console.log("Map: " + map); 46 | } 47 | 48 | public int getBean() { 49 | return 30; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/dev/latvian/apps/ichor/test/InterpreterExpressionTests.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.test; 2 | 3 | import org.junit.jupiter.api.MethodOrderer; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.jupiter.api.TestMethodOrder; 6 | import org.junit.jupiter.api.Timeout; 7 | 8 | @Timeout(value = 3, threadMode = Timeout.ThreadMode.SEPARATE_THREAD) 9 | @TestMethodOrder(MethodOrderer.MethodName.class) 10 | public class InterpreterExpressionTests { 11 | private static void test(String eval, String match) { 12 | InterpreterTests.testInterpreter("console.log(" + eval + ")", match); 13 | } 14 | 15 | @Test 16 | public void add() { 17 | test("2 + 3", "5"); 18 | } 19 | 20 | @Test 21 | public void addf() { 22 | test("2.5 + 3.8", "6.3"); 23 | } 24 | 25 | @Test 26 | public void adds() { 27 | test("'h' + 'i'", "hi"); 28 | } 29 | 30 | @Test 31 | public void addns() { 32 | test("'11' + 1", "111"); 33 | } 34 | 35 | @Test 36 | public void subns() { 37 | test("'11' - 1", "10"); 38 | } 39 | 40 | @Test 41 | public void mul() { 42 | test("2 * 3", "6"); 43 | } 44 | 45 | @Test 46 | public void mulf() { 47 | test("2.5 * 3.0", "7.5"); 48 | } 49 | 50 | @Test 51 | public void div() { 52 | test("3 / 2", "1.5"); 53 | } 54 | 55 | @Test 56 | public void divf() { 57 | test("2 / 0.5", "4"); 58 | } 59 | 60 | @Test 61 | public void eq() { 62 | test("3 == 3.0", "true"); 63 | } 64 | 65 | @Test 66 | public void seq() { 67 | test("3 === 3.0", "true"); 68 | } 69 | 70 | @Test 71 | public void neq() { 72 | test("3 != 30.0", "true"); 73 | } 74 | 75 | @Test 76 | public void sneq() { 77 | test("3 !== 3.0", "false"); 78 | } 79 | 80 | @Test 81 | public void lt() { 82 | test("1 < 3.0", "true"); 83 | } 84 | 85 | @Test 86 | public void lte() { 87 | test("1 <= 1.0", "true"); 88 | } 89 | 90 | @Test 91 | public void gt() { 92 | test("3 > 1.0", "true"); 93 | } 94 | 95 | @Test 96 | public void gte() { 97 | test("3 >= 3.0", "true"); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/test/java/dev/latvian/apps/ichor/test/ReflectionExample.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.test; 2 | 3 | public class ReflectionExample { 4 | public int publicField = 30; 5 | private final float privateField = 40.5F; 6 | public transient String publicTransientField = "Hi"; 7 | 8 | public void sout(String text) { 9 | System.out.println(text); 10 | } 11 | 12 | public void serr(String text) { 13 | System.err.println(text); 14 | } 15 | 16 | public void soutNum(short number) { 17 | System.out.println(number); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/dev/latvian/apps/ichor/test/ScopeTests.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.test; 2 | 3 | import dev.latvian.apps.ichor.Context; 4 | import dev.latvian.apps.ichor.RootScope; 5 | import dev.latvian.apps.ichor.error.IchorError; 6 | import org.junit.jupiter.api.Assertions; 7 | import org.junit.jupiter.api.Test; 8 | 9 | public class ScopeTests { 10 | @Test 11 | public void redeclaration() { 12 | Assertions.assertThrows(IchorError.class, () -> { 13 | var root = new RootScope(new Context()); 14 | root.addImmutable("test", 5); 15 | 16 | var child = root.push(); 17 | child.setMember("test", 10); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/dev/latvian/apps/ichor/test/TestConsole.java: -------------------------------------------------------------------------------- 1 | package dev.latvian.apps.ichor.test; 2 | 3 | import java.io.PrintStream; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | public final class TestConsole { 8 | private final PrintStream printStream; 9 | public final List output; 10 | public String lastLine = null; 11 | 12 | public TestConsole(PrintStream printStream) { 13 | this.printStream = printStream; 14 | this.output = new ArrayList<>(); 15 | } 16 | 17 | public void log(String s) { 18 | if (s == null) { 19 | s = ""; 20 | } 21 | 22 | lastLine = s; 23 | 24 | if (!s.isBlank()) { 25 | output.add(s.trim()); 26 | } 27 | 28 | printStream.println("> " + s); 29 | } 30 | 31 | public void logClass(Object o) { 32 | log(o.getClass().getName()); 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "TestConsoleImpl"; 38 | } 39 | 40 | public String getLastLine() { 41 | return lastLine; 42 | } 43 | 44 | public void setLastLine(String s) { 45 | lastLine = s; 46 | } 47 | } 48 | --------------------------------------------------------------------------------