├── .gitattributes ├── .github └── workflows │ ├── docs.yml │ └── gradle.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── build.gradle ├── dist ├── icon.png ├── own ├── own.cmd ├── ownlang ├── ownlang-runner.cmd └── ownlang.cmd ├── docs ├── .gitignore ├── build.gradle ├── docs │ ├── .vuepress │ │ ├── components │ │ │ ├── Scope.vue │ │ │ └── Since.vue │ │ ├── config.js │ │ ├── configs │ │ │ ├── navbar.js │ │ │ ├── pages.js │ │ │ └── sidebar.js │ │ └── styles │ │ │ └── palette.scss │ ├── README.md │ ├── code │ │ ├── basics │ │ │ ├── destructuring_assignment1.own │ │ │ ├── destructuring_assignment2.own │ │ │ ├── destructuring_assignment3.own │ │ │ ├── destructuring_assignment4.own │ │ │ ├── fibonacci.own │ │ │ ├── loops1.own │ │ │ ├── pattern_matching1.own │ │ │ ├── pattern_matching2.own │ │ │ ├── pattern_matching3.own │ │ │ ├── pattern_matching4.own │ │ │ ├── pattern_matching5.own │ │ │ ├── pattern_matching6.own │ │ │ └── string_functions1.own │ │ ├── functional_en.own │ │ ├── functional_ru.own │ │ ├── high_order_functions_en.own │ │ ├── high_order_functions_ru.own │ │ ├── http_en.own │ │ ├── http_ru.own │ │ ├── operator_overloading.own │ │ └── pattern_matching.own │ ├── en │ │ ├── README.md │ │ ├── basics │ │ │ ├── README.md │ │ │ ├── array_functions.md │ │ │ ├── comments.md │ │ │ ├── destructuring_assignment.md │ │ │ ├── functions.md │ │ │ ├── loops.md │ │ │ ├── pattern_matching.md │ │ │ ├── string_functions.md │ │ │ ├── strings.md │ │ │ └── types.md │ │ ├── changelog.md │ │ └── links.md │ └── ru │ │ ├── README.md │ │ ├── basics │ │ ├── README.md │ │ ├── array_functions.md │ │ ├── comments.md │ │ ├── destructuring_assignment.md │ │ ├── functions.md │ │ ├── loops.md │ │ ├── pattern_matching.md │ │ ├── string_functions.md │ │ ├── strings.md │ │ └── types.md │ │ ├── changelog.md │ │ └── links.md ├── package.json ├── pnpm-lock.yaml └── src │ ├── docgen-md.own │ ├── main │ └── java │ │ └── com │ │ └── annimon │ │ └── ownlang │ │ └── docs │ │ ├── ModuleInfo.java │ │ └── ModulesInfoCreator.java │ └── modules │ ├── android.yml │ ├── base64.yml │ ├── canvas.yml │ ├── canvas_android.yml │ ├── canvasfx.yml │ ├── collections.yml │ ├── date.yml │ ├── downloader.yml │ ├── files.yml │ ├── forms.yml │ ├── forms_android.yml │ ├── functional.yml │ ├── gps_android.yml │ ├── gzip.yml │ ├── http.yml │ ├── imageprocessing_android.yml │ ├── java.yml │ ├── jdbc.yml │ ├── json.yml │ ├── math.yml │ ├── okhttp.yml │ ├── ounit.yml │ ├── regex.yml │ ├── robot.yml │ ├── server.yml │ ├── socket.yml │ ├── std.yml │ ├── types.yml │ ├── yaml.yml │ └── zip.yml ├── editors ├── README.md ├── gtksourceview │ └── ownlang.lang ├── highlighjs │ └── own.js ├── idea │ ├── IntelliJ IDEA Global Settings │ └── filetypes │ │ └── OwnLang.xml └── prismjs │ └── own-language.js ├── examples ├── basics │ ├── array.own │ ├── bitwise_operators.own │ ├── classes.own │ ├── destructuring_assignment.own │ ├── extended_identifier.own │ ├── loops.own │ ├── map.own │ ├── operator_overloading.own │ ├── pattern_matching.own │ ├── ternary_operator.own │ ├── thread.own │ └── types.own ├── canvas │ ├── 1.own │ ├── 2.own │ ├── animate_line.own │ ├── animate_line_thread.own │ ├── control_point.own │ ├── fractal_polygon.own │ ├── fractal_rect.own │ ├── fx_basic_shapes.own │ ├── fx_event_handlers.own │ ├── fx_global_alpha.own │ ├── fx_image.own │ ├── fx_image_negate.own │ ├── fx_koch_snowflake.own │ └── fx_rotation.own ├── console │ └── colors.own ├── database │ ├── hsqldb.own │ └── sqlite.own ├── formats │ ├── gzip.own │ ├── json.own │ ├── yaml.own │ └── zip.own ├── forms │ ├── basic.own │ ├── button.own │ ├── chatgpt.own │ ├── complicatedForm.own │ ├── look_and_feel.own │ ├── panel.own │ ├── progressbar.own │ ├── samobot_chat.own │ ├── textarea.own │ ├── textfield.own │ └── windowlistener.own ├── functions │ ├── basics.own │ ├── calculator.own │ ├── chain.own │ ├── default_arguments.own │ ├── factorial.own │ ├── filter_map.own │ ├── flatmap.own │ ├── function_call_chain.own │ ├── function_chain.own │ ├── groupby.own │ ├── reduce.own │ ├── sortby.own │ ├── stream.own │ └── tomap.own ├── game │ ├── agar.own │ ├── minesweeper.own │ ├── pipes-online │ │ ├── pipes_online.own │ │ └── server │ │ │ ├── main.js │ │ │ └── package.json │ ├── pipes.own │ └── pipes_online.own ├── java │ ├── collections.own │ └── system_info.own ├── network │ ├── demo.own │ ├── github_timeline.own │ ├── okhttp_imgur_upload.own │ ├── okhttp_telegram_sendvoice.own │ ├── okhttp_websocket.own │ ├── telegram_api.own │ └── twitch_tools.own ├── robot │ └── paint_lines.own ├── server │ ├── notes_public │ │ ├── index.html │ │ ├── notes.js │ │ └── styles.css │ ├── server_spa.own │ └── server_spa_simple.own └── versions │ ├── whatsnew_1.5.0.own │ └── whatsnew_2.0.0.own ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── modules ├── canvasfx │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── annimon │ │ └── ownlang │ │ └── modules │ │ └── canvasfx │ │ └── canvasfx.java ├── jdbc │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── annimon │ │ └── ownlang │ │ └── modules │ │ └── jdbc │ │ ├── ConnectionValue.java │ │ ├── JdbcConverters.java │ │ ├── ResultSetValue.java │ │ ├── StatementValue.java │ │ └── jdbc.java ├── main │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── annimon │ │ └── ownlang │ │ └── modules │ │ ├── base64 │ │ └── base64.java │ │ ├── canvas │ │ └── canvas.java │ │ ├── collections │ │ └── collections.java │ │ ├── date │ │ └── date.java │ │ ├── downloader │ │ └── downloader.java │ │ ├── files │ │ └── files.java │ │ ├── forms │ │ ├── AbstractButtonValue.java │ │ ├── ComponentValue.java │ │ ├── Components.java │ │ ├── ContainerValue.java │ │ ├── JButtonValue.java │ │ ├── JComponentValue.java │ │ ├── JFrameValue.java │ │ ├── JLabelValue.java │ │ ├── JPanelValue.java │ │ ├── JProgressBarValue.java │ │ ├── JScrollPaneValue.java │ │ ├── JTextAreaValue.java │ │ ├── JTextComponentValue.java │ │ ├── JTextFieldValue.java │ │ ├── LayoutManagerValue.java │ │ ├── LayoutManagers.java │ │ ├── WindowValue.java │ │ └── forms.java │ │ ├── functional │ │ ├── StreamValue.java │ │ ├── functional.java │ │ ├── functional_chain.java │ │ ├── functional_combine.java │ │ ├── functional_dropWhile.java │ │ ├── functional_filter.java │ │ ├── functional_filterNot.java │ │ ├── functional_flatmap.java │ │ ├── functional_forEach.java │ │ ├── functional_forEachIndexed.java │ │ ├── functional_groupBy.java │ │ ├── functional_map.java │ │ ├── functional_match.java │ │ ├── functional_reduce.java │ │ ├── functional_sortBy.java │ │ ├── functional_stream.java │ │ ├── functional_takeWhile.java │ │ └── functional_toMap.java │ │ ├── gzip │ │ └── gzip.java │ │ ├── http │ │ ├── HttpFunctions.java │ │ ├── http.java │ │ ├── http_download.java │ │ └── http_urlencode.java │ │ ├── java │ │ └── java.java │ │ ├── json │ │ ├── json.java │ │ ├── json_decode.java │ │ └── json_encode.java │ │ ├── math │ │ └── math.java │ │ ├── okhttp │ │ ├── CallValue.java │ │ ├── HttpClientBuilderValue.java │ │ ├── HttpClientValue.java │ │ ├── MultipartBodyBuilderValue.java │ │ ├── MultipartBodyValue.java │ │ ├── RequestBodyValue.java │ │ ├── RequestBuilderValue.java │ │ ├── ResponseBodyValue.java │ │ ├── ResponseValue.java │ │ ├── Values.java │ │ ├── WebSocketValue.java │ │ └── okhttp.java │ │ ├── ounit │ │ └── ounit.java │ │ ├── regex │ │ ├── MatcherValue.java │ │ ├── PatternValue.java │ │ └── regex.java │ │ ├── robot │ │ ├── robot.java │ │ ├── robot_exec.java │ │ ├── robot_fromclipboard.java │ │ └── robot_toclipboard.java │ │ ├── std │ │ ├── ArrayFunctions.java │ │ ├── NumberFunctions.java │ │ ├── StringFunctions.java │ │ ├── SystemFunctions.java │ │ ├── std.java │ │ ├── std_arrayCombine.java │ │ ├── std_arrayKeyExists.java │ │ ├── std_arrayKeys.java │ │ ├── std_arraySplice.java │ │ ├── std_arrayValues.java │ │ ├── std_charat.java │ │ ├── std_default.java │ │ ├── std_echo.java │ │ ├── std_indexof.java │ │ ├── std_join.java │ │ ├── std_lastindexof.java │ │ ├── std_length.java │ │ ├── std_newarray.java │ │ ├── std_rand.java │ │ ├── std_range.java │ │ ├── std_readln.java │ │ ├── std_replace.java │ │ ├── std_replaceall.java │ │ ├── std_replacefirst.java │ │ ├── std_sleep.java │ │ ├── std_sort.java │ │ ├── std_split.java │ │ ├── std_sprintf.java │ │ ├── std_substring.java │ │ ├── std_sync.java │ │ ├── std_thread.java │ │ ├── std_tochar.java │ │ ├── std_tolowercase.java │ │ ├── std_touppercase.java │ │ ├── std_trim.java │ │ └── std_try.java │ │ ├── types │ │ └── types.java │ │ ├── yaml │ │ ├── yaml.java │ │ ├── yaml_decode.java │ │ └── yaml_encode.java │ │ └── zip │ │ └── zip.java ├── server │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── annimon │ │ └── ownlang │ │ └── modules │ │ └── server │ │ ├── Config.java │ │ ├── ContextValue.java │ │ ├── ServerValue.java │ │ └── server.java └── socket │ ├── build.gradle │ └── src │ └── main │ └── java │ └── com │ └── annimon │ └── ownlang │ └── modules │ └── socket │ └── socket.java ├── ownlang-core ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── annimon │ └── ownlang │ ├── Console.java │ ├── Shared.java │ ├── Version.java │ ├── exceptions │ ├── ArgumentsMismatchException.java │ ├── OperationIsNotSupportedException.java │ ├── OwnLangRuntimeException.java │ ├── PatternMatchingException.java │ ├── StoppedException.java │ ├── TypeException.java │ ├── UnknownClassException.java │ ├── UnknownFunctionException.java │ ├── UnknownPropertyException.java │ └── VariableDoesNotExistsException.java │ ├── lib │ ├── Arguments.java │ ├── ArrayValue.java │ ├── AutoCloseableScope.java │ ├── CallStack.java │ ├── ClassDeclaration.java │ ├── ClassField.java │ ├── ClassInstance.java │ ├── ClassMethod.java │ ├── Converters.java │ ├── EvaluableValue.java │ ├── Function.java │ ├── FunctionValue.java │ ├── Functions.java │ ├── Instantiable.java │ ├── MapValue.java │ ├── ModuleLoader.java │ ├── NumberValue.java │ ├── RootScope.java │ ├── Scope.java │ ├── ScopeHandler.java │ ├── StringValue.java │ ├── Types.java │ ├── Value.java │ ├── ValueUtils.java │ └── Variables.java │ ├── modules │ └── Module.java │ ├── outputsettings │ ├── ConsoleOutputSettings.java │ ├── OutputSettings.java │ └── StringOutputSettings.java │ ├── stages │ ├── ScopedStage.java │ ├── ScopedStageFactory.java │ ├── Stage.java │ ├── StagesData.java │ └── StagesDataMap.java │ └── util │ ├── ErrorsLocationFormatterStage.java │ ├── ErrorsStackTraceFormatterStage.java │ ├── ExceptionConverterStage.java │ ├── ExceptionStackTraceToStringStage.java │ ├── Pos.java │ ├── Range.java │ ├── SimpleError.java │ ├── SourceLocatedError.java │ ├── SourceLocation.java │ ├── SourceLocationFormatterStage.java │ └── input │ ├── InputSource.java │ ├── InputSourceDetector.java │ ├── InputSourceFile.java │ ├── InputSourceProgram.java │ ├── InputSourceResource.java │ └── SourceLoaderStage.java ├── ownlang-desktop ├── build.gradle └── src │ └── main │ ├── java │ └── com │ │ └── annimon │ │ └── ownlang │ │ ├── Main.java │ │ └── RunOptions.java │ └── resources │ └── scripts │ ├── checkupdate.own │ ├── listscripts.own │ ├── own.own │ └── own │ ├── Config.own │ ├── Own.own │ ├── Packages.own │ ├── Projects.own │ └── Registry.own ├── ownlang-parser ├── build.gradle └── src │ ├── main │ └── java │ │ └── com │ │ └── annimon │ │ └── ownlang │ │ ├── exceptions │ │ ├── OwnLangParserException.java │ │ └── ParseException.java │ │ ├── lib │ │ └── UserDefinedFunction.java │ │ ├── parser │ │ ├── BeautifierStage.java │ │ ├── Lexer.java │ │ ├── Parser.java │ │ ├── ParserMetadata.java │ │ ├── SourceLoader.java │ │ ├── Token.java │ │ ├── TokenType.java │ │ ├── ast │ │ │ ├── Accessible.java │ │ │ ├── Argument.java │ │ │ ├── Arguments.java │ │ │ ├── ArrayExpression.java │ │ │ ├── AssignmentExpression.java │ │ │ ├── BinaryExpression.java │ │ │ ├── BlockStatement.java │ │ │ ├── BreakStatement.java │ │ │ ├── ClassDeclarationStatement.java │ │ │ ├── ConditionalExpression.java │ │ │ ├── ContainerAccessExpression.java │ │ │ ├── ContinueStatement.java │ │ │ ├── DestructuringAssignmentStatement.java │ │ │ ├── DoWhileStatement.java │ │ │ ├── ExprStatement.java │ │ │ ├── ForStatement.java │ │ │ ├── ForeachArrayStatement.java │ │ │ ├── ForeachMapStatement.java │ │ │ ├── FunctionDefineStatement.java │ │ │ ├── FunctionReferenceExpression.java │ │ │ ├── FunctionalExpression.java │ │ │ ├── IfStatement.java │ │ │ ├── IncludeStatement.java │ │ │ ├── InterruptableNode.java │ │ │ ├── MapExpression.java │ │ │ ├── MatchExpression.java │ │ │ ├── Node.java │ │ │ ├── ObjectCreationExpression.java │ │ │ ├── PrintStatement.java │ │ │ ├── PrintlnStatement.java │ │ │ ├── ResultVisitor.java │ │ │ ├── ReturnStatement.java │ │ │ ├── Statement.java │ │ │ ├── TernaryExpression.java │ │ │ ├── UnaryExpression.java │ │ │ ├── UseStatement.java │ │ │ ├── ValueExpression.java │ │ │ ├── VariableExpression.java │ │ │ ├── Visitor.java │ │ │ └── WhileStatement.java │ │ ├── error │ │ │ ├── ParseError.java │ │ │ ├── ParseErrors.java │ │ │ └── ParseErrorsFormatterStage.java │ │ ├── linters │ │ │ ├── AssignValidator.java │ │ │ ├── DefaultFunctionsOverrideValidator.java │ │ │ ├── IncludeSourceValidator.java │ │ │ ├── LintVisitor.java │ │ │ ├── LinterResult.java │ │ │ ├── LinterResults.java │ │ │ ├── LinterStage.java │ │ │ └── LoopStatementsValidator.java │ │ ├── optimization │ │ │ ├── ConstantFolding.java │ │ │ ├── ConstantPropagation.java │ │ │ ├── DeadCodeElimination.java │ │ │ ├── ExpressionSimplification.java │ │ │ ├── InstructionCombining.java │ │ │ ├── Optimizable.java │ │ │ ├── OptimizationStage.java │ │ │ ├── OptimizationVisitor.java │ │ │ ├── SummaryOptimization.java │ │ │ ├── VariableInfo.java │ │ │ └── VariablesGrabber.java │ │ └── visitors │ │ │ ├── AbstractVisitor.java │ │ │ ├── FunctionAdder.java │ │ │ ├── ModuleDetector.java │ │ │ ├── PrintVisitor.java │ │ │ ├── VariablePrinter.java │ │ │ └── VisitorUtils.java │ │ └── stages │ │ ├── ExecutionStage.java │ │ ├── FunctionAddingStage.java │ │ ├── LexerStage.java │ │ └── ParserStage.java │ └── test │ ├── java │ ├── com │ │ └── annimon │ │ │ └── ownlang │ │ │ └── parser │ │ │ ├── LexerBenchmarkTest.java │ │ │ ├── LexerPositionsTest.java │ │ │ ├── LexerTest.java │ │ │ ├── LexerValidDataProvider.java │ │ │ ├── MockOUnitStage.java │ │ │ ├── ParserBenchmarkTest.java │ │ │ ├── ParserTest.java │ │ │ ├── ProgramsBenchmarkTest.java │ │ │ ├── ProgramsTest.java │ │ │ ├── TestDataUtil.java │ │ │ └── ast │ │ │ ├── ASTHelper.java │ │ │ ├── OperatorExpressionTest.java │ │ │ ├── ValueExpressionTest.java │ │ │ └── VariableExpressionTest.java │ └── interop │ │ └── Data.java │ └── resources │ ├── benchmarks │ ├── calculator.own │ └── useStatement.own │ ├── expressions │ ├── assignmentExpression.own │ ├── binaryExpressionOnNumbers.own │ ├── binaryExpressionOnStrings.own │ ├── binaryUnaryExpr.own │ ├── foreachKeyValue.own │ ├── foreachValue.own │ ├── functionReference.own │ ├── include.own │ ├── includeClass.own.txt │ ├── includeParseErrorSource.own.txt │ ├── matchExpression.own │ ├── nullCoalesce.own │ ├── unaryExpressionOnStrings.own │ └── varFuncSameName.own │ ├── modules │ ├── base64 │ │ └── base64.own │ ├── date │ │ ├── compareDates.own │ │ ├── dateFormat.own │ │ ├── dateParse.own │ │ └── newDate.own │ ├── files │ │ └── files.own │ ├── functional │ │ ├── chain.own │ │ ├── foreach.own │ │ ├── groupby.own │ │ ├── stream.own │ │ └── tomap.own │ ├── gzip │ │ └── gzipBytes.own │ ├── java │ │ └── classes.own │ ├── regex │ │ ├── match.own │ │ └── replaceCallback.own │ ├── std │ │ ├── arraySplice.own │ │ ├── default.own │ │ ├── getBytes.own │ │ ├── indexOf.own │ │ ├── lastIndexOf.own │ │ ├── parseInt.own │ │ ├── parseLong.own │ │ ├── range.own │ │ ├── stringFromBytes.own │ │ ├── stripMargin.own │ │ ├── toHexString.own │ │ └── try.own │ └── yaml │ │ ├── yamldecode.own │ │ └── yamlencode.own │ └── other │ ├── arrayFunctions.own │ ├── classScope.own │ ├── functionChain.own │ ├── recursion.own │ ├── scope.own │ ├── stringFunctions.own │ ├── types.own │ └── useStatementScope.own ├── ownlang-utils ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── annimon │ └── ownlang │ └── utils │ ├── OptimizationDumper.java │ ├── Repl.java │ ├── Sandbox.java │ ├── TimeMeasurement.java │ └── repl │ ├── JLineConsole.java │ ├── OwnLangCompleter.java │ ├── ReplConsole.java │ └── SystemConsole.java ├── program.own ├── proguard.properties └── settings.gradle /.gitattributes: -------------------------------------------------------------------------------- 1 | *.own linguist-language=Scala 2 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: [ "latest" ] 6 | pull_request: 7 | branches: [ "latest" ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | java: [ '17', '21' ] 15 | name: Test 16 | steps: 17 | - uses: actions/checkout@v4 18 | - name: Set up JDK ${{ matrix.java }} 19 | uses: actions/setup-java@v4 20 | with: 21 | java-version: ${{ matrix.java }} 22 | distribution: 'temurin' 23 | - name: Setup Gradle 24 | uses: gradle/actions/setup-gradle@v4 25 | - name: Run tests 26 | run: ./gradlew test 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.gradle/ 2 | /.idea/ 3 | /.nb-gradle/ 4 | **/build/ 5 | /out/ 6 | /store/ 7 | /optimizations/ 8 | OwnLang.iml 9 | *.sh -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gradle:8.13.0-jdk21-alpine AS cache 2 | RUN mkdir -p /home/gradle/cache_home 3 | ENV GRADLE_USER_HOME=/home/gradle/cache_home 4 | COPY build.gradle /home/gradle/java-code/ 5 | WORKDIR /home/gradle/java-code 6 | RUN GRADLE_OPTS="-Xmx256m" gradle build --build-cache --stacktrace -i --no-daemon 7 | 8 | FROM gradle:8.13.0-jdk21-alpine AS builder 9 | COPY --from=cache /home/gradle/cache_home /home/gradle/.gradle 10 | COPY . /usr/src/java-code 11 | WORKDIR /usr/src/java-code 12 | RUN GRADLE_OPTS="-Xmx256m" gradle shadowJar --build-cache --stacktrace --no-daemon 13 | 14 | FROM eclipse-temurin:21-jre-alpine 15 | WORKDIR /opt/ownlang 16 | ENV PATH /opt/ownlang:$PATH 17 | COPY --from=builder /usr/src/java-code/ownlang-desktop/build/libs/*.jar libs/OwnLang.jar 18 | COPY --from=builder /usr/src/java-code/modules/jdbc/build/libs/*.jar modules/ 19 | COPY --from=builder /usr/src/java-code/modules/server/build/libs/*.jar modules/ 20 | COPY --from=builder /usr/src/java-code/modules/socket/build/libs/*.jar modules/ 21 | COPY dist/own . 22 | COPY dist/ownlang . 23 | RUN chmod +x own ownlang 24 | ENTRYPOINT ["ownlang"] 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2015-2016 Victor Melnik 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | versions = [ 3 | project: '2.1.0', 4 | 5 | json: '20240303', // org.json:json 6 | snakeyaml: '1.33', // org.yaml:snakeyaml 7 | okhttp: '4.12.0', // com.squareup.okhttp3:okhttp 8 | socket: '2.1.1', // io.socket:socket.io-client 9 | jline: '2.14.6', // jline:jline 10 | 11 | javalin: '6.3.0', // io.javalin:javalin 12 | slf4j: '2.0.16', // org.slf4j:slf4j-simple 13 | jackson: '2.18.0', // com.fasterxml.jackson.core:jackson-databind 14 | 15 | junit: '5.11.2', // org.junit:junit-bom 16 | jmh: '1.37', // org.openjdk.jmh:jmh-core 17 | assertj: '3.26.3' // org.assertj:assertj-core 18 | ] 19 | } 20 | 21 | allprojects { 22 | repositories { 23 | mavenCentral() 24 | } 25 | 26 | gradle.projectsEvaluated { 27 | tasks.withType(JavaCompile).tap { 28 | configureEach { 29 | [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' 30 | options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /dist/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aNNiMON/Own-Programming-Language-Tutorial/f9c2bff15962b60ddc2d5b94a7f35d18e9b06900/dist/icon.png -------------------------------------------------------------------------------- /dist/own: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | ownlang run own "$@" -------------------------------------------------------------------------------- /dist/own.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | ownlang run own %* -------------------------------------------------------------------------------- /dist/ownlang: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | (set -o igncr) 2>/dev/null && set -o igncr; # cygwin encoding fix 3 | 4 | # resolve links - $0 may be a softlink 5 | PRG="$0" 6 | while [ -h "$PRG" ] ; do 7 | ls=`ls -ld "$PRG"` 8 | link=`expr "$ls" : '.*-> \(.*\)$'` 9 | if expr "$link" : '/.*' > /dev/null; then 10 | PRG="$link" 11 | else 12 | PRG=`dirname "$PRG"`/"$link" 13 | fi 14 | done 15 | PRGDIR=`dirname "$PRG"` 16 | 17 | _classpath=".:$PRGDIR/OwnLang.jar" 18 | if [ `uname -a | grep -i -c cygwin` -ne 0 ]; then # Cygwin, translate the path 19 | for k in "$PRGDIR"/modules/*.jar 20 | do 21 | _classpath="${_classpath};`cygpath -w ${k}`" 22 | done 23 | for k in "$PRGDIR"/libs/*.jar 24 | do 25 | _classpath="${_classpath};`cygpath -w ${k}`" 26 | done 27 | else 28 | for k in "$PRGDIR"/modules/*.jar 29 | do 30 | _classpath="${_classpath}:${k}" 31 | done 32 | for k in "$PRGDIR"/libs/*.jar 33 | do 34 | _classpath="${_classpath}:${k}" 35 | done 36 | fi 37 | 38 | java -cp "${_classpath}" com.annimon.ownlang.Main "$@" -------------------------------------------------------------------------------- /dist/ownlang-runner.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | set JAVA=java 3 | %JAVA% -cp "%~dp0/*;%~dp0modules/*;%~dp0libs/*" com.annimon.ownlang.Main --file %1 4 | pause -------------------------------------------------------------------------------- /dist/ownlang.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set JAVA=java 4 | %JAVA% ^ 5 | -cp "%~dp0/*;%~dp0modules/*;%~dp0libs/*" com.annimon.ownlang.Main %* 6 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .temp 3 | .cache 4 | docs/.vuepress/configs/modules.js 5 | docs/.vuepress/dist/ 6 | docs/*/modules/ 7 | -------------------------------------------------------------------------------- /docs/docs/.vuepress/components/Scope.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 18 | 19 | -------------------------------------------------------------------------------- /docs/docs/.vuepress/components/Since.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /docs/docs/.vuepress/configs/navbar.js: -------------------------------------------------------------------------------- 1 | import pages from './pages' 2 | 3 | let navbar = {} 4 | for (let lang of ['en', 'ru']) { 5 | let config = [] 6 | for (let [relativePath, entry] of Object.entries(pages)) { 7 | const path = '/' + lang + relativePath 8 | config.push({ 9 | text: entry.text[lang], 10 | children: entry.pages.map(r => path + r) 11 | }) 12 | } 13 | navbar[lang] = config 14 | } 15 | 16 | export const navbarConfig = navbar -------------------------------------------------------------------------------- /docs/docs/.vuepress/configs/pages.js: -------------------------------------------------------------------------------- 1 | import modules from './modules' 2 | export default { 3 | '/': { 4 | text: {'en': 'OwnLang', 'ru': 'OwnLang'}, 5 | pages: [ 6 | 'README.md', 7 | 'links.md', 8 | 'changelog.md', 9 | ] 10 | }, 11 | 12 | '/basics/': { 13 | text: {'en': 'Basics', 'ru': 'Основы'}, 14 | pages: [ 15 | 'comments.md', 16 | 'strings.md', 17 | 'types.md', 18 | 'loops.md', 19 | 'functions.md', 20 | 'destructuring_assignment.md', 21 | 'pattern_matching.md', 22 | 'string_functions.md', 23 | 'array_functions.md' 24 | ] 25 | }, 26 | 27 | '/modules/': { 28 | text: {'en': 'Modules', 'ru': 'Модули'}, 29 | pages: modules 30 | } 31 | } -------------------------------------------------------------------------------- /docs/docs/.vuepress/configs/sidebar.js: -------------------------------------------------------------------------------- 1 | import pages from './pages' 2 | 3 | let sidebar = {} 4 | for (let lang of ['en', 'ru']) { 5 | let config = {} 6 | for (let [relativePath, entry] of Object.entries(pages)) { 7 | const path = '/' + lang + relativePath 8 | config[path] = (path in config) ? config[path] : [] 9 | config[path].push({ 10 | text: entry.text[lang], 11 | children: entry.pages.map(r => path + r) 12 | }) 13 | } 14 | sidebar[lang] = config 15 | } 16 | 17 | export const sidebarConfig = sidebar -------------------------------------------------------------------------------- /docs/docs/.vuepress/styles/palette.scss: -------------------------------------------------------------------------------- 1 | :root { 2 | --c-brand: #f15d15; 3 | --c-brand-light: #ff9562; 4 | --c-badge-danger: #f63f3f; 5 | --c-badge-warning: #d0af01; 6 | 7 | --usc-since: var(--c-badge-warning); 8 | --usc-scope: var(--c-brand); 9 | } 10 | html.dark { 11 | --c-brand: #e1792d; 12 | --c-brand-light: #ff8e3d; 13 | --c-badge-danger: #d94657; 14 | --c-badge-danger-text: #160304; 15 | } -------------------------------------------------------------------------------- /docs/docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | title: OwnLang 4 | heroText: OwnLang 5 | tagline: Dynamic functional programming language 6 | actions: 7 | - text: English 8 | link: /en/ 9 | type: primary 10 | - text: Русский 11 | link: /ru/ 12 | type: primary 13 | footer: © 2024 aNNiMON 14 | --- 15 | -------------------------------------------------------------------------------- /docs/docs/code/basics/destructuring_assignment1.own: -------------------------------------------------------------------------------- 1 | arr = ["a", "b", "c"] 2 | extract(var1, var2, var3) = arr 3 | print var1 // a 4 | print var2 // b 5 | print var3 // c -------------------------------------------------------------------------------- /docs/docs/code/basics/destructuring_assignment2.own: -------------------------------------------------------------------------------- 1 | arr = ["a", "b", "c"] 2 | var1 = arr[0] 3 | var2 = arr[1] 4 | var3 = arr[2] -------------------------------------------------------------------------------- /docs/docs/code/basics/destructuring_assignment3.own: -------------------------------------------------------------------------------- 1 | map = {"key1": 1, "test": "text"} 2 | extract(var1, var2) = map 3 | println var1 // [key1, 1] 4 | println var2 // [test, text] -------------------------------------------------------------------------------- /docs/docs/code/basics/destructuring_assignment4.own: -------------------------------------------------------------------------------- 1 | extract(x, , z) = [93, 58, 90] 2 | println x // 93 3 | println z // 90 -------------------------------------------------------------------------------- /docs/docs/code/basics/fibonacci.own: -------------------------------------------------------------------------------- 1 | def fibonacci(count) { 2 | def fib(n) { 3 | if n < 2 return n 4 | return fib(n-2) + fib(n-1) 5 | } 6 | return fib(count) 7 | } 8 | 9 | println fibonacci(10) // 55 -------------------------------------------------------------------------------- /docs/docs/code/basics/loops1.own: -------------------------------------------------------------------------------- 1 | arr = [1, 2, 3, 4] 2 | for v : arr { 3 | println v 4 | } 5 | 6 | map = {"key1": 1, "key2": 2} 7 | for key, value : map 8 | println key + " = " value 9 | } -------------------------------------------------------------------------------- /docs/docs/code/basics/pattern_matching1.own: -------------------------------------------------------------------------------- 1 | x = 2 2 | print match x { 3 | case 1: "One" 4 | case 2: "Two" 5 | case "str": "String" 6 | case _: "Unknown" 7 | } -------------------------------------------------------------------------------- /docs/docs/code/basics/pattern_matching2.own: -------------------------------------------------------------------------------- 1 | x = "str" 2 | match x { 3 | case "": { 4 | println "Empty string" 5 | } 6 | case "str": { 7 | println "String!" 8 | } 9 | } -------------------------------------------------------------------------------- /docs/docs/code/basics/pattern_matching3.own: -------------------------------------------------------------------------------- 1 | def test(x) = match x { 2 | case a: "case a: " + a 3 | case b: "case b: " + b 4 | case c: "case c: " + c 5 | } 6 | a = 10 7 | b = 20 8 | println test(15) // case c: 15 9 | println test(20) // case b: 20 10 | println test("test") // case c: test -------------------------------------------------------------------------------- /docs/docs/code/basics/pattern_matching4.own: -------------------------------------------------------------------------------- 1 | def test(x) = match x { 2 | case x if x < 0: "(-∞ .. 0)" 3 | case x if x > 0: "(0 .. +∞)" 4 | case x: "0" 5 | } 6 | 7 | println test(-10) // (-∞ .. 0) 8 | println test(0) // 0 9 | println test(10) // (0 .. +∞) -------------------------------------------------------------------------------- /docs/docs/code/basics/pattern_matching5.own: -------------------------------------------------------------------------------- 1 | def arrayRecursive(arr) = match arr { 2 | case [head :: tail]: "[" + head + ", " + arrayRecursive(tail) + "]" 3 | case []: "[]" 4 | case last: "[" + last + ", []]" 5 | } 6 | 7 | println arrayRecursive([1, 2, 3, 4, 5, 6, 7]) // [1, [2, [3, [4, [5, [6, [7, []]]]]]]] -------------------------------------------------------------------------------- /docs/docs/code/basics/pattern_matching6.own: -------------------------------------------------------------------------------- 1 | for i = 1, i <= 100, i++ { 2 | println match [i % 3 == 0, i % 5 == 0] { 3 | case (true, false): "Fizz" 4 | case (false, true): "Buzz" 5 | case (true, true): "FizzBuzz" 6 | case _: i 7 | } 8 | } -------------------------------------------------------------------------------- /docs/docs/code/basics/string_functions1.own: -------------------------------------------------------------------------------- 1 | str = " ababcaab " 2 | println indexOf(str, "abc") 3 | println str.indexOf("abc") 4 | 5 | def isBlank(s) = s.trim().isEmpty() 6 | println isBlank(str) 7 | println str.isBlank() -------------------------------------------------------------------------------- /docs/docs/code/functional_en.own: -------------------------------------------------------------------------------- 1 | use std, functional 2 | 3 | nums = [1,2,3,4,5,6,7,8,9,10] 4 | nums = filter(nums, def(x) = x % 2 == 0) 5 | // Squares of even numbers 6 | squares = map(nums, def(x) = x * x) 7 | foreach(squares, ::echo) 8 | // Sum of squares 9 | sum = reduce(squares, 0, def(x, y) = x + y) 10 | println "Sum: " + sum 11 | // Same using stream 12 | println "Sum: " + stream(range(1, 11)) 13 | .filter(def(x) = x % 2 == 0) 14 | .map(def(x) = x * x) 15 | .reduce(0, def(x, y) = x + y) 16 | -------------------------------------------------------------------------------- /docs/docs/code/functional_ru.own: -------------------------------------------------------------------------------- 1 | use std, functional 2 | 3 | nums = [1,2,3,4,5,6,7,8,9,10] 4 | nums = filter(nums, def(x) = x % 2 == 0) 5 | // Квадраты чётных чисел 6 | squares = map(nums, def(x) = x * x) 7 | foreach(squares, ::echo) 8 | // Сумма квадратов 9 | sum = reduce(squares, 0, def(x, y) = x + y) 10 | println "Сумма: " + sum 11 | // То же самое с использованием stream 12 | println "Сумма: " + stream(range(1, 11)) 13 | .filter(def(x) = x % 2 == 0) 14 | .map(def(x) = x * x) 15 | .reduce(0, def(x, y) = x + y) 16 | -------------------------------------------------------------------------------- /docs/docs/code/high_order_functions_en.own: -------------------------------------------------------------------------------- 1 | operations = { 2 | "+" : def(a,b) = a+b, 3 | "-" : def(a,b) = a-b, 4 | "*" : def(a,b) = a*b, 5 | "/" : ::division 6 | } 7 | def division(v1, v2) { 8 | if (v2 == 0) return "error: division by zero" 9 | return v1 / v2 10 | } 11 | 12 | for operation : operations { 13 | println operation(2, 3) 14 | } -------------------------------------------------------------------------------- /docs/docs/code/high_order_functions_ru.own: -------------------------------------------------------------------------------- 1 | operations = { 2 | "+" : def(a,b) = a+b, 3 | "-" : def(a,b) = a-b, 4 | "*" : def(a,b) = a*b, 5 | "/" : ::division 6 | } 7 | def division(v1, v2) { 8 | if (v2 == 0) return "ошибка: деление на ноль" 9 | return v1 / v2 10 | } 11 | 12 | for operation : operations { 13 | println operation(2, 3) 14 | } -------------------------------------------------------------------------------- /docs/docs/code/http_en.own: -------------------------------------------------------------------------------- 1 | use std, http, functional 2 | 3 | // GET request 4 | http("https://api.github.com/events", def(r) { 5 | use json 6 | events = jsondecode(r) 7 | }) 8 | 9 | // POST request 10 | http("http://jsonplaceholder.typicode.com/users", "POST", { 11 | "name": "OwnLang", 12 | "versionCode": 10 13 | }, ::echo) 14 | 15 | // PATCH request 16 | http("http://jsonplaceholder.typicode.com/users/2", "PATCH", {"name": "Patched Name"}, ::patch_callback) 17 | 18 | def patch_callback(v) { 19 | println v 20 | } 21 | -------------------------------------------------------------------------------- /docs/docs/code/http_ru.own: -------------------------------------------------------------------------------- 1 | use std, http, functional 2 | 3 | // GET-запрос 4 | http("https://api.github.com/events", def(r) { 5 | use json 6 | events = jsondecode(r) 7 | }) 8 | 9 | // POST-запрос 10 | http("http://jsonplaceholder.typicode.com/users", "POST", { 11 | "name": "OwnLang", 12 | "versionCode": 10 13 | }, ::echo) 14 | 15 | // PATCH-запрос 16 | http("http://jsonplaceholder.typicode.com/users/2", "PATCH", {"name": "Патч"}, ::patch_callback) 17 | 18 | def patch_callback(v) { 19 | println v 20 | } 21 | -------------------------------------------------------------------------------- /docs/docs/code/operator_overloading.own: -------------------------------------------------------------------------------- 1 | use std, types, math 2 | 3 | def `..`(a, b) = range(a, b - 1) 4 | def `**`(a, b) = int(pow(a, b)) 5 | for y : 1 .. 10 { 6 | println sprintf("2 ^ %d = %d", y, 2 ** y) 7 | } -------------------------------------------------------------------------------- /docs/docs/code/pattern_matching.own: -------------------------------------------------------------------------------- 1 | def factorial(n) = match n { 2 | case 0: 1 3 | case n if n < 0: 0 4 | case _: n * factorial(n - 1) 5 | } 6 | 7 | def fizzbuzz(limit = 100) { 8 | for i = 1, i <= limit, i++ { 9 | println match [i % 3 == 0, i % 5 == 0] { 10 | case (true, false): "Fizz" 11 | case (false, true): "Buzz" 12 | case (true, true): "FizzBuzz" 13 | case _: i 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /docs/docs/en/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | OwnLang — dynamic functional programming language inspired by Scala and Python. Available for PC and Android devices. 4 | 5 | ## Key features 6 | 7 | ### Higher-order functions 8 | 9 | Functions are values, so we can store them to variables for operating. 10 | 11 | @[code](../code/high_order_functions_en.own) 12 | 13 | ### Pattern Matching 14 | 15 | Pattern matching with value pattern, tuple pattern, list pattern and optional condition. 16 | 17 | @[code](../code/pattern_matching.own) 18 | 19 | ### Functional data operations 20 | 21 | Operate data in functional style. 22 | 23 | @[code](../code/functional_en.own) 24 | 25 | ### Operator overloading 26 | 27 | Why not? 28 | 29 | @[code](../code/operator_overloading.own) 30 | 31 | ### Network module 32 | 33 | Easy async HTTP requests with `http` module. 34 | 35 | @[code](../code/http_en.own) -------------------------------------------------------------------------------- /docs/docs/en/basics/README.md: -------------------------------------------------------------------------------- 1 | # Basics 2 | 3 | * [Comments](comments.md) 4 | * [Strings](strings.md) 5 | * [Types](types.md) 6 | * [Loops](loops.md) 7 | * [Functions definition](functions.md) 8 | * [Destructuring assignment](destructuring_assignment.md) 9 | * [Pattern matching](pattern_matching.md) 10 | * [String functions](string_functions.md) 11 | * [Array functions](array_functions.md) 12 | -------------------------------------------------------------------------------- /docs/docs/en/basics/array_functions.md: -------------------------------------------------------------------------------- 1 | # Array functions 2 | 3 | Fields: 4 | - `length` - number of elements of the array 5 | 6 | Functions: 7 | - `isEmpty()` - returns true, if the array is empty 8 | - `joinToString(delimiter = "", prefix = "", suffix = "")` - joins array into a string 9 | -------------------------------------------------------------------------------- /docs/docs/en/basics/comments.md: -------------------------------------------------------------------------------- 1 | # Comments 2 | 3 | ```own 4 | // Line comment 5 | /* multiline 6 | comment 7 | */ 8 | print /*inner comment*/ "Text" 9 | ``` -------------------------------------------------------------------------------- /docs/docs/en/basics/destructuring_assignment.md: -------------------------------------------------------------------------------- 1 | # Destructuring assignment 2 | 3 | Destructuring assignment allows to define multiple variables for each element of an array or map. 4 | 5 | For arrays, value is assigned to variable: 6 | 7 | @[code](../../code/basics/destructuring_assignment1.own) 8 | 9 | Which is equivalent to: 10 | 11 | @[code](../../code/basics/destructuring_assignment2.own) 12 | 13 | For maps, key and value are assigned to variable: 14 | 15 | @[code](../../code/basics/destructuring_assignment3.own) 16 | 17 | To skip value just leave argument empty: 18 | 19 | @[code](../../code/basics/destructuring_assignment4.own) 20 | -------------------------------------------------------------------------------- /docs/docs/en/basics/functions.md: -------------------------------------------------------------------------------- 1 | # Functions definition 2 | 3 | To define function uses the `def` keyword: 4 | 5 | ```own 6 | def function(arg1, arg2) { 7 | println arg1 8 | } 9 | ``` 10 | 11 | ## Shorthand definition 12 | 13 | There is short syntax fot function body: 14 | 15 | ```own 16 | def repeat(str, count) = str * count 17 | ``` 18 | 19 | Which is equivalent to: 20 | 21 | ```own 22 | def repeat(str, count) { 23 | return str * count 24 | } 25 | ``` 26 | 27 | ## Default arguments 28 | 29 | Function arguments can have default values. 30 | 31 | ```own 32 | def repeat(str, count = 5) = str * count 33 | ``` 34 | 35 | In this case only `str` argument is required. 36 | 37 | ```own 38 | repeat("*") // ***** 39 | repeat("+", 3) // +++ 40 | ``` 41 | 42 | Default arguments can't be declared before required arguments. 43 | 44 | ```own 45 | def repeat(str = "*", count) = str * count 46 | ``` 47 | 48 | Causes parsing error: `ParseError on line 1: Required argument cannot be after optional` 49 | 50 | ## Inner functions 51 | 52 | You can define function in other function. 53 | 54 | @[code](../../code/basics/fibonacci.own) 55 | -------------------------------------------------------------------------------- /docs/docs/en/basics/string_functions.md: -------------------------------------------------------------------------------- 1 | # String functions 2 | 3 | Fields: 4 | - `length` - string length 5 | - `lower` - lower case string 6 | - `upper` - upper case string 7 | - `chars` - ASCII characters array 8 | 9 | Functions: 10 | - `trim()` - removes any leading and trailing whitespaces in string 11 | - `startsWith(str, offset = 0)` - checks whether the string starts with the substring str at offset 12 | - `endsWith(str)` - checks whether the string ends with the str 13 | - `matches(regex)` - checks whether the string matches regex pattern 14 | - `contains(str)` - checks whether the string contains substring str 15 | - `equalsIgnoreCase(str)` - checks equality of two strings ignore case (tEsT = TEST) 16 | - `isEmpty()` - returns true, if the string is empty 17 | 18 | In addition, there are automatic function extensions available if the function accepts a string as the first argument: 19 | 20 | @[code](../../code/basics/string_functions1.own) 21 | -------------------------------------------------------------------------------- /docs/docs/en/basics/strings.md: -------------------------------------------------------------------------------- 1 | # Strings 2 | 3 | Strings are defined in double quotes and can be multiline. Escaping Unicode characters is also supported.: 4 | 5 | ```own 6 | str = "\n\tThis is 7 | \tmultiline 8 | \ttext 9 | " 10 | ``` 11 | 12 | `print` and `println` operators are used to output text. -------------------------------------------------------------------------------- /docs/docs/en/basics/types.md: -------------------------------------------------------------------------------- 1 | # Types 2 | 3 | OwnLang types are: 4 | 5 | * Number - numbers (integer, float) 6 | * String - strings 7 | * Array - arrays 8 | * Map - objects (an associative arrays) 9 | * Function - functions 10 | * Class 11 | 12 | Since OwnLang is dynamic programming language, which means that explicitly declare the types is not necessary. 13 | 14 | ```own 15 | x = 10 // integer 16 | y = 1.61803 // float 17 | z = "abcd" // string 18 | ``` 19 | 20 | If some function requires string as argument, but number was passed, then numeric value will automatically converts to string. 21 | 22 | ```own 23 | x = 90 24 | print x // Ok, 90 converts to "90" 25 | ``` -------------------------------------------------------------------------------- /docs/docs/en/links.md: -------------------------------------------------------------------------------- 1 | # Links 2 | 3 | ## Downloads 4 | 5 | Android: [Free](https://play.google.com/store/apps/details?id=com.annimon.ownlang.free) / [Pro](https://play.google.com/store/apps/details?id=com.annimon.ownlang) 6 | PC / Netbeans Plugin / etc: [GitHub Releases](https://github.com/aNNiMON/Own-Programming-Language-Tutorial/releases) 7 | Source code: [GitHub](https://github.com/aNNiMON/Own-Programming-Language-Tutorial) 8 | 9 | Also available as AUR package: 10 | 11 | ``` 12 | paru -S ownlang 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/docs/ru/README.md: -------------------------------------------------------------------------------- 1 | # Возможности 2 | 3 | OwnLang — скриптовый функциональный язык программирования с динамической типизацией для ПК и Android устройств. 4 | 5 | ## Ключевые особенности 6 | 7 | ### Функции высшего порядка 8 | 9 | Функции выступают как значения, а значит мы можем сохранять их в переменные для дальнейшего использования. 10 | 11 | @[code](../code/high_order_functions_ru.own) 12 | 13 | ### Pattern Matching 14 | 15 | Сопоставление по образцу с шаблоном значений, шаблоном кортежей, шаблоном списков и дополнительным сравнением. 16 | 17 | @[code](../code/pattern_matching.own) 18 | 19 | ### Функциональные операции над данными 20 | 21 | Оперирование данными в функциональном стиле. 22 | 23 | @[code](../code/functional_ru.own) 24 | 25 | ### Перегрузка операторов 26 | 27 | Почему бы и нет? 28 | 29 | @[code](../code/operator_overloading.own) 30 | 31 | ### Модуль для работы с сетью Интернет 32 | 33 | Простые асинхронные HTTP-запросы с модулем `http`. 34 | 35 | @[code](../code/http_ru.own) -------------------------------------------------------------------------------- /docs/docs/ru/basics/README.md: -------------------------------------------------------------------------------- 1 | # Синтаксис и основы языка 2 | 3 | * [Комментарии](comments.md) 4 | * [Строки](strings.md) 5 | * [Типы](types.md) 6 | * [Циклы](loops.md) 7 | * [Определение функций](functions.md) 8 | * [Реструктуризующее присваивание](destructuring_assignment.md) 9 | * [Pattern matching](pattern_matching.md) (сопоставление с образцом) 10 | * [Функции строк](string_functions.md) 11 | * [Функции массивов](array_functions.md) -------------------------------------------------------------------------------- /docs/docs/ru/basics/array_functions.md: -------------------------------------------------------------------------------- 1 | # Функции массивов 2 | 3 | Поля: 4 | - `length` - количество элементов массива 5 | 6 | Функции: 7 | - `isEmpty()` - возвращает true, если массив пуст 8 | - `joinToString(delimiter = "", prefix = "", suffix = "")` - склеивает массив в строку 9 | -------------------------------------------------------------------------------- /docs/docs/ru/basics/comments.md: -------------------------------------------------------------------------------- 1 | # Комментарии 2 | 3 | ```own 4 | // Однострочный комментарий 5 | /* многострочный 6 | комментарий 7 | */ 8 | print /*или так*/ "Текст" 9 | ``` -------------------------------------------------------------------------------- /docs/docs/ru/basics/destructuring_assignment.md: -------------------------------------------------------------------------------- 1 | # Реструктуризующее присваивание 2 | 3 | Реструктуризующее (деструктивное) присваивание позволяет определить сразу несколько переменных по каждому элементу массива или объекта. 4 | 5 | Для массивов, переменным присваивается значение. 6 | 7 | @[code](../../code/basics/destructuring_assignment1.own) 8 | 9 | Что равносильно: 10 | 11 | @[code](../../code/basics/destructuring_assignment2.own) 12 | 13 | Для объектов, переменным присваивается массив [ключ, значение] 14 | 15 | @[code](../../code/basics/destructuring_assignment3.own) 16 | 17 | Если нужно пропустить какое-либо значение, название переменной можно не писать: 18 | 19 | @[code](../../code/basics/destructuring_assignment4.own) -------------------------------------------------------------------------------- /docs/docs/ru/basics/functions.md: -------------------------------------------------------------------------------- 1 | # Определение функций 2 | 3 | Для определения функции используется ключевое слово `def`. Затем идёт имя, аргументы и тело функции. Пример: 4 | 5 | ```own 6 | def function(arg1, arg2) { 7 | println arg1 8 | } 9 | ``` 10 | 11 | ## Короткий синтаксис 12 | 13 | Возможен короткий синтаксис: 14 | 15 | ```own 16 | def repeat(str, count) = str * count 17 | ``` 18 | 19 | что равносильно: 20 | 21 | ```own 22 | def repeat(str, count) { 23 | return str * count 24 | } 25 | ``` 26 | 27 | ## Аргументы по умолчанию 28 | 29 | Аргументы функции могут иметь значения по умолчанию. 30 | 31 | ```own 32 | def repeat(str, count = 5) = str * count 33 | ``` 34 | 35 | В этом случае обязательным будет только аргумент `str` 36 | 37 | ```own 38 | repeat("*") // ***** 39 | repeat("+", 3) // +++ 40 | ``` 41 | 42 | Аргументы по умолчанию обязательно должны идти после обязательных аргументов, если такие были. 43 | 44 | ```own 45 | def repeat(str = "*", count) = str * count 46 | ``` 47 | 48 | Приведёт к ошибки парсинга: `ParseError on line 1: Required argument cannot be after optional` 49 | 50 | ## Внутренние функции 51 | 52 | Внутри функции можно объявить другую функцию. 53 | 54 | @[code](../../code/basics/fibonacci.own) -------------------------------------------------------------------------------- /docs/docs/ru/basics/string_functions.md: -------------------------------------------------------------------------------- 1 | # Функции строк 2 | 3 | Поля: 4 | - `length` - длина строки 5 | - `lower` - строка в нижнем регистре 6 | - `upper` - строка в верхнем регистре 7 | - `chars` - массив символов в виде ASCII-кодов 8 | 9 | Функции: 10 | - `trim()` - обрезает пробельные невидимые символы по обоим концам строки 11 | - `startsWith(str, offset = 0)` - проверяет, начинается ли строка с подстроки str в позиции offset 12 | - `endsWith(str)` - проверяет, заканчивается ли строка подстрокой str 13 | - `matches(regex)` - проверяет соответствие строки с заданным шаблоном 14 | - `contains(str)` - проверяет, содержится ли в строке подстрока str 15 | - `equalsIgnoreCase(str)` - проверяет, равны ли строки вне зависимости от регистра (tEsT = TEST) 16 | - `isEmpty()` - возвращает true, если строка пустая 17 | 18 | Кроме того, доступны автоматические функции-расширения, если функция принимает в качестве первого аргумента строку: 19 | 20 | @[code](../../code/basics/string_functions1.own) 21 | -------------------------------------------------------------------------------- /docs/docs/ru/basics/strings.md: -------------------------------------------------------------------------------- 1 | # Строки 2 | 3 | Строки задаются в двойных кавычках и могут быть многострочные. Поддерживается юникод и экранирование символов: 4 | 5 | ```own 6 | str = "\n\tЭто 7 | \tмногострочный 8 | \tтекст 9 | " 10 | ``` 11 | 12 | Для вывода строк есть два оператора `print` и `println` -------------------------------------------------------------------------------- /docs/docs/ru/basics/types.md: -------------------------------------------------------------------------------- 1 | # Типы 2 | 3 | В OwnLang есть такие типы: 4 | 5 | * Number - числа (охватывает как целые, так и вещественные числа) 6 | * String - строки 7 | * Array - массивы 8 | * Map - объекты (ассоциативные массивы) 9 | * Function - функции 10 | * Class - классы 11 | 12 | Поскольку OwnLang - динамически типизируемый язык программирования, это значит, что явно объявлять типы не нужно. 13 | 14 | ```own 15 | x = 10 // целое число 16 | y = 1.61803 // вещественное число 17 | z = "abcd" // строка 18 | ``` 19 | 20 | Если какая-либо функция предполагает использование строк в качестве аргументов, а были переданы числа, то значения автоматически приведутся к строке. 21 | 22 | ```own 23 | x = 90 24 | print x 25 | ``` -------------------------------------------------------------------------------- /docs/docs/ru/links.md: -------------------------------------------------------------------------------- 1 | # Ссылки 2 | 3 | ## Загрузки 4 | 5 | Android: [Free](https://play.google.com/store/apps/details?id=com.annimon.ownlang.free) / [Pro](https://play.google.com/store/apps/details?id=com.annimon.ownlang) 6 | PC / плагин Netbeans / прочее: [GitHub Releases](https://github.com/aNNiMON/Own-Programming-Language-Tutorial/releases) 7 | Исходный код: [GitHub](https://github.com/aNNiMON/Own-Programming-Language-Tutorial) 8 | 9 | Также доступно в виде пакета в AUR: 10 | 11 | ``` 12 | paru -S ownlang 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ownlang-docs", 3 | "version": "1.0.0", 4 | "description": "OwnLang Documentation", 5 | "main": "index.js", 6 | "scripts": { 7 | "docs:dev": "vuepress dev docs", 8 | "docs:build": "vuepress build docs" 9 | }, 10 | "keywords": [ 11 | "documentation", 12 | "ownlang", 13 | "programming-language" 14 | ], 15 | "author": "aNNiMON", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "@vuepress/client": "2.0.0-rc.0", 19 | "@vuepress/plugin-prismjs": "2.0.0-rc.0", 20 | "@vuepress/plugin-register-components": "2.0.0-rc.0", 21 | "@vuepress/plugin-search": "2.0.0-rc.0", 22 | "@vuepress/utils": "2.0.0-rc.0", 23 | "prismjs": "^1.30.0", 24 | "vue": "^3.4.20", 25 | "vuepress": "2.0.0-rc.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /docs/src/modules/base64.yml: -------------------------------------------------------------------------------- 1 | name: base64 2 | scope: both 3 | desc: "Contains base64 encoding and decoding functions" 4 | desc_ru: "Содержит функции кодирования данных в base64 и наоборот" 5 | constants: 6 | - name: BASE64_URL_SAFE 7 | type: 1 8 | typeName: number 9 | value: '8' 10 | desc: 'Url safe encoding output' 11 | desc_ru: 'Вывод данных в безопасном для ссылок формате' 12 | functions: 13 | - name: base64decode 14 | args: 'data, type = 0' 15 | desc: 'decodes base64-encoded byte array or string into byte array' 16 | desc_ru: 'декодирует массив байт или строку, закодированную в base64, в массив байт' 17 | - name: base64encode 18 | args: 'data, type = 0' 19 | desc: 'encodes byte array or string into base64-encoded byte array' 20 | desc_ru: 'кодирует массив байт или строку в закодированный base64 массив байт' 21 | - name: base64encodeToString 22 | args: 'data, type = 0' 23 | desc: 'encodes byte array or string into base64-encoded string' 24 | desc_ru: 'кодирует массив байт или строку в закодированную base64 строку' -------------------------------------------------------------------------------- /docs/src/modules/downloader.yml: -------------------------------------------------------------------------------- 1 | name: downloader 2 | scope: both 3 | desc: "Contains functions for downloading large files" 4 | desc_ru: "Содержит функции для скачивания больших файлов" 5 | functions: 6 | - name: getContentLength 7 | args: 'url' 8 | desc: 'gets content length by sending HEAD request to the given url' 9 | desc_ru: 'получает значение заголовка Content-Length путём отправки HEAD-запроса на указанный url' 10 | - name: downloader 11 | args: 'downloadUrl, filePath, progressCallback = def() {}, bufferSize = 16384' 12 | desc: 'downloads file from `downloadUrl` to `filePath`' 13 | desc_ru: 'скачивает файл по адресу `downloadUrl` и сохраняет в `filePath`' 14 | example: |- 15 | use downloader, std 16 | 17 | MBYTES = 1048576.0 // 1024*1024 18 | url = "http://www.ovh.net/files/10Mb.dat" 19 | file = "10Mb.dat" 20 | 21 | downloader(url, file, def(progress, bytesDownloaded, bytesMax) { 22 | bar = "#" * (progress / 2) 23 | print sprintf("%-50s %d%% %.2f / %.2f MiB\\r", bar, progress, bytesDownloaded / MBYTES, bytesMax / MBYTES) 24 | }) -------------------------------------------------------------------------------- /docs/src/modules/json.yml: -------------------------------------------------------------------------------- 1 | name: json 2 | scope: "both" 3 | desc: "Contains functions for working with the json format" 4 | desc_ru: "Содержит функции преобразования данных в формат json и наоборот" 5 | constants: [] 6 | functions: 7 | - name: "jsondecode" 8 | args: "data" 9 | desc: "converts data to json string" 10 | desc_ru: "преобразует переданные данные в строку в формате json" 11 | example: |- 12 | use json 13 | print jsondecode("{\"key1\":1,\"key2\":[1,2,3],\"key3\":\"text\"}") // {key2=[1, 2, 3], key3=text, key1=1} 14 | - name: "jsonencode" 15 | args: "jsonString, indent = 0" 16 | desc: "converts string to data" 17 | desc_ru: "преобразует строку в формате json в данные" 18 | example: |- 19 | use json 20 | data = { 21 | "key1": 1, 22 | "key2": [1, 2, 3], 23 | "key3": "text" 24 | } 25 | print jsonencode(data) // {"key1":1,"key2":[1,2,3],"key3":"text"} -------------------------------------------------------------------------------- /docs/src/modules/yaml.yml: -------------------------------------------------------------------------------- 1 | name: yaml 2 | scope: desktop 3 | desc: "Contains functions for working with the yaml format" 4 | desc_ru: "Содержит функции преобразования данных в формат yaml и наоборот" 5 | constants: [] 6 | functions: 7 | - name: yamldecode 8 | args: "data" 9 | desc: "converts data to yaml string" 10 | desc_ru: "преобразует переданные данные в строку в формате yaml" 11 | - name: yamlencode 12 | args: "yamlString" 13 | desc: "converts yaml string to data" 14 | desc_ru: "преобразует строку в формате yaml в данные" -------------------------------------------------------------------------------- /editors/README.md: -------------------------------------------------------------------------------- 1 | # Syntax for Editors 2 | 3 | ## Intellij IDEA 4 | 5 | 1. Open an `idea` folder 6 | 2. Add all files and folders to zip archive, e.g. `settings.zip` 7 | 3. File -> Manage IDE Settings -> Import. Select your zip file. 8 | 9 | ## Prism.js 10 | 11 | ```javascript 12 | import Prism from 'prismjs'; 13 | import definePrismOwnLang from './prismjs/own-language.js' 14 | definePrismOwnLang(Prism) 15 | ``` 16 | 17 | ## GTKSourceView 18 | 19 | Place `ownlang.lang` in `/usr/share/gtksourceview-3.0/language-specs/ownlang.lang` -------------------------------------------------------------------------------- /editors/highlighjs/own.js: -------------------------------------------------------------------------------- 1 | export default function(hljs) { 2 | const STRING = { 3 | className: 'string', 4 | variants: [{ 5 | begin: '"', end: '"', 6 | contains: [hljs.BACKSLASH_ESCAPE] 7 | }] 8 | }; 9 | 10 | const EXTENDED_LITERAL = { 11 | className: 'literal', 12 | variants: [{ 13 | begin: '`', end: '`', 14 | illegal: '\\n' 15 | }] 16 | }; 17 | 18 | const METHOD = { 19 | className: 'function', 20 | beginKeywords: 'def', 21 | end: /[:={\[(\n;]/, 22 | excludeEnd: true, 23 | contains: [{ 24 | className: 'title', 25 | begin: /[^0-9\n\t "'(),.`{}\[\]:;][^\n\t "'(),.`{}\[\]:;]+|[^0-9\n\t "'(),.`{}\[\]:;=]/, 26 | relevance: 0 27 | }] 28 | }; 29 | 30 | return { 31 | keywords: { 32 | literal: 'true false this null', 33 | keyword: 'break class continue def else for if match print println return use while do case extract include' 34 | }, 35 | contains: [ 36 | hljs.C_LINE_COMMENT_MODE, 37 | hljs.C_BLOCK_COMMENT_MODE, 38 | STRING, 39 | EXTENDED_LITERAL, 40 | METHOD, 41 | hljs.C_NUMBER_MODE 42 | ] 43 | }; 44 | }; -------------------------------------------------------------------------------- /editors/idea/IntelliJ IDEA Global Settings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aNNiMON/Own-Programming-Language-Tutorial/f9c2bff15962b60ddc2d5b94a7f35d18e9b06900/editors/idea/IntelliJ IDEA Global Settings -------------------------------------------------------------------------------- /editors/idea/filetypes/OwnLang.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /editors/prismjs/own-language.js: -------------------------------------------------------------------------------- 1 | export default function(Prism) { 2 | Prism.languages.own = Prism.languages.extend('clike', { 3 | 'string': { 4 | pattern: /(^|[^\\])"(?:\\.|[^"\\])*"/, 5 | lookbehind: true, 6 | greedy: true 7 | }, 8 | 'keyword': /\b(?:break|case|class|continue|def|do|else|extract|for|if|include|match|new|print|println|return|while|use)\b/, 9 | 'function': { 10 | pattern: /((?:^|\s)def\s*)([a-zA-Z_]\w*)?(?=\s*\()/g, 11 | lookbehind: true 12 | }, 13 | 'operator': { 14 | pattern: /(^|[^.])(?:<<=?|>>>?=?|->|--|\+\+|&&|\*\*|\|\||::|\.\.\.?|[?:~]|[-+*/%&|^!=<>]=?)/m, 15 | lookbehind: true 16 | }, 17 | 'punctuation': /[{}[\];(),.:`]/ 18 | }); 19 | } -------------------------------------------------------------------------------- /examples/basics/array.own: -------------------------------------------------------------------------------- 1 | arr1 = [1, 2, 3, 4, 5] 2 | println arr1[0] 3 | println arr1[1] 4 | println arr1 5 | 6 | use std 7 | arr2 = newarray(5) 8 | arr2[2] = 9 9 | arr2 = arr2 :: 4 10 | println arr2 11 | 12 | // Append array 13 | arr3 = arr1 :: arr2 14 | println arr3 15 | // Merge array 16 | arr4 = arr1 << arr2 17 | println arr4 18 | 19 | 20 | // Array 2d 21 | arr5 = newarray(4, 4) 22 | println arr5 23 | arr5[1] = arr1 24 | println arr5 25 | 26 | arr6 = [arr5[1], arr5[2]] 27 | println arr6 -------------------------------------------------------------------------------- /examples/basics/bitwise_operators.own: -------------------------------------------------------------------------------- 1 | use std 2 | 3 | echo(#ABCDEF - #12345) 4 | echo(-8 << 2) 5 | echo(-8 >> 2) 6 | echo(-8 >>> 2) 7 | 8 | echo() 9 | for a = 0, a <= 1, a++ { 10 | for b = 0, b <= 1, b++ { 11 | echo(a, " | ", b, " ", a | b) 12 | echo(a, " & ", b, " ", a & b) 13 | echo(a, " ^ ", b, " ", a ^ b) 14 | } 15 | echo(" ~", a, " ", ~a) 16 | echo(" !", a, " ", !a) 17 | } -------------------------------------------------------------------------------- /examples/basics/classes.own: -------------------------------------------------------------------------------- 1 | use std 2 | 3 | class Point { 4 | def Point(x = 0, y = 0) { 5 | this.x = x 6 | this.y = y 7 | } 8 | 9 | def moveBy(p) { 10 | this.move(p.x, p.y) 11 | } 12 | 13 | def move(dx, dy) { 14 | this.x += dx 15 | this.y += dy 16 | } 17 | 18 | def toString() = "(" + this.x + ", " + this.y + ")" 19 | } 20 | 21 | p = new Point(20, 30) 22 | p.move(10, -5) 23 | println p.toString() 24 | 25 | p2 = new Point(1, 1) 26 | p2.moveBy(p) 27 | println p2.toString() 28 | -------------------------------------------------------------------------------- /examples/basics/destructuring_assignment.own: -------------------------------------------------------------------------------- 1 | use std 2 | 3 | println "Destructuring assignment" 4 | arr = ["a", "b", "c"] 5 | extract(var1, var2, var3) = arr 6 | echo(var1, var2, var3) 7 | 8 | // Swap 9 | println "Swap variables" 10 | echo(var1, var2) 11 | extract(var2, var1) = [var1, var2] 12 | echo(var1, var2) -------------------------------------------------------------------------------- /examples/basics/extended_identifier.own: -------------------------------------------------------------------------------- 1 | `extended identifier variable` = 9 2 | println `extended identifier variable` 3 | 4 | `(。◕‿◕。)` = 20 5 | `ʕ•ᴥ•ʔ` = 30 6 | println `(。◕‿◕。)` * `ʕ•ᴥ•ʔ` -------------------------------------------------------------------------------- /examples/basics/loops.own: -------------------------------------------------------------------------------- 1 | // While loop 2 | println "While loop" 3 | a = 0 4 | while a < 3 { 5 | print a 6 | a++ 7 | } 8 | 9 | 10 | // Do-while loop 11 | println "\n\nDo-while loop" 12 | a = 0 13 | do { 14 | print a 15 | a++ 16 | } while (a < 3) 17 | 18 | 19 | // For loop 20 | println "\n\nFor loop" 21 | for a = 0, a < 10, a++ 22 | print a 23 | 24 | 25 | // Foreach loop 26 | println "\n\nForeach loop on array" 27 | arr = [1, 2, 3, 4, 5] 28 | for a : arr 29 | print a 30 | 31 | use std 32 | println "\n\nForeach loop on map" 33 | object = {"key1": "value1", "key2": 100, "arr": [0, 1]} 34 | for key, value : object 35 | echo(key, ":", value) 36 | 37 | 38 | use functional 39 | 40 | // Functional loop 41 | println "\n\nFunctional loop on array" 42 | foreach(arr, ::echo) 43 | foreach(arr, def(v) { 44 | print v 45 | }) 46 | 47 | println "\n\nFunctional loop on map" 48 | foreach(object, ::echo) 49 | foreach(object, def(k, v) { 50 | print " " + k + " : " + v 51 | }) 52 | println "" 53 | 54 | // Range loop 55 | println "\n\nRange loop" 56 | for x : range(10) { 57 | print x 58 | } -------------------------------------------------------------------------------- /examples/basics/map.own: -------------------------------------------------------------------------------- 1 | map1 = {"key": "value"} 2 | println map1["key"] 3 | println map1.key 4 | 5 | map1["newkey"] = "newvalue" 6 | map1[0] = "zero" 7 | x = 400 8 | map1[x] = [1, 2, 3] 9 | println map1 -------------------------------------------------------------------------------- /examples/basics/operator_overloading.own: -------------------------------------------------------------------------------- 1 | use std, types, math 2 | 3 | println "Operator overloading" 4 | def `::`(v1, v2) = string(v1) + string(v2) 5 | print "1 :: 2 :: 3 = " 6 | println 1 :: 2 :: 3 7 | 8 | def `^`(v1, v2) = pow(v1[0], v2[0]) 9 | print "[2] ^ [7] = " 10 | println [2] ^ [7] 11 | 12 | def `..`(a, b) = range(a, b) 13 | def `**`(a, b) = int(pow(a, b)) 14 | for y : 1 .. 10 { 15 | println sprintf("2 ^ %d = %d", y, 2 ** y) 16 | } -------------------------------------------------------------------------------- /examples/basics/ternary_operator.own: -------------------------------------------------------------------------------- 1 | println "Ternary operator" 2 | a = 0 3 | b = 1 4 | println (a ? "text1" : "text2") 5 | println (b ? "text3" : "text4") -------------------------------------------------------------------------------- /examples/basics/thread.own: -------------------------------------------------------------------------------- 1 | use std 2 | 3 | def thread1() { 4 | i = 0 5 | while (i < 100) { 6 | println "i = " + i 7 | i++ 8 | sleep(100) 9 | } 10 | } 11 | 12 | // thread("thread1") 13 | thread(::thread1) 14 | 15 | k = 0 16 | while (k < 10) { 17 | println "k = " + k 18 | k++ 19 | sleep(1000) 20 | } 21 | 22 | -------------------------------------------------------------------------------- /examples/basics/types.own: -------------------------------------------------------------------------------- 1 | use std, types 2 | 3 | println typeof(1) 4 | println typeof("1") 5 | println typeof([]) 6 | println typeof({}) 7 | println typeof(def() = 0) 8 | 9 | println typeof(number("1")) 10 | println typeof(string(1)) 11 | -------------------------------------------------------------------------------- /examples/canvas/1.own: -------------------------------------------------------------------------------- 1 | use canvas 2 | 3 | w = 800 h = 600 4 | window("canvas example", w, h); 5 | 6 | // Очистка экрана белым цветом 7 | color(#ffffff) 8 | frect(0, 0, w, h) 9 | 10 | // Рисуем две линии красным цветом 11 | color(#ff0000) 12 | line(0, 0, w, h) 13 | line(w, 0, 0, h) 14 | repaint() 15 | -------------------------------------------------------------------------------- /examples/canvas/2.own: -------------------------------------------------------------------------------- 1 | use std, canvas 2 | 3 | w = 800 h = 600 4 | window("canvas example 2", w, h); 5 | color(#ffffff) 6 | frect(0, 0, w, h) 7 | 8 | step = rand(20) 9 | color(#0000ff) 10 | for y = 0, y < h, y += step { 11 | line(0, y, w, y) 12 | } 13 | for x = 0, x < w, x += step { 14 | line(x, 0, x, h) 15 | } 16 | repaint(); 17 | -------------------------------------------------------------------------------- /examples/canvas/control_point.own: -------------------------------------------------------------------------------- 1 | use canvas, std 2 | 3 | w = 640 h = 480 4 | window("Управление точкой", w, h) 5 | 6 | x = rand(w) y = rand(h) 7 | 8 | run = 1 9 | while run { 10 | key = keypressed() 11 | if (key == VK_LEFT && x > 0) x-- 12 | else if (key == VK_RIGHT && x < w) x++ 13 | else if (key == VK_UP && y > 0) y-- 14 | else if (key == VK_DOWN && y < h) y++ 15 | else if key == VK_ESCAPE run = 0 16 | 17 | color(255,255,255) 18 | frect(0,0,w,h) 19 | color(0) 20 | line(0, h, x, y) 21 | line(w, h, x, y) 22 | repaint() 23 | sleep(10) 24 | } -------------------------------------------------------------------------------- /examples/canvas/fractal_polygon.own: -------------------------------------------------------------------------------- 1 | use canvas, math, std 2 | 3 | msg = "" 4 | NUM_POINTS = 0 5 | while (NUM_POINTS <= 2 || NUM_POINTS > 25) { 6 | NUM_POINTS = 0 + prompt("Сколькиугольник? (3..25)" + msg) 7 | if (NUM_POINTS <= 2) msg = "!! Сказано же, ну!" 8 | else if (NUM_POINTS > 25) msg = " Чувак, " + NUM_POINTS + " это будет ООООЧЕНЬ долго!" 9 | } 10 | angle = 2*PI / NUM_POINTS; 11 | DIVIDER = 2.8 12 | 13 | 14 | w = 800 h = 600 15 | window("Fractal polygon demo", w, h) 16 | fractal(w/2, h/2, w/2) 17 | repaint() 18 | 19 | def cpoly(cx, cy, size) { 20 | ox = cx oy = cy - size 21 | ang = 0 22 | for i = 0, i < NUM_POINTS, i++ { 23 | ang += angle 24 | nx = cx - sin(ang)*size ny = cy - cos(ang)*size 25 | line(ox, oy, nx, ny) 26 | ox = nx oy = ny 27 | } 28 | } 29 | 30 | def fractal(cx, cy, size) { 31 | if size >= 3 { 32 | s2 = size / 2 33 | sD = size / DIVIDER 34 | color(0, 0, 255 - size * 255 / w/2) 35 | cpoly(cx, cy, sD) 36 | fractal(cx, cy - s2, sD) 37 | for n = 0, n < NUM_POINTS, n++ { 38 | fractal(cx - sin(angle*n)*s2, cy - cos(angle*n)*s2, sD) 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /examples/canvas/fractal_rect.own: -------------------------------------------------------------------------------- 1 | use canvas 2 | 3 | w = 800 h = 600 4 | window("Fractal rectangle demo", w, h) 5 | fractal(w/2, h/2, w/2) 6 | repaint() 7 | 8 | def rect(x, y, w, h) { 9 | line(x, y, x + w, y) 10 | line(x + w, y, x + w, y + h) 11 | line(x, y + h, x + w, y + h) 12 | line(x, y, x, y + h) 13 | } 14 | 15 | def fractal(cx, cy, size) { 16 | if size >= 2 { 17 | s2 = size / 2 18 | color(0, 0, 255 - size * 255 / w/2) 19 | rect(cx-s2, cy-s2, size, size) 20 | fractal(cx-s2, cy-s2, s2) 21 | fractal(cx+s2, cy-s2, s2) 22 | fractal(cx-s2, cy+s2, s2) 23 | fractal(cx+s2, cy+s2, s2) 24 | } 25 | } -------------------------------------------------------------------------------- /examples/canvas/fx_basic_shapes.own: -------------------------------------------------------------------------------- 1 | use canvasfx 2 | 3 | // https://docs.oracle.com/javafx/2/canvas/jfxpub-canvas.htm 4 | 5 | g = window("JavaFX Basic shapes", 300, 250) 6 | g.setFill(Color.GREEN) 7 | g.setStroke(Color.BLUE) 8 | g.setLineWidth(5) 9 | g.strokeLine(40, 10, 10, 40) 10 | g.fillOval(10, 60, 30, 30) 11 | g.strokeOval(60, 60, 30, 30) 12 | g.fillRoundRect(110, 60, 30, 30, 10, 10) 13 | g.strokeRoundRect(160, 60, 30, 30, 10, 10) 14 | g.fillArc(10, 110, 30, 30, 45, 240, ArcType.OPEN) 15 | g.fillArc(60, 110, 30, 30, 45, 240, ArcType.CHORD) 16 | g.fillArc(110, 110, 30, 30, 45, 240, ArcType.ROUND) 17 | g.strokeArc(10, 160, 30, 30, 45, 240, ArcType.OPEN) 18 | g.strokeArc(60, 160, 30, 30, 45, 240, ArcType.CHORD) 19 | g.strokeArc(110, 160, 30, 30, 45, 240, ArcType.ROUND) 20 | g.fillPolygon([10, 40, 10, 40], [210, 210, 240, 240], 4) 21 | g.strokePolygon([60, 90, 60, 90], [210, 210, 240, 240], 4) 22 | g.strokePolyline([110, 140, 110, 140], [210, 210, 240, 240], 4) 23 | repaint() -------------------------------------------------------------------------------- /examples/canvas/fx_event_handlers.own: -------------------------------------------------------------------------------- 1 | use canvasfx, std 2 | 3 | w = 800 h = 600 4 | g = window("JavaFX Event handler example", w, h) 5 | 6 | addEventHandler(Events.MOUSE_MOVED, ::onMouseMoved) 7 | addEventHandler(Events.MOUSE_DRAGGED, ::onMouseMoved) 8 | addEventHandler(Events.KEY_PRESSED, def(e) { 9 | if (e.code == KeyCode.C) clearRect(0, 0, w, h) 10 | }) 11 | 12 | def onMouseMoved(e) { 13 | g.setFill(Color.rgb(rand(255), rand(255), rand(255), rand())) 14 | m = 1 + e.isPrimaryButtonDown + e.isSecondaryButtonDown 15 | radius = m * rand(30, 50) 16 | g.fillOval(e.x - radius/2, e.y - radius/2, radius, radius) 17 | } -------------------------------------------------------------------------------- /examples/canvas/fx_global_alpha.own: -------------------------------------------------------------------------------- 1 | use canvasfx 2 | 3 | steps = 20 4 | size = 25 5 | w = steps * (size * 1.25) 6 | h = size * 1.5 7 | 8 | step = 1.0 / steps 9 | 10 | g = window("JavaFX Global Alpha example", w, h) 11 | 12 | g.setFill(Color.RED) 13 | y = size * 0.25 14 | for a = 0, a <= 1.0, a += step { 15 | g.setGlobalAlpha(a) 16 | g.fillRect(a * w, y, size, size) 17 | } -------------------------------------------------------------------------------- /examples/canvas/fx_image.own: -------------------------------------------------------------------------------- 1 | use canvasfx 2 | 3 | g = window("JavaFX Image demo", 400, 200) 4 | img = createImage("https://picsum.photos/400/200/") 5 | g.drawImage(img, 0, 0) 6 | repaint() -------------------------------------------------------------------------------- /examples/canvas/fx_image_negate.own: -------------------------------------------------------------------------------- 1 | use std, canvasfx 2 | 3 | graphics = window("JavaFX Image negation demo", 400, 400) 4 | imgSource = createImage("https://picsum.photos/400/200/") 5 | pixels = imgSource.getPixels() 6 | size = length(pixels) 7 | for i = 0, i < size, i++ { 8 | pixel = pixels[i] 9 | r = (pixel >> 16) & 0xFF 10 | g = (pixel >> 8) & 0xFF 11 | b = pixel & 0xFF 12 | r = 255 - r 13 | g = 255 - g 14 | b = 255 - b 15 | pixels[i] = 0xFF000000 | (r << 16) | (g << 8) | b 16 | } 17 | imgNegate = createImage(imgSource.width, imgSource.height, pixels) 18 | 19 | graphics.drawImage(imgSource, 0, 0) 20 | graphics.drawImage(imgNegate, 0, 200) 21 | repaint() -------------------------------------------------------------------------------- /examples/canvas/fx_rotation.own: -------------------------------------------------------------------------------- 1 | use canvasfx, std 2 | 3 | // http://www.developer.com/java/data/using-graphics-in-javafx.html 4 | 5 | width = 800 height = 600 6 | g = window("JavaFX Rotation example", width, height) 7 | 8 | g.translate(width / 2, height / 2) 9 | 10 | def randomColor() = Color.rgb(rand(255), rand(255), rand(255), 0.9) 11 | 12 | for i = 0, i < 60, i++ { 13 | g.rotate(6.0) 14 | g.setFill(randomColor()) 15 | g.fillOval(10, 60, 30, 30) 16 | g.setStroke(randomColor()) 17 | g.strokeOval(60, 60, 30, 30) 18 | g.setFill(randomColor()) 19 | g.fillRoundRect(110, 60, 30, 30, 10, 10) 20 | g.setFill(randomColor()) 21 | g.fillPolygon([105, 117, 159, 123, 133, 105, 77, 87, 51, 93], 22 | [150, 186, 186, 204, 246, 222, 246, 204, 186, 186], 10) 23 | } -------------------------------------------------------------------------------- /examples/console/colors.own: -------------------------------------------------------------------------------- 1 | use std 2 | 3 | // header 4 | print " " * 4 5 | for b : range(9) 6 | print sprintf(" 4%dm ", b) 7 | println "" 8 | 9 | for f : range(30, 39) { 10 | for s : ["", "1;"] { 11 | print sprintf("%4sm", s+f) 12 | print sprintf(" \u001B[%sm%s\u001B[0m", s+f, "gYw ") 13 | for b : range(8) 14 | print sprintf(" \u001B[4%s;%sm%s\u001B[0m", b, s+f, " gYw ") 15 | println "" 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /examples/database/hsqldb.own: -------------------------------------------------------------------------------- 1 | use std, jdbc 2 | 3 | connection = getConnection("jdbc:hsqldb:file:hsql.db", "", "", "org.hsqldb.jdbcDriver") 4 | statement = connection.createStatement() 5 | statement.executeUpdate("drop table if exists cities") 6 | statement.executeUpdate("CREATE TABLE cities (id IDENTITY, name VARCHAR(32))") 7 | statement.executeUpdate("INSERT INTO cities (name) VALUES('Киев')") 8 | statement.executeUpdate("INSERT INTO cities (name) VALUES('Минск')") 9 | statement.executeUpdate("INSERT INTO cities (name) VALUES('Москва')") 10 | 11 | rs = statement.executeQuery("SELECT id, name FROM cities") 12 | while(rs.next()) { 13 | // read the result set 14 | println "name = " + rs.getString("name") 15 | println "id = " + rs.getInt("id") 16 | } 17 | statement.execute("SHUTDOWN") 18 | -------------------------------------------------------------------------------- /examples/database/sqlite.own: -------------------------------------------------------------------------------- 1 | use std, jdbc 2 | 3 | // Example from https://github.com/xerial/sqlite-jdbc 4 | 5 | connection = getConnection("jdbc:sqlite:sample.db") 6 | statement = connection.createStatement() 7 | statement.setQueryTimeout(30) // set timeout to 30 sec. 8 | 9 | statement.executeUpdate("drop table if exists person") 10 | statement.executeUpdate("create table person (id integer, name string)") 11 | statement.executeUpdate("insert into person values(1, 'leo')") 12 | statement.executeUpdate("insert into person values(2, 'yui')") 13 | 14 | rs = statement.executeQuery("select * from person") 15 | while(rs.next()) { 16 | // read the result set 17 | println "name = " + rs.getString("name") 18 | println "id = " + rs.getInt("id") 19 | } 20 | -------------------------------------------------------------------------------- /examples/formats/json.own: -------------------------------------------------------------------------------- 1 | use json 2 | 3 | data = { 4 | "name": "Json Example", 5 | "version": 1, 6 | "arrayData": [ 7 | 1, 2, 3, 4 8 | ], 9 | "objectData": { 10 | "key": "value", 11 | 10: "1000" 12 | } 13 | } 14 | println "Json encode" 15 | println "Minified: " + jsonencode(data) 16 | println "Pretty-print: " + jsonencode(data, 2) 17 | -------------------------------------------------------------------------------- /examples/formats/zip.own: -------------------------------------------------------------------------------- 1 | use zip 2 | 3 | // println "Zip single file" 4 | // zip("absolute path to file", "example.zip") 5 | 6 | println "Zip files" 7 | zipFiles(["json.own", "yaml.own", "zip.own"], "example1.zip") 8 | 9 | println "Zip files" 10 | zipFiles({ 11 | "json.own": "json.txt", 12 | "yaml.own": "yaml/yaml.own", 13 | "zip.own": "readme.md"}, "example2.zip") 14 | 15 | println "List zip entries" 16 | println listZipEntries("example2.zip").joinToString(", ") 17 | -------------------------------------------------------------------------------- /examples/forms/basic.own: -------------------------------------------------------------------------------- 1 | use forms 2 | 3 | window = newWindow("Basic form example") 4 | window.add("Hello, world") 5 | window.pack() 6 | window.setVisible() -------------------------------------------------------------------------------- /examples/forms/button.own: -------------------------------------------------------------------------------- 1 | use forms 2 | 3 | button = newButton("Click me") 4 | button.onClick(def() { 5 | println "Oh, you clicked me." 6 | }) 7 | 8 | window = newWindow("Button example") 9 | window.add(button) 10 | window.pack() 11 | window.setVisible() -------------------------------------------------------------------------------- /examples/forms/look_and_feel.own: -------------------------------------------------------------------------------- 1 | use java, forms 2 | 3 | UIManager = newClass("javax.swing.UIManager") 4 | // UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel") 5 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()) 6 | 7 | label = newLabel("Current value: 50") 8 | progressBar = newProgressBar() 9 | progressBar.setValue(50) 10 | progressBar.onChange(def() { 11 | label.setText("Current value: " + progressBar.getValue()) 12 | }) 13 | minusBtn = newButton("-1") 14 | minusBtn.onClick(def() = changeProgress(-1)) 15 | plusBtn = newButton("+1") 16 | plusBtn.onClick(def() = changeProgress(1)) 17 | 18 | def changeProgress(delta) { 19 | value = progressBar.getValue() + delta 20 | if (value > 100) value = 100 21 | else if (value < 0) value = 0 22 | progressBar.setValue(value) 23 | } 24 | 25 | window = newWindow("ProgressBar example") 26 | window.add(minusBtn, BorderLayout.WEST) 27 | window.add(progressBar, BorderLayout.CENTER) 28 | window.add(plusBtn, BorderLayout.EAST) 29 | window.add(label, BorderLayout.SOUTH) 30 | window.pack() 31 | window.setLocationByPlatform() 32 | window.setResizable(false) 33 | window.setVisible() 34 | -------------------------------------------------------------------------------- /examples/forms/panel.own: -------------------------------------------------------------------------------- 1 | use forms 2 | 3 | // Create Panel with BoxLayout 4 | panel = newPanel() 5 | panel.setLayout(boxLayout(panel, BoxLayout.PAGE_AXIS)) 6 | // String label (alias to JLabel) 7 | panel.add("String label") 8 | 9 | // Add label 10 | label = newLabel("Label") 11 | label.setHorizontalAlignment(SwingConstants.CENTER) 12 | panel.add(label) 13 | 14 | // Add text field 15 | textField = newTextField("Some text") 16 | textField.setColumns(20) 17 | panel.add(textField) 18 | 19 | // Add button 20 | button = newButton("Button") 21 | panel.add(button) 22 | 23 | // Add another button 24 | clearBtn = newButton("Clear panel") 25 | clearBtn.onClick(def() { 26 | panel.removeAll() 27 | panel.revalidate() 28 | panel.repaint() 29 | }) 30 | panel.add(clearBtn) 31 | 32 | window = newWindow("Panel Example") 33 | window.setLocation(400, 200) 34 | window.add(panel) 35 | window.pack() 36 | window.setAlwaysOnTop() 37 | window.setVisible() -------------------------------------------------------------------------------- /examples/forms/progressbar.own: -------------------------------------------------------------------------------- 1 | use forms 2 | 3 | label = newLabel("Current value: 50") 4 | progressBar = newProgressBar() 5 | progressBar.setValue(50) 6 | progressBar.onChange(def() { 7 | label.setText("Current value: " + progressBar.getValue()) 8 | }) 9 | minusBtn = newButton("-1") 10 | minusBtn.onClick(def() = changeProgress(-1)) 11 | plusBtn = newButton("+1") 12 | plusBtn.onClick(def() = changeProgress(1)) 13 | 14 | def changeProgress(delta) { 15 | value = progressBar.getValue() + delta 16 | if (value > 100) value = 100 17 | else if (value < 0) value = 0 18 | progressBar.setValue(value) 19 | } 20 | 21 | window = newWindow("ProgressBar example") 22 | window.add(minusBtn, BorderLayout.WEST) 23 | window.add(progressBar, BorderLayout.CENTER) 24 | window.add(plusBtn, BorderLayout.EAST) 25 | window.add(label, BorderLayout.SOUTH) 26 | window.pack() 27 | window.setLocationByPlatform() 28 | window.setResizable(false) 29 | window.setVisible() -------------------------------------------------------------------------------- /examples/forms/samobot_chat.own: -------------------------------------------------------------------------------- 1 | use std, http, forms 2 | 3 | chatHistory = newLabel("Чат с самоботом
") 4 | messageField = newTextField() 5 | sendButton = newButton("Отправить") 6 | 7 | messageField.onAction(::onSend) 8 | sendButton.onClick(::onSend) 9 | def onSend() { 10 | text = messageField.getText() 11 | if (length(text) == 0) return 0 12 | messageField.setText("") 13 | chatHistory.setText(chatHistory.getText() + "
вы > " + text) 14 | thread(::http, "https://annimon.com/json/bot.php", "POST", {"text": text}, def(answer) { 15 | chatHistory.setText(chatHistory.getText() + "
бот > " + answer) 16 | }) 17 | } 18 | 19 | messagePanel = newPanel() 20 | messagePanel.setLayout(boxLayout(messagePanel, BoxLayout.LINE_AXIS)) 21 | messagePanel.add(messageField) 22 | messagePanel.add(sendButton) 23 | 24 | mainPanel = newPanel(borderLayout(10, 10)) 25 | mainPanel.setPreferredSize(400, 250) 26 | mainPanel.add(chatHistory, BorderLayout.CENTER) 27 | mainPanel.add(messagePanel, BorderLayout.SOUTH) 28 | 29 | 30 | window = newWindow("Чат с самоботом") 31 | window.setMinimumSize(200, 220) 32 | window.setLocationByPlatform() 33 | window.add(mainPanel) 34 | window.pack() 35 | window.setVisible() 36 | -------------------------------------------------------------------------------- /examples/forms/textarea.own: -------------------------------------------------------------------------------- 1 | use std, forms, functional 2 | 3 | text = map(range(1, 16), def(x) = "line " + x).joinToString("\n") 4 | label = newLabel() 5 | textArea = newTextArea(text) 6 | textArea.addCaretListener(def(event) = updateInfo()) 7 | textArea.addDocumentListener(def(type, event) = updateInfo()) 8 | updateInfo() 9 | 10 | def updateInfo() { 11 | text = "Text length: " + textArea.getText().length 12 | text += ", lines: " + textArea.getLineCount() 13 | selectedText = default(textArea.getSelectedText(), "") 14 | if (!selectedText.isEmpty()) { 15 | text += ", selected: " + selectedText.length 16 | } 17 | label.setText(text) 18 | } 19 | 20 | window = newWindow("JTextArea example") 21 | window.add(newScrollPane(textArea), BorderLayout.CENTER) 22 | window.add(label, BorderLayout.SOUTH) 23 | window.setSize(300, 200) 24 | window.setLocationByPlatform() 25 | window.setVisible() -------------------------------------------------------------------------------- /examples/forms/textfield.own: -------------------------------------------------------------------------------- 1 | use std, forms 2 | 3 | textField = newTextField("Some text") 4 | 5 | button = newButton("Click me") 6 | button.onClick(def() { 7 | println "TextField text: " + textField.getText() 8 | textField.setText(textField.getText() + " Let's add new line") 9 | }) 10 | 11 | window = newWindow("Text field example") 12 | window.add(textField) 13 | window.add(button, BorderLayout.SOUTH) 14 | window.pack() 15 | window.setLocationByPlatform() 16 | window.setVisible() 17 | 18 | textField.onAction(def() = echo("I am a TextField")) 19 | textField.addKeyListener(def(type, event) { 20 | println sprintf("%s %d %s", 21 | type, event.keyCode, toChar(event.keyChar)) 22 | }) -------------------------------------------------------------------------------- /examples/forms/windowlistener.own: -------------------------------------------------------------------------------- 1 | use forms 2 | 3 | textArea = newTextArea("Window logs:") 4 | 5 | window = newWindow("Window listener example") 6 | window.addWindowListener(def(type, event) { 7 | textArea.append("\n" + type + ", id: " + match event.id { 8 | case WINDOW_OPENED: "WINDOW_OPENED" 9 | case WINDOW_CLOSING: "WINDOW_CLOSING" 10 | case WINDOW_CLOSED: "WINDOW_CLOSED" 11 | case WINDOW_ICONIFIED: "WINDOW_ICONIFIED" 12 | case WINDOW_DEICONIFIED: "WINDOW_DEICONIFIED" 13 | case WINDOW_ACTIVATED: "WINDOW_ACTIVATED" 14 | case WINDOW_DEACTIVATED: "WINDOW_DEACTIVATED" 15 | case WINDOW_GAINED_FOCUS: "WINDOW_GAINED_FOCUS" 16 | case WINDOW_LOST_FOCUS: "WINDOW_LOST_FOCUS" 17 | case WINDOW_STATE_CHANGED: "WINDOW_STATE_CHANGED" 18 | case _: "unknown type" 19 | }) 20 | }) 21 | window.add(newScrollPane(textArea)) 22 | window.setSize(300, 200) 23 | window.setLocationByPlatform() 24 | window.setVisible() -------------------------------------------------------------------------------- /examples/functions/basics.own: -------------------------------------------------------------------------------- 1 | use std, math, functional 2 | 3 | add = def(a,b) = a + b 4 | sub = def(a,b) = a - b 5 | mul = def(a,b) = a * b 6 | div = def(a,b) = a / b 7 | cube = def(x) = x * mul(x, x) 8 | println mul(8, 5) 9 | println cube(2) 10 | 11 | functions = [add, sub, mul, div] 12 | for f : functions { 13 | println f 14 | println f(6, 3) 15 | } 16 | 17 | 18 | map = {"+" : add, "-" : sub, "*" : mul, "/" : div} 19 | map["%"] = def(x,y) = x % y 20 | map["pow"] = def(x,y) = pow(x, y) 21 | foreach(map, def(op, func) = echo (4, op, 5, "=", func(4,5))) -------------------------------------------------------------------------------- /examples/functions/calculator.own: -------------------------------------------------------------------------------- 1 | // Simple parser example 2 | use std, types 3 | 4 | operations = { 5 | "+" : def(a,b) = a+b, 6 | "-" : def(a,b) = a-b, 7 | "*" : def(a,b) = a*b, 8 | "/" : def(a,b) = a/b, 9 | "%" : def(a,b) = a%b, 10 | ">" : def(a,b) = a>b, 11 | "<" : def(a,b) = a4") 46 | -------------------------------------------------------------------------------- /examples/functions/chain.own: -------------------------------------------------------------------------------- 1 | use std, functional 2 | 3 | data = [1,2,3,4,5,6,7,8,9] 4 | chain(data, 5 | ::filter, def(x) = x % 2 == 0, 6 | ::map, def(x) = [x, x * x, x * x * x], 7 | ::sortby, def(x) = -x[2], 8 | ::foreach, ::echo 9 | ) 10 | 11 | -------------------------------------------------------------------------------- /examples/functions/default_arguments.own: -------------------------------------------------------------------------------- 1 | def funcWithOptionalArgs(str, count = 5, prefix = "<", suffix = ">") = prefix + (str * count) + suffix 2 | 3 | println funcWithOptionalArgs("*") 4 | println funcWithOptionalArgs("+", 2) 5 | println funcWithOptionalArgs("*", 10, " 2 | 3 | 4 | Notes 5 | 6 | 7 | 8 |
9 |

Notes

10 |
11 | 12 | 13 |
14 | 15 |
16 | 17 | 18 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /examples/server/notes_public/notes.js: -------------------------------------------------------------------------------- 1 | const elNotes = document.querySelector('#notes'); 2 | const elNoteTemplate = document.querySelector('templates ul[name="note"] li'); 3 | 4 | function renderNote(note) { 5 | const el = elNoteTemplate.cloneNode(true); 6 | el.querySelector('h4').innerText = 'Note #' + note.id; 7 | el.querySelector('p').innerText = note.content; 8 | return el; 9 | } 10 | 11 | async function addNote() { 12 | const inpEl = document.querySelector('.new-note input'); 13 | const content = inpEl.value; 14 | const resp = await fetch('/notes/', { 15 | method: "POST", 16 | body: content 17 | }); 18 | const note = await resp.json(); 19 | inpEl.value = ''; 20 | console.log(note); 21 | elNotes.prepend(renderNote(note)); 22 | } 23 | 24 | async function getNotes() { 25 | const resp = await fetch("/notes"); 26 | const notes = await resp.json(); 27 | elNotes.innerHTML = ''; 28 | for (const note of notes) { 29 | elNotes.prepend(renderNote(note)); 30 | } 31 | } 32 | 33 | getNotes(); -------------------------------------------------------------------------------- /examples/server/notes_public/styles.css: -------------------------------------------------------------------------------- 1 | templates { 2 | display: none; 3 | } 4 | 5 | #notes { 6 | list-style-type: none; 7 | padding: 0; 8 | } 9 | .note { 10 | margin: 0; 11 | padding: 0rem 0.3rem; 12 | } 13 | .note h4 { 14 | margin: 0; 15 | } 16 | .note p { 17 | color: #333; 18 | margin-top: 0; 19 | } 20 | 21 | .new-note { 22 | margin-bottom: 2rem; 23 | } 24 | .new-note input { 25 | width: 15rem; 26 | padding: 0.3rem; 27 | } 28 | .new-note button { 29 | padding: 0.3rem 1rem; 30 | background-color: #4caf50; 31 | color: #fff; 32 | border: none; 33 | cursor: pointer; 34 | font-size: 1rem; 35 | } -------------------------------------------------------------------------------- /examples/server/server_spa_simple.own: -------------------------------------------------------------------------------- 1 | use std, jdbc, server 2 | 3 | // curl -X POST http://localhost:8084/notes/ -d "New note 2" 4 | 5 | notes = [] 6 | createNote("This is your first note.") 7 | 8 | def createNote(content) { 9 | note = {"id": notes.length + 1, "content": content}; 10 | notes += note 11 | return note 12 | } 13 | 14 | newServer({"externalDirs": ["notes_public"]}) 15 | .get("/notes", def(ctx) = ctx.json(notes)) 16 | .post("/notes", def(ctx) { 17 | ctx.status(201) 18 | ctx.json( createNote(ctx.body()) ) 19 | }) 20 | .start(8084) -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aNNiMON/Own-Programming-Language-Tutorial/f9c2bff15962b60ddc2d5b94a7f35d18e9b06900/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Aug 12 23:03:44 EEST 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /modules/canvasfx/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'org.openjfx.javafxplugin' version "0.1.0" 4 | id 'com.github.johnrengelman.shadow' version '8.1.1' 5 | } 6 | 7 | group = 'com.annimon.module' 8 | version = '1.1.0' 9 | 10 | javafx { 11 | version = "21" 12 | modules = [ 'javafx.controls', 'javafx.swing' ] 13 | } 14 | 15 | dependencies { 16 | compileOnlyApi project(":ownlang-core") 17 | 18 | testImplementation platform("org.junit:junit-bom:${versions.junit}") 19 | testImplementation 'org.junit.jupiter:junit-jupiter' 20 | } 21 | 22 | test { 23 | useJUnitPlatform() 24 | } -------------------------------------------------------------------------------- /modules/jdbc/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'com.github.johnrengelman.shadow' version '8.1.1' 4 | } 5 | 6 | group = 'com.annimon.module' 7 | version = '1.0.0' 8 | 9 | dependencies { 10 | compileOnlyApi project(":ownlang-core") 11 | 12 | testImplementation platform("org.junit:junit-bom:${versions.junit}") 13 | testImplementation 'org.junit.jupiter:junit-jupiter' 14 | } 15 | 16 | test { 17 | useJUnitPlatform() 18 | } -------------------------------------------------------------------------------- /modules/main/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | } 4 | 5 | group = 'com.annimon.module' 6 | version = versions.project 7 | 8 | dependencies { 9 | compileOnlyApi project(":ownlang-core") 10 | 11 | implementation "com.squareup.okhttp3:okhttp:${versions.okhttp}" 12 | implementation "org.json:json:${versions.json}" 13 | implementation "org.yaml:snakeyaml:${versions.snakeyaml}" 14 | 15 | testImplementation platform("org.junit:junit-bom:${versions.junit}") 16 | testImplementation 'org.junit.jupiter:junit-jupiter' 17 | } 18 | 19 | test { 20 | useJUnitPlatform() 21 | } -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/forms/JButtonValue.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.forms; 2 | 3 | import javax.swing.JButton; 4 | 5 | public class JButtonValue extends AbstractButtonValue { 6 | 7 | final JButton jButton; 8 | 9 | public JButtonValue(JButton jButton) { 10 | super(0, jButton); 11 | this.jButton = jButton; 12 | init(); 13 | } 14 | 15 | private void init() { 16 | } 17 | } -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/forms/JFrameValue.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.forms; 2 | 3 | import static com.annimon.ownlang.lib.Converters.*; 4 | import javax.swing.JFrame; 5 | 6 | public class JFrameValue extends WindowValue { 7 | 8 | final JFrame frame; 9 | 10 | public JFrameValue(JFrame frame) { 11 | super(9, frame); 12 | this.frame = frame; 13 | init(); 14 | } 15 | 16 | private void init() { 17 | set("getTitle", voidToString(frame::getTitle)); 18 | set("getResizable", voidToBoolean(frame::isResizable)); 19 | set("getDefaultCloseOperation", voidToInt(frame::getDefaultCloseOperation)); 20 | set("setDefaultCloseOperation", intToVoid(frame::setDefaultCloseOperation)); 21 | set("setResizable", booleanOptToVoid(frame::setResizable)); 22 | set("setTitle", stringToVoid(frame::setTitle)); 23 | } 24 | } -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/forms/JPanelValue.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.forms; 2 | 3 | import javax.swing.JPanel; 4 | 5 | public class JPanelValue extends JComponentValue { 6 | 7 | final JPanel panel; 8 | 9 | public JPanelValue(JPanel panel) { 10 | super(0, panel); 11 | this.panel = panel; 12 | init(); 13 | } 14 | 15 | private void init() { 16 | } 17 | } -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/forms/LayoutManagerValue.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.forms; 2 | 3 | import com.annimon.ownlang.lib.MapValue; 4 | import java.awt.LayoutManager; 5 | 6 | public class LayoutManagerValue extends MapValue { 7 | 8 | final LayoutManager layout; 9 | 10 | public LayoutManagerValue(LayoutManager layout) { 11 | super(0); 12 | this.layout = layout; 13 | } 14 | } -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_chain.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.functional; 2 | 3 | import com.annimon.ownlang.lib.Arguments; 4 | import com.annimon.ownlang.lib.Function; 5 | import com.annimon.ownlang.lib.Value; 6 | import com.annimon.ownlang.lib.ValueUtils; 7 | 8 | final class functional_chain implements Function { 9 | 10 | @Override 11 | public Value execute(Value[] args) { 12 | Arguments.checkAtLeast(2, args.length); 13 | 14 | Value result = args[0]; 15 | for (int i = 1; i < args.length; i += 2) { 16 | final Function function = ValueUtils.consumeFunction(args[i], i); 17 | result = function.execute(result, args[i+1]); 18 | } 19 | return result; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_combine.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.functional; 2 | 3 | import com.annimon.ownlang.exceptions.TypeException; 4 | import com.annimon.ownlang.lib.Arguments; 5 | import com.annimon.ownlang.lib.Function; 6 | import com.annimon.ownlang.lib.FunctionValue; 7 | import com.annimon.ownlang.lib.Types; 8 | import com.annimon.ownlang.lib.Value; 9 | 10 | final class functional_combine implements Function { 11 | 12 | @Override 13 | public Value execute(Value[] args) { 14 | Arguments.checkAtLeast(1, args.length); 15 | Function result = null; 16 | for (Value arg : args) { 17 | if (arg.type() != Types.FUNCTION) { 18 | throw new TypeException(arg + " is not a function"); 19 | } 20 | final Function current = result; 21 | final Function next = ((FunctionValue) arg).getValue(); 22 | result = fArgs -> { 23 | if (current == null) return next.execute(fArgs); 24 | return next.execute(current.execute(fArgs)); 25 | }; 26 | } 27 | 28 | return new FunctionValue(result); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_filterNot.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.functional; 2 | 3 | import com.annimon.ownlang.lib.*; 4 | 5 | final class functional_filterNot implements Function { 6 | 7 | @Override 8 | public Value execute(Value[] args) { 9 | Arguments.check(2, args.length); 10 | final Value container = args[0]; 11 | final Function predicate = ValueUtils.consumeFunction(args[1], 1); 12 | return functional_filter.filter(container, negate(predicate)); 13 | } 14 | 15 | static Function negate(Function f) { 16 | return args -> NumberValue.fromBoolean(f.execute(args) == NumberValue.ZERO); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_sortBy.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.functional; 2 | 3 | import com.annimon.ownlang.exceptions.TypeException; 4 | import com.annimon.ownlang.lib.Arguments; 5 | import com.annimon.ownlang.lib.ArrayValue; 6 | import com.annimon.ownlang.lib.Function; 7 | import com.annimon.ownlang.lib.Types; 8 | import com.annimon.ownlang.lib.Value; 9 | import com.annimon.ownlang.lib.ValueUtils; 10 | import java.util.Arrays; 11 | import java.util.Comparator; 12 | 13 | final class functional_sortBy implements Function { 14 | 15 | @Override 16 | public Value execute(Value[] args) { 17 | Arguments.check(2, args.length); 18 | if (args[0].type() != Types.ARRAY) { 19 | throw new TypeException("Array expected at first argument"); 20 | } 21 | 22 | final Value[] elements = ((ArrayValue) args[0]).getCopyElements(); 23 | final Function function = ValueUtils.consumeFunction(args[1], 1); 24 | Arrays.sort(elements, Comparator.comparing(function::execute)); 25 | return new ArrayValue(elements); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/functional/functional_stream.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.functional; 2 | 3 | import com.annimon.ownlang.exceptions.TypeException; 4 | import com.annimon.ownlang.lib.*; 5 | 6 | final class functional_stream implements Function { 7 | 8 | @Override 9 | public Value execute(Value[] args) { 10 | Arguments.checkAtLeast(1, args.length); 11 | 12 | final Value value = args[0]; 13 | return switch (value.type()) { 14 | case Types.MAP -> new StreamValue(((MapValue) value).toPairs()); 15 | case Types.ARRAY -> new StreamValue((ArrayValue) value); 16 | default -> throw new TypeException("Invalid argument. Array or map expected"); 17 | }; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/http/http.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.http; 2 | 3 | import com.annimon.ownlang.lib.Function; 4 | import com.annimon.ownlang.lib.Value; 5 | import com.annimon.ownlang.modules.Module; 6 | import java.util.Collections; 7 | import java.util.Map; 8 | 9 | /** 10 | * 11 | * @author aNNiMON 12 | */ 13 | public final class http implements Module { 14 | 15 | @Override 16 | public Map constants() { 17 | return Collections.emptyMap(); 18 | } 19 | 20 | @Override 21 | public Map functions() { 22 | final var httpFunctions = new HttpFunctions(); 23 | return Map.of( 24 | "urlencode", new http_urlencode(), 25 | "http", httpFunctions::http, 26 | "httpSync", httpFunctions::httpSync, 27 | "download", new http_download() 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/http/http_download.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.http; 2 | 3 | import com.annimon.ownlang.lib.*; 4 | import java.io.IOException; 5 | import okhttp3.*; 6 | 7 | public final class http_download implements Function { 8 | 9 | private final OkHttpClient client = new OkHttpClient(); 10 | 11 | @Override 12 | public Value execute(Value[] args) { 13 | Arguments.check(1, args.length); 14 | try { 15 | final Response response = client.newCall( 16 | new Request.Builder().url(args[0].asString()).build()) 17 | .execute(); 18 | return ArrayValue.of(response.body().bytes()); 19 | } catch (IOException ex) { 20 | return new ArrayValue(0); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/http/http_urlencode.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.http; 2 | 3 | import com.annimon.ownlang.lib.Arguments; 4 | import com.annimon.ownlang.lib.Function; 5 | import com.annimon.ownlang.lib.StringValue; 6 | import com.annimon.ownlang.lib.Value; 7 | import java.io.IOException; 8 | import java.net.URLEncoder; 9 | 10 | public final class http_urlencode implements Function { 11 | 12 | @Override 13 | public Value execute(Value[] args) { 14 | Arguments.checkOrOr(1, 2, args.length); 15 | 16 | String charset = "UTF-8"; 17 | if (args.length >= 2) { 18 | charset = args[1].asString(); 19 | } 20 | 21 | try { 22 | final String result = URLEncoder.encode(args[0].asString(), charset); 23 | return new StringValue(result); 24 | } catch (IOException ex) { 25 | return args[0]; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/json/json.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.json; 2 | 3 | import com.annimon.ownlang.lib.Function; 4 | import com.annimon.ownlang.lib.Value; 5 | import com.annimon.ownlang.modules.Module; 6 | import java.util.Collections; 7 | import java.util.Map; 8 | 9 | /** 10 | * 11 | * @author aNNiMON 12 | */ 13 | public final class json implements Module { 14 | 15 | @Override 16 | public Map constants() { 17 | return Collections.emptyMap(); 18 | } 19 | 20 | @Override 21 | public Map functions() { 22 | return Map.of( 23 | "jsonencode", new json_encode(), 24 | "jsondecode", new json_decode() 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/json/json_decode.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.json; 2 | 3 | import com.annimon.ownlang.exceptions.OwnLangRuntimeException; 4 | import com.annimon.ownlang.lib.Arguments; 5 | import com.annimon.ownlang.lib.Function; 6 | import com.annimon.ownlang.lib.Value; 7 | import com.annimon.ownlang.lib.ValueUtils; 8 | import org.json.JSONException; 9 | import org.json.JSONTokener; 10 | 11 | public final class json_decode implements Function { 12 | 13 | @Override 14 | public Value execute(Value[] args) { 15 | Arguments.check(1, args.length); 16 | try { 17 | final String jsonRaw = args[0].asString(); 18 | final Object root = new JSONTokener(jsonRaw).nextValue(); 19 | return ValueUtils.toValue(root); 20 | } catch (JSONException ex) { 21 | throw new OwnLangRuntimeException("Error while parsing json", ex); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/okhttp/MultipartBodyValue.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.okhttp; 2 | 3 | import com.annimon.ownlang.lib.Converters; 4 | import okhttp3.MultipartBody; 5 | 6 | public class MultipartBodyValue extends RequestBodyValue { 7 | 8 | private final MultipartBody multipartBody; 9 | 10 | public MultipartBodyValue(MultipartBody multipartBody) { 11 | super(multipartBody, 5); 12 | this.multipartBody = multipartBody; 13 | init(); 14 | } 15 | 16 | public MultipartBody getMultipartBody() { 17 | return multipartBody; 18 | } 19 | 20 | private void init() { 21 | set("boundary", Converters.voidToString(multipartBody::boundary)); 22 | set("size", Converters.voidToInt(multipartBody::size)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_fromclipboard.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.robot; 2 | 3 | import com.annimon.ownlang.lib.Function; 4 | import com.annimon.ownlang.lib.StringValue; 5 | import com.annimon.ownlang.lib.Value; 6 | import java.awt.Toolkit; 7 | import java.awt.datatransfer.DataFlavor; 8 | 9 | public final class robot_fromclipboard implements Function { 10 | 11 | @Override 12 | public Value execute(Value[] args) { 13 | try { 14 | Object data = Toolkit.getDefaultToolkit().getSystemClipboard() 15 | .getData(DataFlavor.stringFlavor); 16 | return new StringValue(data.toString()); 17 | } catch (Exception ex) { 18 | return StringValue.EMPTY; 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/robot/robot_toclipboard.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.robot; 2 | 3 | import com.annimon.ownlang.lib.Arguments; 4 | import com.annimon.ownlang.lib.Function; 5 | import com.annimon.ownlang.lib.NumberValue; 6 | import com.annimon.ownlang.lib.Value; 7 | import java.awt.Toolkit; 8 | import java.awt.datatransfer.StringSelection; 9 | 10 | public final class robot_toclipboard implements Function { 11 | 12 | @Override 13 | public Value execute(Value[] args) { 14 | Arguments.check(1, args.length); 15 | Toolkit.getDefaultToolkit().getSystemClipboard() 16 | .setContents(new StringSelection(args[0].asString()), null); 17 | return NumberValue.ZERO; 18 | } 19 | } -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/NumberFunctions.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.lib.Arguments; 4 | import com.annimon.ownlang.lib.NumberValue; 5 | import com.annimon.ownlang.lib.StringValue; 6 | import com.annimon.ownlang.lib.Types; 7 | import com.annimon.ownlang.lib.Value; 8 | 9 | final class NumberFunctions { 10 | 11 | private NumberFunctions() { } 12 | 13 | static Value toHexString(Value[] args) { 14 | Arguments.check(1, args.length); 15 | long value; 16 | if (args[0].type() == Types.NUMBER) { 17 | value = ((NumberValue) args[0]).asLong(); 18 | } else { 19 | value = (long) args[0].asNumber(); 20 | } 21 | return new StringValue(Long.toHexString(value)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeyExists.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.exceptions.TypeException; 4 | import com.annimon.ownlang.lib.Arguments; 5 | import com.annimon.ownlang.lib.Function; 6 | import com.annimon.ownlang.lib.MapValue; 7 | import com.annimon.ownlang.lib.NumberValue; 8 | import com.annimon.ownlang.lib.Types; 9 | import com.annimon.ownlang.lib.Value; 10 | 11 | final class std_arrayKeyExists implements Function { 12 | 13 | @Override 14 | public Value execute(Value[] args) { 15 | Arguments.check(2, args.length); 16 | if (args[1].type() != Types.MAP) { 17 | throw new TypeException("Map expected in second argument"); 18 | } 19 | final MapValue map = ((MapValue) args[1]); 20 | return NumberValue.fromBoolean(map.containsKey(args[0])); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayKeys.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.exceptions.TypeException; 4 | import com.annimon.ownlang.lib.Arguments; 5 | import com.annimon.ownlang.lib.ArrayValue; 6 | import com.annimon.ownlang.lib.Function; 7 | import com.annimon.ownlang.lib.MapValue; 8 | import com.annimon.ownlang.lib.Types; 9 | import com.annimon.ownlang.lib.Value; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | final class std_arrayKeys implements Function { 15 | 16 | @Override 17 | public Value execute(Value[] args) { 18 | Arguments.check(1, args.length); 19 | if (args[0].type() != Types.MAP) { 20 | throw new TypeException("Map expected in first argument"); 21 | } 22 | final MapValue map = ((MapValue) args[0]); 23 | final List keys = new ArrayList<>(map.size()); 24 | for (Map.Entry entry : map) { 25 | keys.add(entry.getKey()); 26 | } 27 | return new ArrayValue(keys); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_arrayValues.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.exceptions.TypeException; 4 | import com.annimon.ownlang.lib.Arguments; 5 | import com.annimon.ownlang.lib.ArrayValue; 6 | import com.annimon.ownlang.lib.Function; 7 | import com.annimon.ownlang.lib.MapValue; 8 | import com.annimon.ownlang.lib.Types; 9 | import com.annimon.ownlang.lib.Value; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | final class std_arrayValues implements Function { 15 | 16 | @Override 17 | public Value execute(Value[] args) { 18 | Arguments.check(1, args.length); 19 | if (args[0].type() != Types.MAP) { 20 | throw new TypeException("Map expected in first argument"); 21 | } 22 | final MapValue map = ((MapValue) args[0]); 23 | final List values = new ArrayList<>(map.size()); 24 | for (Map.Entry entry : map) { 25 | values.add(entry.getValue()); 26 | } 27 | return new ArrayValue(values); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_charat.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.lib.Arguments; 4 | import com.annimon.ownlang.lib.Function; 5 | import com.annimon.ownlang.lib.NumberValue; 6 | import com.annimon.ownlang.lib.Value; 7 | 8 | final class std_charat implements Function { 9 | 10 | @Override 11 | public Value execute(Value[] args) { 12 | Arguments.check(2, args.length); 13 | 14 | final String input = args[0].asString(); 15 | final int index = args[1].asInt(); 16 | 17 | return NumberValue.of((short)input.charAt(index)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_default.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.lib.*; 4 | 5 | final class std_default implements Function { 6 | 7 | @Override 8 | public Value execute(Value[] args) { 9 | Arguments.check(2, args.length); 10 | if (isEmpty(args[0])) { 11 | return args[1]; 12 | } 13 | return args[0]; 14 | } 15 | 16 | private boolean isEmpty(Value value) { 17 | if (value == null || value.raw() == null) { 18 | return true; 19 | } 20 | return switch (value.type()) { 21 | case Types.NUMBER -> (value.asInt() == 0); 22 | case Types.STRING -> (value.asString().isEmpty()); 23 | case Types.ARRAY -> ((ArrayValue) value).size() == 0; 24 | case Types.MAP -> ((MapValue) value).size() == 0; 25 | case Types.CLASS -> ((ClassInstance) value).getThisMap().size() == 0; 26 | default -> false; 27 | }; 28 | } 29 | } -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_echo.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.Console; 4 | import com.annimon.ownlang.lib.Function; 5 | import com.annimon.ownlang.lib.NumberValue; 6 | import com.annimon.ownlang.lib.Value; 7 | 8 | final class std_echo implements Function { 9 | 10 | @Override 11 | public Value execute(Value[] args) { 12 | final StringBuilder sb = new StringBuilder(); 13 | for (Value arg : args) { 14 | sb.append(arg.asString()); 15 | sb.append(' '); 16 | } 17 | Console.println(sb.toString()); 18 | return NumberValue.ZERO; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_indexof.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.lib.Arguments; 4 | import com.annimon.ownlang.lib.Function; 5 | import com.annimon.ownlang.lib.NumberValue; 6 | import com.annimon.ownlang.lib.Value; 7 | 8 | final class std_indexof implements Function { 9 | 10 | @Override 11 | public Value execute(Value[] args) { 12 | Arguments.checkOrOr(2, 3, args.length); 13 | 14 | final String input = args[0].asString(); 15 | final String what = args[1].asString(); 16 | final int index = (args.length == 3) ? args[2].asInt() : 0; 17 | 18 | return NumberValue.of(input.indexOf(what, index)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_lastindexof.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.lib.Arguments; 4 | import com.annimon.ownlang.lib.Function; 5 | import com.annimon.ownlang.lib.NumberValue; 6 | import com.annimon.ownlang.lib.Value; 7 | 8 | final class std_lastindexof implements Function { 9 | 10 | @Override 11 | public Value execute(Value[] args) { 12 | Arguments.checkOrOr(2, 3, args.length); 13 | 14 | final String input = args[0].asString(); 15 | final String what = args[1].asString(); 16 | if (args.length == 2) { 17 | return NumberValue.of(input.lastIndexOf(what)); 18 | } 19 | final int index = args[2].asInt(); 20 | return NumberValue.of(input.lastIndexOf(what, index)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_length.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.lib.*; 4 | 5 | final class std_length implements Function { 6 | 7 | @Override 8 | public Value execute(Value[] args) { 9 | Arguments.check(1, args.length); 10 | 11 | final Value value = args[0]; 12 | final int length = switch (value.type()) { 13 | case Types.ARRAY -> ((ArrayValue) value).size(); 14 | case Types.MAP -> ((MapValue) value).size(); 15 | case Types.CLASS -> ((ClassInstance) value).getThisMap().size(); 16 | case Types.STRING -> ((StringValue) value).length(); 17 | case Types.FUNCTION -> { 18 | final Function func = ((FunctionValue) value).getValue(); 19 | yield func.getArgsCount(); 20 | } 21 | default -> 0; 22 | }; 23 | return NumberValue.of(length); 24 | } 25 | } -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_newarray.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.lib.ArrayValue; 4 | import com.annimon.ownlang.lib.Function; 5 | import com.annimon.ownlang.lib.NumberValue; 6 | import com.annimon.ownlang.lib.Value; 7 | 8 | final class std_newarray implements Function { 9 | 10 | @Override 11 | public Value execute(Value[] args) { 12 | return createArray(args, 0); 13 | } 14 | 15 | private ArrayValue createArray(Value[] args, int index) { 16 | final int size = args[index].asInt(); 17 | final int last = args.length - 1; 18 | ArrayValue array = new ArrayValue(size); 19 | if (index == last) { 20 | for (int i = 0; i < size; i++) { 21 | array.set(i, NumberValue.ZERO); 22 | } 23 | } else if (index < last) { 24 | for (int i = 0; i < size; i++) { 25 | array.set(i, createArray(args, index + 1)); 26 | } 27 | } 28 | return array; 29 | } 30 | } -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_readln.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.lib.Function; 4 | import com.annimon.ownlang.lib.StringValue; 5 | import com.annimon.ownlang.lib.Value; 6 | import java.util.Scanner; 7 | 8 | final class std_readln implements Function { 9 | 10 | @Override 11 | public Value execute(Value[] args) { 12 | try (Scanner sc = new Scanner(System.in)) { 13 | return new StringValue(sc.nextLine()); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replace.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.lib.Arguments; 4 | import com.annimon.ownlang.lib.Function; 5 | import com.annimon.ownlang.lib.StringValue; 6 | import com.annimon.ownlang.lib.Value; 7 | 8 | final class std_replace implements Function { 9 | 10 | @Override 11 | public Value execute(Value[] args) { 12 | Arguments.check(3, args.length); 13 | 14 | final String input = args[0].asString(); 15 | final String target = args[1].asString(); 16 | final String replacement = args[2].asString(); 17 | 18 | return new StringValue(input.replace(target, replacement)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replaceall.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.lib.Arguments; 4 | import com.annimon.ownlang.lib.Function; 5 | import com.annimon.ownlang.lib.StringValue; 6 | import com.annimon.ownlang.lib.Value; 7 | 8 | final class std_replaceall implements Function { 9 | 10 | @Override 11 | public Value execute(Value[] args) { 12 | Arguments.check(3, args.length); 13 | 14 | final String input = args[0].asString(); 15 | final String regex = args[1].asString(); 16 | final String replacement = args[2].asString(); 17 | 18 | return new StringValue(input.replaceAll(regex, replacement)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_replacefirst.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.lib.Arguments; 4 | import com.annimon.ownlang.lib.Function; 5 | import com.annimon.ownlang.lib.StringValue; 6 | import com.annimon.ownlang.lib.Value; 7 | 8 | final class std_replacefirst implements Function { 9 | 10 | @Override 11 | public Value execute(Value[] args) { 12 | Arguments.check(3, args.length); 13 | 14 | final String input = args[0].asString(); 15 | final String regex = args[1].asString(); 16 | final String replacement = args[2].asString(); 17 | 18 | return new StringValue(input.replaceFirst(regex, replacement)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sleep.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.lib.Arguments; 4 | import com.annimon.ownlang.lib.Function; 5 | import com.annimon.ownlang.lib.NumberValue; 6 | import com.annimon.ownlang.lib.Value; 7 | 8 | final class std_sleep implements Function { 9 | 10 | @Override 11 | public Value execute(Value[] args) { 12 | Arguments.check(1, args.length); 13 | 14 | try { 15 | Thread.sleep((long) args[0].asNumber()); 16 | } catch (InterruptedException ex) { 17 | Thread.currentThread().interrupt(); 18 | } 19 | return NumberValue.ZERO; 20 | } 21 | } -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_split.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.lib.Arguments; 4 | import com.annimon.ownlang.lib.ArrayValue; 5 | import com.annimon.ownlang.lib.Function; 6 | import com.annimon.ownlang.lib.Value; 7 | 8 | final class std_split implements Function { 9 | 10 | @Override 11 | public Value execute(Value[] args) { 12 | Arguments.checkOrOr(2, 3, args.length); 13 | 14 | final String input = args[0].asString(); 15 | final String regex = args[1].asString(); 16 | final int limit = (args.length == 3) ? args[2].asInt() : 0; 17 | 18 | final String[] parts = input.split(regex, limit); 19 | return ArrayValue.of(parts); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_sprintf.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.lib.Arguments; 4 | import com.annimon.ownlang.lib.Function; 5 | import com.annimon.ownlang.lib.StringValue; 6 | import com.annimon.ownlang.lib.Types; 7 | import com.annimon.ownlang.lib.Value; 8 | 9 | final class std_sprintf implements Function { 10 | 11 | @Override 12 | public Value execute(Value[] args) { 13 | Arguments.checkAtLeast(1, args.length); 14 | 15 | final String format = args[0].asString(); 16 | final Object[] values = new Object[args.length - 1]; 17 | for (int i = 1; i < args.length; i++) { 18 | values[i - 1] = (args[i].type() == Types.NUMBER) 19 | ? args[i].raw() 20 | : args[i].asString(); 21 | } 22 | return new StringValue(String.format(format, values)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_substring.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.lib.Arguments; 4 | import com.annimon.ownlang.lib.Function; 5 | import com.annimon.ownlang.lib.StringValue; 6 | import com.annimon.ownlang.lib.Value; 7 | 8 | final class std_substring implements Function { 9 | 10 | @Override 11 | public Value execute(Value[] args) { 12 | Arguments.checkOrOr(2, 3, args.length); 13 | 14 | final String input = args[0].asString(); 15 | final int startIndex = args[1].asInt(); 16 | 17 | String result; 18 | if (args.length == 2) { 19 | result = input.substring(startIndex); 20 | } else { 21 | final int endIndex = args[2].asInt(); 22 | result = input.substring(startIndex, endIndex); 23 | } 24 | 25 | return new StringValue(result); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_thread.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.Console; 4 | import com.annimon.ownlang.lib.*; 5 | 6 | final class std_thread implements Function { 7 | 8 | @Override 9 | public Value execute(Value[] args) { 10 | Arguments.checkAtLeast(1, args.length); 11 | 12 | Function body; 13 | if (args[0].type() == Types.FUNCTION) { 14 | body = ((FunctionValue) args[0]).getValue(); 15 | } else { 16 | body = ScopeHandler.getFunction(args[0].asString()); 17 | } 18 | 19 | // Shift arguments 20 | final Value[] params = new Value[args.length - 1]; 21 | if (params.length > 0) { 22 | System.arraycopy(args, 1, params, 0, params.length); 23 | } 24 | 25 | final Thread thread = new Thread(() -> body.execute(params)); 26 | thread.setUncaughtExceptionHandler(Console::handleException); 27 | thread.start(); 28 | return NumberValue.ZERO; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tochar.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.lib.Arguments; 4 | import com.annimon.ownlang.lib.Function; 5 | import com.annimon.ownlang.lib.StringValue; 6 | import com.annimon.ownlang.lib.Value; 7 | 8 | final class std_tochar implements Function { 9 | 10 | @Override 11 | public Value execute(Value[] args) { 12 | Arguments.check(1, args.length); 13 | return new StringValue(String.valueOf((char) args[0].asInt())); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_tolowercase.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.lib.Arguments; 4 | import com.annimon.ownlang.lib.Function; 5 | import com.annimon.ownlang.lib.StringValue; 6 | import com.annimon.ownlang.lib.Value; 7 | 8 | final class std_tolowercase implements Function { 9 | 10 | @Override 11 | public Value execute(Value[] args) { 12 | Arguments.check(1, args.length); 13 | return new StringValue(args[0].asString().toLowerCase()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_touppercase.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.lib.Arguments; 4 | import com.annimon.ownlang.lib.Function; 5 | import com.annimon.ownlang.lib.StringValue; 6 | import com.annimon.ownlang.lib.Value; 7 | 8 | final class std_touppercase implements Function { 9 | 10 | @Override 11 | public Value execute(Value[] args) { 12 | Arguments.check(1, args.length); 13 | return new StringValue(args[0].asString().toUpperCase()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_trim.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.lib.Arguments; 4 | import com.annimon.ownlang.lib.Function; 5 | import com.annimon.ownlang.lib.StringValue; 6 | import com.annimon.ownlang.lib.Value; 7 | 8 | final class std_trim implements Function { 9 | 10 | @Override 11 | public Value execute(Value[] args) { 12 | Arguments.check(1, args.length); 13 | return new StringValue(args[0].asString().trim()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/std/std_try.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.std; 2 | 3 | import com.annimon.ownlang.lib.*; 4 | 5 | final class std_try implements Function { 6 | 7 | @Override 8 | public Value execute(Value[] args) { 9 | Arguments.checkOrOr(1, 2, args.length); 10 | try { 11 | return ValueUtils.consumeFunction(args[0], 0).execute(); 12 | } catch (Exception ex) { 13 | if (args.length == 2) { 14 | switch (args[1].type()) { 15 | case Types.FUNCTION: 16 | final String message = ex.getMessage(); 17 | final Function catchFunction = ((FunctionValue) args[1]).getValue(); 18 | return catchFunction.execute( 19 | new StringValue(ex.getClass().getName()), 20 | new StringValue(message == null ? "" : message)); 21 | default: 22 | return args[1]; 23 | } 24 | } 25 | return NumberValue.MINUS_ONE; 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /modules/main/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules.yaml; 2 | 3 | import com.annimon.ownlang.lib.*; 4 | import com.annimon.ownlang.modules.Module; 5 | import java.util.Collections; 6 | import java.util.Map; 7 | 8 | /** 9 | * 10 | * @author aNNiMON 11 | */ 12 | public final class yaml implements Module { 13 | 14 | @Override 15 | public Map constants() { 16 | return Collections.emptyMap(); 17 | } 18 | 19 | @Override 20 | public Map functions() { 21 | return Map.of( 22 | "yamlencode", new yaml_encode(), 23 | "yamldecode", new yaml_decode() 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /modules/server/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'com.github.johnrengelman.shadow' version '8.1.1' 4 | } 5 | 6 | group = 'com.annimon.module' 7 | version = '1.0.0' 8 | 9 | dependencies { 10 | compileOnlyApi project(":ownlang-core") 11 | implementation "io.javalin:javalin:${versions.javalin}" 12 | implementation "org.slf4j:slf4j-simple:${versions.slf4j}" 13 | implementation "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}" 14 | 15 | testImplementation platform("org.junit:junit-bom:${versions.junit}") 16 | testImplementation 'org.junit.jupiter:junit-jupiter' 17 | } 18 | 19 | test { 20 | useJUnitPlatform() 21 | } -------------------------------------------------------------------------------- /modules/socket/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'com.github.johnrengelman.shadow' version '8.1.1' 4 | } 5 | 6 | group = 'com.annimon.module' 7 | version = '1.0.0' 8 | 9 | dependencies { 10 | compileOnlyApi project(":ownlang-core") 11 | 12 | implementation ("io.socket:socket.io-client:${versions.socket}") { 13 | exclude group: 'org.json', module: 'json' 14 | exclude group: 'com.squareup.okhttp3', module: 'okhttp' 15 | } 16 | compileOnly "com.squareup.okhttp3:okhttp:${versions.okhttp}" 17 | compileOnly "org.json:json:${versions.json}" 18 | 19 | testImplementation platform("org.junit:junit-bom:${versions.junit}") 20 | testImplementation 'org.junit.jupiter:junit-jupiter' 21 | } 22 | 23 | test { 24 | useJUnitPlatform() 25 | } -------------------------------------------------------------------------------- /ownlang-core/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | } 4 | 5 | group = 'com.annimon' 6 | version = versions.project 7 | 8 | dependencies { 9 | implementation "org.json:json:${versions.json}" 10 | 11 | testImplementation platform("org.junit:junit-bom:${versions.junit}") 12 | testImplementation 'org.junit.jupiter:junit-jupiter' 13 | } 14 | 15 | test { 16 | useJUnitPlatform() 17 | } 18 | 19 | ext.generatedJavaDir = "${project.buildDir}/gen/src/main/java" 20 | sourceSets.main.java.srcDirs += project.generatedJavaDir 21 | 22 | tasks.register('generateJavaSources') { 23 | doLast { 24 | def source = """ 25 | package com.annimon.ownlang; 26 | class Gen { 27 | private Gen() {} 28 | public static final String BUILD_DATE = "${new Date().format('YYMMdd')}"; 29 | } 30 | """.stripIndent() 31 | def genFile = new File("${project.generatedJavaDir}/com/annimon/ownlang/Gen.java") 32 | genFile.getParentFile().mkdirs() 33 | genFile.write(source) 34 | } 35 | } 36 | compileJava.dependsOn(generateJavaSources) -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/Shared.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang; 2 | 3 | public final class Shared { 4 | private static String[] ownlangArgs = new String[0]; 5 | 6 | public static String[] getOwnlangArgs() { 7 | return ownlangArgs; 8 | } 9 | 10 | public static void setOwnlangArgs(String[] args) { 11 | ownlangArgs = args; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/Version.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang; 2 | 3 | public final class Version { 4 | public static final int VERSION_MAJOR = 2; 5 | public static final int VERSION_MINOR = 1; 6 | public static final int VERSION_PATCH = 0; 7 | 8 | public static final String VERSION = VERSION_MAJOR + "." 9 | + VERSION_MINOR + "." + VERSION_PATCH 10 | + "_" + Gen.BUILD_DATE; 11 | } -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/exceptions/ArgumentsMismatchException.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.exceptions; 2 | 3 | import com.annimon.ownlang.util.Range; 4 | 5 | public final class ArgumentsMismatchException extends OwnLangRuntimeException { 6 | 7 | public ArgumentsMismatchException() { 8 | } 9 | 10 | public ArgumentsMismatchException(String message) { 11 | super(message); 12 | } 13 | 14 | public ArgumentsMismatchException(String message, Range range) { 15 | super(message, range); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OperationIsNotSupportedException.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.exceptions; 2 | 3 | public final class OperationIsNotSupportedException extends OwnLangRuntimeException { 4 | 5 | public OperationIsNotSupportedException(Object operation) { 6 | super("Operation " + operation + " is not supported"); 7 | } 8 | 9 | public OperationIsNotSupportedException(Object operation, String message) { 10 | super("Operation " + operation + " is not supported " + message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/exceptions/OwnLangRuntimeException.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.exceptions; 2 | 3 | import com.annimon.ownlang.util.Range; 4 | import com.annimon.ownlang.util.SourceLocatedError; 5 | 6 | /** 7 | * Base type for all runtime exceptions 8 | */ 9 | public class OwnLangRuntimeException extends RuntimeException implements SourceLocatedError { 10 | 11 | private final Range range; 12 | 13 | public OwnLangRuntimeException() { 14 | super(); 15 | this.range = null; 16 | } 17 | 18 | public OwnLangRuntimeException(Exception ex) { 19 | super(ex); 20 | this.range = null; 21 | } 22 | 23 | public OwnLangRuntimeException(String message) { 24 | this(message, (Range) null); 25 | } 26 | 27 | public OwnLangRuntimeException(String message, Range range) { 28 | super(message); 29 | this.range = range; 30 | } 31 | 32 | public OwnLangRuntimeException(String message, Throwable ex) { 33 | super(message, ex); 34 | this.range = null; 35 | } 36 | 37 | @Override 38 | public Range getRange() { 39 | return range; 40 | } 41 | } -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/exceptions/PatternMatchingException.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.exceptions; 2 | 3 | public final class PatternMatchingException extends OwnLangRuntimeException { 4 | 5 | public PatternMatchingException() { 6 | } 7 | 8 | public PatternMatchingException(String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/exceptions/StoppedException.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.exceptions; 2 | 3 | public class StoppedException extends OwnLangRuntimeException { 4 | 5 | 6 | } 7 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/exceptions/TypeException.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.exceptions; 2 | 3 | import com.annimon.ownlang.util.Range; 4 | 5 | public final class TypeException extends OwnLangRuntimeException { 6 | 7 | public TypeException(String message) { 8 | super(message); 9 | } 10 | 11 | public TypeException(String message, Range range) { 12 | super(message, range); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownClassException.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.exceptions; 2 | 3 | import com.annimon.ownlang.util.Range; 4 | 5 | public final class UnknownClassException extends OwnLangRuntimeException { 6 | 7 | private final String className; 8 | 9 | public UnknownClassException(String name, Range range) { 10 | super("Unknown class " + name, range); 11 | this.className = name; 12 | } 13 | 14 | public String getClassName() { 15 | return className; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownFunctionException.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.exceptions; 2 | 3 | import com.annimon.ownlang.util.Range; 4 | 5 | public final class UnknownFunctionException extends OwnLangRuntimeException { 6 | 7 | private final String functionName; 8 | 9 | public UnknownFunctionException(String name) { 10 | super("Unknown function " + name); 11 | this.functionName = name; 12 | } 13 | 14 | public UnknownFunctionException(String name, Range range) { 15 | super("Unknown function " + name, range); 16 | this.functionName = name; 17 | } 18 | 19 | public String getFunctionName() { 20 | return functionName; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/exceptions/UnknownPropertyException.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.exceptions; 2 | 3 | public final class UnknownPropertyException extends OwnLangRuntimeException { 4 | 5 | private final String propertyName; 6 | 7 | public UnknownPropertyException(String name) { 8 | super("Unknown property " + name); 9 | this.propertyName = name; 10 | } 11 | 12 | public String getPropertyName() { 13 | return propertyName; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/exceptions/VariableDoesNotExistsException.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.exceptions; 2 | 3 | import com.annimon.ownlang.util.Range; 4 | 5 | public final class VariableDoesNotExistsException extends OwnLangRuntimeException { 6 | 7 | private final String variable; 8 | 9 | public VariableDoesNotExistsException(String variable, Range range) { 10 | super("Variable " + variable + " does not exists", range); 11 | this.variable = variable; 12 | } 13 | 14 | public String getVariable() { 15 | return variable; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/lib/AutoCloseableScope.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.lib; 2 | 3 | public final class AutoCloseableScope implements AutoCloseable { 4 | @Override 5 | public void close() { 6 | ScopeHandler.pop(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassDeclaration.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.lib; 2 | 3 | import java.util.List; 4 | 5 | public record ClassDeclaration( 6 | String name, 7 | List classFields, 8 | List classMethods 9 | ) implements Instantiable { 10 | 11 | /** 12 | * Create an instance and put evaluated fields with method declarations 13 | * @return new {@link ClassInstance} 14 | */ 15 | public ClassInstance newInstance(Value[] args) { 16 | final var instance = new ClassInstance(name); 17 | for (ClassField f : classFields) { 18 | instance.addField(f); 19 | } 20 | for (ClassMethod m : classMethods) { 21 | instance.addMethod(m); 22 | } 23 | return instance.callConstructor(args); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/lib/ClassField.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.lib; 2 | 3 | public record ClassField( 4 | String name, 5 | EvaluableValue evaluableValue 6 | ) { 7 | } 8 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/lib/EvaluableValue.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.lib; 2 | 3 | public interface EvaluableValue { 4 | 5 | Value eval(); 6 | } 7 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/lib/Function.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.lib; 2 | 3 | /** 4 | * 5 | * @author aNNiMON 6 | */ 7 | public interface Function { 8 | 9 | Value execute(Value... args); 10 | 11 | default int getArgsCount() { 12 | return 0; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/lib/Functions.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.lib; 2 | 3 | /** 4 | * 5 | * @author aNNiMON 6 | */ 7 | public final class Functions { 8 | private Functions() { } 9 | 10 | /** 11 | * @deprecated This function remains for backward compatibility with old separate modules 12 | * Use {@link ScopeHandler#setFunction(String, Function)} 13 | */ 14 | @Deprecated 15 | public static void set(String key, Function function) { 16 | ScopeHandler.setFunction(key, function); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/lib/Instantiable.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.lib; 2 | 3 | /** 4 | * Interface for values that supports creating instances with `new` keyword. 5 | */ 6 | public interface Instantiable { 7 | 8 | Value newInstance(Value[] args); 9 | } 10 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/lib/ModuleLoader.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.lib; 2 | 3 | import com.annimon.ownlang.modules.Module; 4 | 5 | public final class ModuleLoader { 6 | private static final String PACKAGE = "com.annimon.ownlang.modules.%s.%s"; 7 | 8 | private ModuleLoader() { } 9 | 10 | public static Module load(String name) { 11 | try { 12 | return (Module) Class.forName(String.format(PACKAGE, name, name)) 13 | .getDeclaredConstructor() 14 | .newInstance(); 15 | } catch (Exception ex) { 16 | throw new RuntimeException("Unable to load module " + name, ex); 17 | } 18 | } 19 | 20 | public static void loadAndUse(String name) { 21 | final var rootScope = ScopeHandler.rootScope(); 22 | if (rootScope.isModuleLoaded(name)) return; 23 | 24 | final var module = load(name); 25 | rootScope.getConstants().putAll(module.constants()); 26 | rootScope.getFunctions().putAll(module.functions()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/lib/Types.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.lib; 2 | 3 | public final class Types { 4 | 5 | public static final int 6 | OBJECT = 0, 7 | NUMBER = 1, 8 | STRING = 2, 9 | ARRAY = 3, 10 | MAP = 4, 11 | FUNCTION = 5, 12 | CLASS = 6; 13 | 14 | private static final int FIRST = OBJECT; 15 | private static final int LAST = CLASS; 16 | private static final String[] NAMES = { 17 | "object", "number", "string", "array", "map", "function", "class" 18 | }; 19 | 20 | public static String typeToString(int type) { 21 | if (FIRST <= type && type <= LAST) { 22 | return NAMES[type]; 23 | } 24 | return "unknown (" + type + ")"; 25 | } 26 | 27 | private Types() { } 28 | } 29 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/lib/Value.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.lib; 2 | 3 | /** 4 | * 5 | * @author aNNiMON 6 | */ 7 | public interface Value extends Comparable { 8 | 9 | Object raw(); 10 | 11 | int asInt(); 12 | 13 | double asNumber(); 14 | 15 | String asString(); 16 | 17 | int type(); 18 | 19 | default Object asJavaObject() { 20 | return raw(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/lib/Variables.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.lib; 2 | 3 | /** 4 | * 5 | * @author aNNiMON 6 | */ 7 | public final class Variables { 8 | private Variables() { } 9 | 10 | /** 11 | * @deprecated This function remains for backward compatibility with old separate modules 12 | * Use {@link ScopeHandler#setVariable(String, Value)} 13 | */ 14 | @Deprecated 15 | public static void set(String name, Value value) { 16 | ScopeHandler.setVariable(name, value); 17 | } 18 | 19 | /** 20 | * @deprecated This function remains for backward compatibility with old separate modules 21 | * Use {@link ScopeHandler#setConstant(String, Value)} 22 | */ 23 | @Deprecated 24 | public static void define(String name, Value value) { 25 | ScopeHandler.setConstant(name, value); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/modules/Module.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.modules; 2 | 3 | import com.annimon.ownlang.lib.Function; 4 | import com.annimon.ownlang.lib.Value; 5 | import java.util.Map; 6 | 7 | /** 8 | * Main interface for modules 9 | * @author aNNiMON 10 | */ 11 | public interface Module { 12 | 13 | Map constants(); 14 | 15 | Map functions(); 16 | } 17 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/outputsettings/OutputSettings.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.outputsettings; 2 | 3 | import java.io.File; 4 | 5 | public sealed interface OutputSettings permits ConsoleOutputSettings, StringOutputSettings { 6 | 7 | String newline(); 8 | 9 | void print(String value); 10 | 11 | void print(Object value); 12 | 13 | void println(); 14 | 15 | void println(String value); 16 | 17 | void println(Object value); 18 | 19 | String getText(); 20 | 21 | void error(Throwable throwable); 22 | 23 | void error(CharSequence value); 24 | 25 | File fileInstance(String path); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/stages/ScopedStage.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.stages; 2 | 3 | import java.util.function.Consumer; 4 | 5 | public class ScopedStage implements Stage { 6 | 7 | private final String stageName; 8 | private final Stage stage; 9 | private final Consumer startStage; 10 | private final Consumer endStage; 11 | 12 | ScopedStage(String stageName, Stage stage, 13 | Consumer startStage, Consumer endStage) { 14 | this.stageName = stageName; 15 | this.stage = stage; 16 | this.startStage = startStage; 17 | this.endStage = endStage; 18 | } 19 | 20 | @Override 21 | public R perform(StagesData stagesData, I input) { 22 | try { 23 | startStage.accept(stageName); 24 | return stage.perform(stagesData, input); 25 | } finally { 26 | endStage.accept(stageName); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/stages/ScopedStageFactory.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.stages; 2 | 3 | import java.util.function.Consumer; 4 | 5 | public final class ScopedStageFactory { 6 | private final Consumer startStage; 7 | private final Consumer endStage; 8 | 9 | public ScopedStageFactory(Consumer startStage, Consumer endStage) { 10 | this.startStage = startStage; 11 | this.endStage = endStage; 12 | } 13 | 14 | public ScopedStage create(String stageName, Stage stage) { 15 | return new ScopedStage<>(stageName, stage, startStage, endStage); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/stages/Stage.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.stages; 2 | 3 | public interface Stage { 4 | R perform(StagesData stagesData, I input); 5 | 6 | default Stage then(Stage next) { 7 | return (scope, input) -> { 8 | R result = perform(scope, input); 9 | return next.perform(scope, result); 10 | }; 11 | } 12 | 13 | default Stage thenConditional(boolean enabled, Stage next) { 14 | return enabled ? then(next) : this; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesData.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.stages; 2 | 3 | import java.util.function.Supplier; 4 | 5 | public interface StagesData { 6 | 7 | T get(String tag); 8 | 9 | default T getOrDefault(String tag, T other) { 10 | T value = get(tag); 11 | return value != null ? value : other; 12 | } 13 | 14 | default T getOrDefault(String tag, Supplier otherSuppler) { 15 | T value = get(tag); 16 | return value != null ? value : otherSuppler.get(); 17 | } 18 | 19 | void put(String tag, Object input); 20 | } 21 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/stages/StagesDataMap.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.stages; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class StagesDataMap implements StagesData { 7 | private final Map data = new HashMap<>(); 8 | 9 | @SuppressWarnings("unchecked") 10 | @Override 11 | public T get(String tag) { 12 | return (T) data.get(tag); 13 | } 14 | 15 | @Override 16 | public void put(String tag, Object input) { 17 | data.put(tag, input); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsStackTraceFormatterStage.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.util; 2 | 3 | import com.annimon.ownlang.Console; 4 | import com.annimon.ownlang.stages.Stage; 5 | import com.annimon.ownlang.stages.StagesData; 6 | 7 | public class ErrorsStackTraceFormatterStage implements Stage, String> { 8 | 9 | @Override 10 | public String perform(StagesData stagesData, Iterable input) { 11 | final var sb = new StringBuilder(); 12 | for (SourceLocatedError error : input) { 13 | if (!error.hasStackTrace()) continue; 14 | for (StackTraceElement el : error.getStackTrace()) { 15 | sb.append("\t").append(el).append(Console.newline()); 16 | } 17 | } 18 | return sb.toString(); 19 | } 20 | } -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/util/ExceptionConverterStage.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.util; 2 | 3 | import com.annimon.ownlang.stages.Stage; 4 | import com.annimon.ownlang.stages.StagesData; 5 | 6 | public class ExceptionConverterStage implements Stage { 7 | @Override 8 | public SourceLocatedError perform(StagesData stagesData, Exception ex) { 9 | if (ex instanceof SourceLocatedError sle) return sle; 10 | return new SimpleError(ex.getMessage()); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/util/ExceptionStackTraceToStringStage.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.util; 2 | 3 | import com.annimon.ownlang.stages.Stage; 4 | import com.annimon.ownlang.stages.StagesData; 5 | import java.io.ByteArrayOutputStream; 6 | import java.io.PrintStream; 7 | import java.nio.charset.StandardCharsets; 8 | 9 | public class ExceptionStackTraceToStringStage implements Stage { 10 | @Override 11 | public String perform(StagesData stagesData, Exception ex) { 12 | final var baos = new ByteArrayOutputStream(); 13 | try (final PrintStream ps = new PrintStream(baos)) { 14 | for (StackTraceElement traceElement : ex.getStackTrace()) { 15 | ps.println("\tat " + traceElement); 16 | } 17 | } 18 | return baos.toString(StandardCharsets.UTF_8); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/util/Pos.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.util; 2 | 3 | public record Pos(int row, int col) { 4 | public static final Pos UNKNOWN = new Pos(-1, -1); 5 | public static final Pos ZERO = new Pos(0, 0); 6 | 7 | public Pos normalize() { 8 | return new Pos(Math.max(0, row - 1), Math.max(0, col - 1)); 9 | } 10 | 11 | public String format() { 12 | return "[" + row + ":" + col + "]"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/util/Range.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.util; 2 | 3 | import java.util.Objects; 4 | 5 | public record Range(Pos start, Pos end) { 6 | public static final Range ZERO = new Range(Pos.ZERO, Pos.ZERO); 7 | 8 | public Range normalize() { 9 | return new Range(start.normalize(), end.normalize()); 10 | } 11 | 12 | public boolean isEqualPosition() { 13 | return Objects.equals(start, end); 14 | } 15 | 16 | public boolean isOnSameLine() { 17 | return start.row() == end.row(); 18 | } 19 | 20 | public String format() { 21 | if (isEqualPosition()) 22 | return start.format(); 23 | else if (isOnSameLine()) { 24 | return "[%d:%d~%d]".formatted(start.row(), start.col(), end.col()); 25 | } else { 26 | return start.format() + "..." + end.format(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/util/SimpleError.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.util; 2 | 3 | public record SimpleError(String message, Range range) implements SourceLocatedError { 4 | public SimpleError(String message) { 5 | this(message, null); 6 | } 7 | 8 | @Override 9 | public String getMessage() { 10 | return message; 11 | } 12 | 13 | @Override 14 | public String toString() { 15 | return message; 16 | } 17 | 18 | @Override 19 | public Range getRange() { 20 | return range; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLocatedError.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.util; 2 | 3 | public interface SourceLocatedError extends SourceLocation { 4 | 5 | String getMessage(); 6 | 7 | default StackTraceElement[] getStackTrace() { 8 | return new StackTraceElement[0]; 9 | } 10 | 11 | default boolean hasStackTrace() { 12 | return !stackTraceIsEmpty(); 13 | } 14 | 15 | private boolean stackTraceIsEmpty() { 16 | final var st = getStackTrace(); 17 | return st == null || st.length == 0; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/util/SourceLocation.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.util; 2 | 3 | public interface SourceLocation { 4 | 5 | default Range getRange() { 6 | return null; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSource.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.util.input; 2 | 3 | import java.io.IOException; 4 | 5 | public interface InputSource { 6 | String getPath(); 7 | 8 | String load() throws IOException; 9 | 10 | default String getBasePath() { 11 | final String normalizedPath = getPath().replace("\\", "/"); 12 | int i = normalizedPath.lastIndexOf("/"); 13 | if (i == -1) return ""; 14 | return normalizedPath.substring(0, i + 1); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceFile.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.util.input; 2 | 3 | import java.io.FileInputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | 9 | public record InputSourceFile(String path) implements InputSource { 10 | 11 | @Override 12 | public String getPath() { 13 | return path; 14 | } 15 | 16 | @Override 17 | public String load() throws IOException { 18 | if (Files.isReadable(Path.of(path))) { 19 | try (InputStream is = new FileInputStream(path)) { 20 | return SourceLoaderStage.readStream(is); 21 | } 22 | } 23 | throw new IOException(path + " not found"); 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return "File " + path; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceProgram.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.util.input; 2 | 3 | public record InputSourceProgram(String program) implements InputSource { 4 | 5 | @Override 6 | public String getPath() { 7 | return "."; 8 | } 9 | 10 | @Override 11 | public String load() { 12 | return program; 13 | } 14 | 15 | @Override 16 | public String toString() { 17 | return "Program"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceResource.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.util.input; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | 6 | public record InputSourceResource(String path) implements InputSource { 7 | 8 | @Override 9 | public String getPath() { 10 | return path; 11 | } 12 | 13 | @Override 14 | public String load() throws IOException { 15 | try (InputStream is = getClass().getResourceAsStream(path)) { 16 | if (is != null) { 17 | return SourceLoaderStage.readStream(is); 18 | } 19 | } 20 | throw new IOException(path + " not found"); 21 | } 22 | 23 | @Override 24 | public String getBasePath() { 25 | return "resource:" + InputSource.super.getBasePath(); 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return "Resource " + path; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ownlang-desktop/src/main/resources/scripts/listscripts.own: -------------------------------------------------------------------------------- 1 | println "Available scripts: 2 | checkUpdate - checks updates on GitHub 3 | own - own package manager 4 | 5 | To run a script use command: 6 | ownlang run checkUpdate 7 | " -------------------------------------------------------------------------------- /ownlang-desktop/src/main/resources/scripts/own/Projects.own: -------------------------------------------------------------------------------- 1 | use files 2 | 3 | class Projects { 4 | def Projects(ctx) { 5 | this.ctx = ctx 6 | this.defaultPrograms = { 7 | "main.own" : "use std\n\necho(\"Hello, world!\", ARGS)" 8 | } 9 | } 10 | 11 | def savePrograms() { 12 | this.ctx.logger.debug("Projects.savePrograms") 13 | for name, content : this.defaultPrograms { 14 | if exists(name) continue 15 | f = fopen(name, "w") 16 | writeText(f, content) 17 | flush(f) 18 | fclose(f) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ownlang-parser/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | } 4 | 5 | group = 'com.annimon' 6 | version = versions.project 7 | 8 | dependencies { 9 | api project(':ownlang-core') 10 | 11 | testImplementation project(':modules:main') 12 | testImplementation platform("org.junit:junit-bom:${versions.junit}") 13 | testImplementation 'org.junit.jupiter:junit-jupiter' 14 | testImplementation "org.junit.jupiter:junit-jupiter-params" 15 | testImplementation("org.assertj:assertj-core:${versions.assertj}") 16 | testImplementation "org.openjdk.jmh:jmh-core:${versions.jmh}" 17 | testImplementation "org.openjdk.jmh:jmh-generator-annprocess:${versions.jmh}" 18 | testAnnotationProcessor "org.openjdk.jmh:jmh-generator-annprocess:${versions.jmh}" 19 | } 20 | 21 | test { 22 | useJUnitPlatform() 23 | } -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OwnLangParserException.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.exceptions; 2 | 3 | import com.annimon.ownlang.util.SourceLocatedError; 4 | import java.util.Collection; 5 | import java.util.List; 6 | 7 | /** 8 | * Single Exception for Lexer, Parser and Linter errors 9 | */ 10 | public class OwnLangParserException extends RuntimeException { 11 | 12 | private final Collection errors; 13 | 14 | public OwnLangParserException(SourceLocatedError error) { 15 | super(error.toString()); 16 | errors = List.of(error);; 17 | } 18 | 19 | public OwnLangParserException(Collection errors) { 20 | super(errors.toString()); 21 | this.errors = errors; 22 | } 23 | 24 | public Collection getParseErrors() { 25 | return errors; 26 | } 27 | } -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.exceptions; 2 | 3 | import com.annimon.ownlang.util.Range; 4 | 5 | /** 6 | * 7 | * @author aNNiMON 8 | */ 9 | public final class ParseException extends RuntimeException { 10 | 11 | private final Range range; 12 | 13 | public ParseException(String message, Range range) { 14 | super(message); 15 | this.range = range; 16 | } 17 | 18 | public Range getRange() { 19 | return range; 20 | } 21 | } -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/ParserMetadata.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser; 2 | 3 | public record ParserMetadata( 4 | String basePath 5 | ) { 6 | } 7 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/SourceLoader.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.FileInputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.nio.charset.StandardCharsets; 8 | 9 | public final class SourceLoader { 10 | 11 | private SourceLoader() { } 12 | 13 | public static String readSource(String name) throws IOException { 14 | InputStream is = SourceLoader.class.getResourceAsStream("/" + name); 15 | if (is != null) return readAndCloseStream(is); 16 | 17 | is = new FileInputStream(name); 18 | return readAndCloseStream(is); 19 | } 20 | 21 | public static String readAndCloseStream(InputStream is) throws IOException { 22 | final ByteArrayOutputStream result = new ByteArrayOutputStream(); 23 | final int bufferSize = 1024; 24 | final byte[] buffer = new byte[bufferSize]; 25 | int read; 26 | while ((read = is.read(buffer)) != -1) { 27 | result.write(buffer, 0, read); 28 | } 29 | is.close(); 30 | return result.toString(StandardCharsets.UTF_8); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/Token.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser; 2 | 3 | import com.annimon.ownlang.util.Pos; 4 | 5 | /** 6 | * @author aNNiMON 7 | */ 8 | public record Token(TokenType type, String text, Pos pos) { 9 | 10 | public String shortDescription() { 11 | return type().name() + " " + text; 12 | } 13 | 14 | @Override 15 | public String toString() { 16 | return type.name() + " " + pos().format() + " " + text; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Accessible.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.ast; 2 | 3 | import com.annimon.ownlang.lib.Value; 4 | 5 | public interface Accessible extends Node { 6 | 7 | Value get(); 8 | 9 | Value set(Value value); 10 | } 11 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Argument.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.ast; 2 | 3 | public record Argument(String name, Node valueExpr) { 4 | 5 | public Argument(String name) { 6 | this(name, null); 7 | } 8 | 9 | @Override 10 | public String toString() { 11 | return name + (valueExpr == null ? "" : " = " + valueExpr); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ArrayExpression.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.ast; 2 | 3 | import com.annimon.ownlang.lib.ArrayValue; 4 | import com.annimon.ownlang.lib.Value; 5 | import java.util.List; 6 | 7 | /** 8 | * 9 | * @author aNNiMON 10 | */ 11 | public final class ArrayExpression implements Node { 12 | 13 | public final List elements; 14 | 15 | public ArrayExpression(List arguments) { 16 | this.elements = arguments; 17 | } 18 | 19 | @Override 20 | public Value eval() { 21 | final int size = elements.size(); 22 | final ArrayValue array = new ArrayValue(size); 23 | for (int i = 0; i < size; i++) { 24 | array.set(i, elements.get(i).eval()); 25 | } 26 | return array; 27 | } 28 | 29 | @Override 30 | public void accept(Visitor visitor) { 31 | visitor.visit(this); 32 | } 33 | 34 | @Override 35 | public R accept(ResultVisitor visitor, T t) { 36 | return visitor.visit(this, t); 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return elements.toString(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.ast; 2 | 3 | import com.annimon.ownlang.lib.Value; 4 | import com.annimon.ownlang.util.Range; 5 | import com.annimon.ownlang.util.SourceLocation; 6 | 7 | /** 8 | * 9 | * @author aNNiMON 10 | */ 11 | public final class BreakStatement extends RuntimeException implements Statement, SourceLocation { 12 | private final Range range; 13 | 14 | public BreakStatement(Range range) { 15 | this.range = range; 16 | } 17 | 18 | @Override 19 | public Range getRange() { 20 | return range; 21 | } 22 | 23 | @Override 24 | public Value eval() { 25 | throw this; 26 | } 27 | 28 | @Override 29 | public void accept(Visitor visitor) { 30 | visitor.visit(this); 31 | } 32 | 33 | @Override 34 | public R accept(ResultVisitor visitor, T t) { 35 | return visitor.visit(this, t); 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return "break"; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.ast; 2 | 3 | import com.annimon.ownlang.lib.Value; 4 | import com.annimon.ownlang.util.Range; 5 | import com.annimon.ownlang.util.SourceLocation; 6 | 7 | /** 8 | * 9 | * @author aNNiMON 10 | */ 11 | public final class ContinueStatement extends RuntimeException implements Statement, SourceLocation { 12 | private final Range range; 13 | 14 | public ContinueStatement(Range range) { 15 | this.range = range; 16 | } 17 | 18 | @Override 19 | public Range getRange() { 20 | return range; 21 | } 22 | 23 | @Override 24 | public Value eval() { 25 | throw this; 26 | } 27 | 28 | @Override 29 | public void accept(Visitor visitor) { 30 | visitor.visit(this); 31 | } 32 | 33 | @Override 34 | public R accept(ResultVisitor visitor, T t) { 35 | return visitor.visit(this, t); 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return "continue"; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ExprStatement.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.ast; 2 | 3 | import com.annimon.ownlang.lib.Value; 4 | 5 | /** 6 | * Wrapper for expressions, which can be used as statements. 7 | * 8 | * @author aNNiMON 9 | */ 10 | public final class ExprStatement extends InterruptableNode implements Statement { 11 | 12 | public final Node expr; 13 | 14 | public ExprStatement(Node function) { 15 | this.expr = function; 16 | } 17 | 18 | @Override 19 | public Value eval() { 20 | super.interruptionCheck(); 21 | return expr.eval(); 22 | } 23 | 24 | @Override 25 | public void accept(Visitor visitor) { 26 | visitor.visit(this); 27 | } 28 | 29 | @Override 30 | public R accept(ResultVisitor visitor, T t) { 31 | return visitor.visit(this, t); 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return expr.toString(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/FunctionReferenceExpression.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.ast; 2 | 3 | import com.annimon.ownlang.lib.*; 4 | 5 | /** 6 | * 7 | * @author aNNiMON 8 | */ 9 | public final class FunctionReferenceExpression extends InterruptableNode { 10 | 11 | public final String name; 12 | 13 | public FunctionReferenceExpression(String name) { 14 | this.name = name; 15 | } 16 | 17 | @Override 18 | public FunctionValue eval() { 19 | super.interruptionCheck(); 20 | return new FunctionValue(ScopeHandler.getFunction(name)); 21 | } 22 | 23 | @Override 24 | public void accept(Visitor visitor) { 25 | visitor.visit(this); 26 | } 27 | 28 | @Override 29 | public R accept(ResultVisitor visitor, T t) { 30 | return visitor.visit(this, t); 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | return "::" + name; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/InterruptableNode.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.ast; 2 | 3 | import com.annimon.ownlang.exceptions.StoppedException; 4 | 5 | public abstract class InterruptableNode implements Node { 6 | 7 | public static final int RUNNING = 0, PAUSED = 1, STOPPED = 2; 8 | 9 | private static volatile int state; 10 | 11 | public static void run() { 12 | state = RUNNING; 13 | } 14 | 15 | public static void pause() { 16 | state = PAUSED; 17 | } 18 | 19 | public static void stop() { 20 | state = STOPPED; 21 | } 22 | 23 | protected void interruptionCheck() { 24 | if (state == RUNNING) return; 25 | if (state == STOPPED) { 26 | throw new StoppedException(); 27 | } 28 | try { 29 | while (state == PAUSED) { 30 | Thread.sleep(1000); 31 | } 32 | } catch (InterruptedException ioe) { 33 | Thread.currentThread().interrupt(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Node.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.ast; 2 | 3 | import com.annimon.ownlang.lib.Value; 4 | 5 | /** 6 | * 7 | * @author aNNiMON 8 | */ 9 | public interface Node { 10 | 11 | Value eval(); 12 | 13 | void accept(Visitor visitor); 14 | 15 | R accept(ResultVisitor visitor, T input); 16 | } 17 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/PrintStatement.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.ast; 2 | 3 | import com.annimon.ownlang.Console; 4 | import com.annimon.ownlang.lib.NumberValue; 5 | import com.annimon.ownlang.lib.Value; 6 | 7 | /** 8 | * 9 | * @author aNNiMON 10 | */ 11 | public final class PrintStatement extends InterruptableNode implements Statement { 12 | 13 | public final Node expression; 14 | 15 | public PrintStatement(Node expression) { 16 | this.expression = expression; 17 | } 18 | 19 | @Override 20 | public Value eval() { 21 | super.interruptionCheck(); 22 | Console.print(expression.eval().asString()); 23 | return NumberValue.ZERO; 24 | } 25 | 26 | @Override 27 | public void accept(Visitor visitor) { 28 | visitor.visit(this); 29 | } 30 | 31 | @Override 32 | public R accept(ResultVisitor visitor, T t) { 33 | return visitor.visit(this, t); 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "print " + expression; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/PrintlnStatement.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.ast; 2 | 3 | import com.annimon.ownlang.Console; 4 | import com.annimon.ownlang.lib.NumberValue; 5 | import com.annimon.ownlang.lib.Value; 6 | 7 | /** 8 | * 9 | * @author aNNiMON 10 | */ 11 | public final class PrintlnStatement extends InterruptableNode implements Statement { 12 | 13 | public final Node expression; 14 | 15 | public PrintlnStatement(Node expression) { 16 | this.expression = expression; 17 | } 18 | 19 | @Override 20 | public Value eval() { 21 | super.interruptionCheck(); 22 | Console.println(expression.eval().asString()); 23 | return NumberValue.ZERO; 24 | } 25 | 26 | @Override 27 | public void accept(Visitor visitor) { 28 | visitor.visit(this); 29 | } 30 | 31 | @Override 32 | public R accept(ResultVisitor visitor, T t) { 33 | return visitor.visit(this, t); 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "println " + expression; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ReturnStatement.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.ast; 2 | 3 | import com.annimon.ownlang.lib.Value; 4 | 5 | /** 6 | * 7 | * @author aNNiMON 8 | */ 9 | public final class ReturnStatement extends RuntimeException implements Statement { 10 | 11 | public final Node expression; 12 | private Value result; 13 | 14 | public ReturnStatement(Node expression) { 15 | this.expression = expression; 16 | } 17 | 18 | public Value getResult() { 19 | return result; 20 | } 21 | 22 | @Override 23 | public Value eval() { 24 | result = expression.eval(); 25 | throw this; 26 | } 27 | 28 | @Override 29 | public void accept(Visitor visitor) { 30 | visitor.visit(this); 31 | } 32 | 33 | @Override 34 | public R accept(ResultVisitor visitor, T t) { 35 | return visitor.visit(this, t); 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return "return " + expression; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/Statement.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.ast; 2 | 3 | /** 4 | * 5 | * @author aNNiMON 6 | */ 7 | public interface Statement extends Node { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/TernaryExpression.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.ast; 2 | 3 | import com.annimon.ownlang.lib.Value; 4 | 5 | /** 6 | * 7 | * @author aNNiMON 8 | */ 9 | public final class TernaryExpression implements Node { 10 | 11 | public final Node condition; 12 | public final Node trueExpr, falseExpr; 13 | 14 | public TernaryExpression(Node condition, Node trueExpr, Node falseExpr) { 15 | this.condition = condition; 16 | this.trueExpr = trueExpr; 17 | this.falseExpr = falseExpr; 18 | } 19 | 20 | @Override 21 | public Value eval() { 22 | if (condition.eval().asInt() != 0) { 23 | return trueExpr.eval(); 24 | } else { 25 | return falseExpr.eval(); 26 | } 27 | } 28 | 29 | @Override 30 | public void accept(Visitor visitor) { 31 | visitor.visit(this); 32 | } 33 | 34 | @Override 35 | public R accept(ResultVisitor visitor, T t) { 36 | return visitor.visit(this, t); 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return String.format("%s ? %s : %s", condition, trueExpr, falseExpr); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseError.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.error; 2 | 3 | import com.annimon.ownlang.util.Range; 4 | import com.annimon.ownlang.util.SourceLocatedError; 5 | 6 | public record ParseError( 7 | String message, 8 | Range range, 9 | StackTraceElement[] stackTraceElements 10 | ) implements SourceLocatedError { 11 | 12 | public ParseError(String message, Range range) { 13 | this(message, range, new StackTraceElement[0]); 14 | } 15 | 16 | @Override 17 | public String getMessage() { 18 | return message; 19 | } 20 | 21 | @Override 22 | public Range getRange() { 23 | return range; 24 | } 25 | 26 | @Override 27 | public StackTraceElement[] getStackTrace() { 28 | return stackTraceElements; 29 | } 30 | 31 | @Override 32 | public String toString() { 33 | return "Error on line " + range().start().row() + ": " + message; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.error; 2 | 3 | import com.annimon.ownlang.stages.Stage; 4 | import com.annimon.ownlang.stages.StagesData; 5 | import com.annimon.ownlang.util.ErrorsLocationFormatterStage; 6 | import com.annimon.ownlang.util.ErrorsStackTraceFormatterStage; 7 | import com.annimon.ownlang.util.SourceLocatedError; 8 | import java.util.Collection; 9 | 10 | public class ParseErrorsFormatterStage implements Stage, String> { 11 | 12 | @Override 13 | public String perform(StagesData stagesData, Collection input) { 14 | String error = new ErrorsLocationFormatterStage() 15 | .perform(stagesData, input); 16 | String stackTrace = new ErrorsStackTraceFormatterStage() 17 | .perform(stagesData, input); 18 | return error + "\n" + stackTrace; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.linters; 2 | 3 | import com.annimon.ownlang.parser.ast.*; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | 7 | final class DefaultFunctionsOverrideValidator extends LintVisitor { 8 | 9 | private final Set moduleFunctions = new HashSet<>(); 10 | 11 | DefaultFunctionsOverrideValidator(LinterResults results) { 12 | super(results); 13 | } 14 | 15 | @Override 16 | public void visit(FunctionDefineStatement s) { 17 | super.visit(s); 18 | if (moduleFunctions.contains(s.name)) { 19 | results.add(LinterResult.warning( 20 | "Function \"%s\" overrides default module function".formatted(s.name))); 21 | } 22 | } 23 | 24 | @Override 25 | public void visit(IncludeStatement s) { 26 | super.visit(s); 27 | applyVisitor(s, this); 28 | } 29 | 30 | @Override 31 | public void visit(UseStatement s) { 32 | super.visit(s); 33 | moduleFunctions.addAll(s.loadFunctions().keySet()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.linters; 2 | 3 | import com.annimon.ownlang.parser.ast.IncludeStatement; 4 | import com.annimon.ownlang.parser.ast.Node; 5 | import com.annimon.ownlang.parser.ast.Visitor; 6 | import com.annimon.ownlang.parser.visitors.AbstractVisitor; 7 | import com.annimon.ownlang.parser.visitors.VisitorUtils; 8 | 9 | abstract class LintVisitor extends AbstractVisitor { 10 | protected final LinterResults results; 11 | 12 | LintVisitor(LinterResults results) { 13 | this.results = results; 14 | } 15 | 16 | protected void applyVisitor(IncludeStatement s, Visitor visitor) { 17 | final Node program = VisitorUtils.includeProgram(s); 18 | if (program != null) { 19 | program.accept(visitor); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/Optimizable.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.optimization; 2 | 3 | import com.annimon.ownlang.parser.ast.Node; 4 | 5 | public interface Optimizable { 6 | 7 | Node optimize(Node node); 8 | 9 | int optimizationsCount(); 10 | 11 | String summaryInfo(); 12 | } 13 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/optimization/VariableInfo.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.optimization; 2 | 3 | import com.annimon.ownlang.lib.Value; 4 | 5 | public final class VariableInfo { 6 | public Value value; 7 | int modifications; 8 | 9 | @Override 10 | public String toString() { 11 | return (value == null ? "?" : value) + " (" + modifications + " mods)"; 12 | } 13 | } -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/FunctionAdder.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.visitors; 2 | 3 | import com.annimon.ownlang.parser.ast.*; 4 | 5 | /** 6 | * 7 | * @author aNNiMON 8 | */ 9 | public final class FunctionAdder extends AbstractVisitor { 10 | 11 | @Override 12 | public void visit(FunctionDefineStatement s) { 13 | super.visit(s); 14 | s.eval(); 15 | } 16 | 17 | @Override 18 | public void visit(ClassDeclarationStatement s) { 19 | // skip, otherwise class methods will be visible outside of class 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.visitors; 2 | 3 | import com.annimon.ownlang.parser.ast.Node; 4 | import com.annimon.ownlang.parser.ast.UseStatement; 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | 8 | public class ModuleDetector extends AbstractVisitor { 9 | 10 | private final Set modules; 11 | 12 | public ModuleDetector() { 13 | modules = new HashSet<>(); 14 | } 15 | 16 | public Set detect(Node s) { 17 | s.accept(this); 18 | return modules; 19 | } 20 | 21 | @Override 22 | public void visit(UseStatement s) { 23 | modules.addAll(s.modules); 24 | super.visit(s); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/VariablePrinter.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.visitors; 2 | 3 | import com.annimon.ownlang.Console; 4 | import com.annimon.ownlang.parser.ast.*; 5 | 6 | /** 7 | * 8 | * @author aNNiMON 9 | */ 10 | public final class VariablePrinter extends AbstractVisitor { 11 | 12 | @Override 13 | public void visit(AssignmentExpression s) { 14 | super.visit(s); 15 | Console.println(s.target); 16 | } 17 | 18 | @Override 19 | public void visit(VariableExpression s) { 20 | super.visit(s); 21 | Console.println(s.name); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/stages/ExecutionStage.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.stages; 2 | 3 | import com.annimon.ownlang.parser.ast.Node; 4 | 5 | public class ExecutionStage implements Stage { 6 | 7 | @Override 8 | public Node perform(StagesData stagesData, Node input) { 9 | input.eval(); 10 | return input; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/stages/FunctionAddingStage.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.stages; 2 | 3 | import com.annimon.ownlang.parser.ast.Node; 4 | import com.annimon.ownlang.parser.visitors.FunctionAdder; 5 | 6 | public class FunctionAddingStage implements Stage { 7 | 8 | @Override 9 | public Node perform(StagesData stagesData, Node input) { 10 | input.accept(new FunctionAdder()); 11 | return input; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ownlang-parser/src/main/java/com/annimon/ownlang/stages/LexerStage.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.stages; 2 | 3 | import com.annimon.ownlang.parser.Lexer; 4 | import com.annimon.ownlang.parser.Token; 5 | import java.util.List; 6 | 7 | public class LexerStage implements Stage> { 8 | 9 | public static final String TAG_TOKENS = "tokens"; 10 | 11 | @Override 12 | public List perform(StagesData stagesData, String input) { 13 | Lexer lexer = new Lexer(input); 14 | List tokens = lexer.tokenize(); 15 | stagesData.put(TAG_TOKENS, tokens); 16 | return tokens; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ownlang-parser/src/test/java/com/annimon/ownlang/parser/TestDataUtil.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser; 2 | 3 | import java.io.File; 4 | import java.util.Arrays; 5 | import java.util.stream.Stream; 6 | 7 | public class TestDataUtil { 8 | 9 | static Stream scanDirectory(String dirPath) { 10 | return scanDirectory(new File(dirPath)); 11 | } 12 | 13 | static Stream scanDirectory(File dir) { 14 | final File[] files = dir.listFiles(); 15 | if (files == null || files.length == 0) { 16 | return Stream.empty(); 17 | } 18 | return Arrays.stream(files) 19 | .flatMap(f -> f.isDirectory() ? scanDirectory(f) : Stream.of(f)) 20 | .filter(f -> f.getName().endsWith(".own")); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ownlang-parser/src/test/java/com/annimon/ownlang/parser/ast/ValueExpressionTest.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.parser.ast; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static com.annimon.ownlang.parser.ast.ASTHelper.*; 6 | 7 | /** 8 | * 9 | * @author aNNiMON 10 | */ 11 | public class ValueExpressionTest { 12 | 13 | @Test 14 | public void testValue() { 15 | assertValue(number(4), value(4).eval()); 16 | assertValue(string("ABCD"), value("ABCD").eval()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ownlang-parser/src/test/java/interop/Data.java: -------------------------------------------------------------------------------- 1 | package interop; 2 | 3 | public final class Data { 4 | public final int intValue = 3228; 5 | public final int[] intArrayValue = {1, 2, 3}; 6 | public final Object nullObject = null; 7 | public final String stringValue = "str"; 8 | public final String[] stringArrayValue = {"abc", "test"}; 9 | public final Object[] objectArray = new Object[] {intValue, intArrayValue, nullObject, stringValue, stringArrayValue}; 10 | public final Object compoundObject = objectArray; 11 | 12 | public void method() { 13 | System.out.println("method"); 14 | } 15 | 16 | public String methodWithResult() { 17 | return "result"; 18 | } 19 | 20 | 21 | private int value; 22 | private String text; 23 | 24 | public void set(int value) { 25 | this.value = value; 26 | } 27 | 28 | public void set(String text) { 29 | this.text = text; 30 | } 31 | 32 | public int getValue() { 33 | return value; 34 | } 35 | 36 | public String getText() { 37 | return text; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/benchmarks/calculator.own: -------------------------------------------------------------------------------- 1 | // Simple parser example 2 | use std, types 3 | 4 | operations = { 5 | "+" : def(a,b) = a+b, 6 | "-" : def(a,b) = a-b, 7 | "*" : def(a,b) = a*b, 8 | "/" : def(a,b) = a/b, 9 | "%" : def(a,b) = a%b, 10 | ">" : def(a,b) = a>b, 11 | "<" : def(a,b) = a4") 46 | -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/benchmarks/useStatement.own: -------------------------------------------------------------------------------- 1 | for i = 0, i < 50, i++ { 2 | use std 3 | use files 4 | use math, functional 5 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/expressions/assignmentExpression.own: -------------------------------------------------------------------------------- 1 | def testSimpleAssignment() { 2 | a = 8 3 | b = "str" 4 | assertEquals(8, a) 5 | assertEquals("str", b) 6 | } 7 | 8 | def testPrefixIncrement() { 9 | a = 8 10 | assertEquals(9, ++a) 11 | assertEquals(9, a) 12 | } 13 | 14 | def testPostfixIncrement() { 15 | a = 8 16 | assertEquals(8, a++) 17 | assertEquals(9, a) 18 | } 19 | 20 | def testPrefixDecrement() { 21 | a = 8 22 | assertEquals(7, --a) 23 | assertEquals(7, a) 24 | } 25 | 26 | def testPostfixDecrement() { 27 | a = 8 28 | assertEquals(8, a--) 29 | assertEquals(7, a) 30 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/expressions/binaryExpressionOnStrings.own: -------------------------------------------------------------------------------- 1 | def testStringConcatenation() { 2 | assertEquals("22", "2" + "2") 3 | assertEquals("22", "2" + 2) 4 | } 5 | 6 | def testStringMultiplication() { 7 | assertEquals("******", "*" * 6) 8 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/expressions/binaryUnaryExpr.own: -------------------------------------------------------------------------------- 1 | def testAdditionOnNumbers() { 2 | assertEquals(6, 0 + 1 + 2 + 3) 3 | } 4 | 5 | def testSubtractionOnNumbers() { 6 | assertEquals(-6, 0 - 1 - 2 - 3) 7 | } 8 | 9 | def testPrefixIncrement() { 10 | a = 8 11 | assertEquals(9, ++a) 12 | assertEquals(9, a) 13 | } 14 | 15 | def testPostfixIncrement() { 16 | a = 8 17 | assertEquals(8, a++) 18 | assertEquals(9, a) 19 | } 20 | 21 | def testStringReversing() { 22 | assertEquals("tset", -"test") 23 | } 24 | 25 | def testStringMultiplication() { 26 | assertEquals("******", "*" * 6) 27 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/expressions/foreachKeyValue.own: -------------------------------------------------------------------------------- 1 | def testArrayIterate() { 2 | sum = 0 3 | for v, i : [1, 2, 3] { 4 | sum += v * i 5 | } 6 | assertEquals(1 * 0 + 2 * 1 + 3 * 2, sum) 7 | } 8 | 9 | def testMapIterate() { 10 | map = {12: 1, 13: 2, 14: 3} 11 | sumKey = 0 12 | sumValue = 0 13 | for key, value : map { 14 | sumKey += key 15 | sumValue += value 16 | } 17 | assertEquals(39, sumKey) 18 | assertEquals(6, sumValue) 19 | } 20 | 21 | def testStringIterate() { 22 | str = "" 23 | sum = 0 24 | for s, code : "abcd" { 25 | str += s.upper 26 | sum += code 27 | } 28 | assertEquals("ABCD", str) 29 | assertEquals(394/*97 + 98 + 99 + 100*/, sum) 30 | } 31 | 32 | def testScope() { 33 | a = 100 34 | b = 200 35 | sum = 0 36 | for a, b : {14: 3} { 37 | sum += a 38 | sum += b 39 | } 40 | assertEquals(17, sum) 41 | assertEquals(14, a) 42 | assertEquals(3, b) 43 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/expressions/foreachValue.own: -------------------------------------------------------------------------------- 1 | use std 2 | 3 | def testArrayIterate() { 4 | sum = 0 5 | for v : [1, 2, 3] { 6 | sum += v 7 | } 8 | assertEquals(6, sum) 9 | } 10 | 11 | def testMapIterate() { 12 | map = {12: 1, 13: 2, 14: 3} 13 | sumKey = 0 14 | sumValue = 0 15 | for pair : map { 16 | extract(key, value) = pair 17 | sumKey += key 18 | sumValue += value 19 | } 20 | assertEquals(39, sumKey) 21 | assertEquals(6, sumValue) 22 | } 23 | 24 | def testStringIterate() { 25 | sum = 0 26 | for s : "abcd" { 27 | sum += s.charAt(0) 28 | } 29 | assertEquals(394/*97 + 98 + 99 + 100*/, sum) 30 | } 31 | 32 | def testScope() { 33 | v = 45 34 | sum = 0 35 | for v : [1, 2, 3] { 36 | sum += v 37 | } 38 | assertEquals(6, sum) 39 | assertEquals(3, v) 40 | } 41 | 42 | -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/expressions/functionReference.own: -------------------------------------------------------------------------------- 1 | def func() = "function" 2 | var = def() = "variable" 3 | def consumer(x) = x(); 4 | 5 | assertEquals("function", consumer(::func)) 6 | assertEquals("variable", consumer(var)) -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/expressions/include.own: -------------------------------------------------------------------------------- 1 | use std 2 | 3 | def testIncludeClass() { 4 | include "includeClass.own.txt" 5 | obj = new IncludeClass() 6 | assertEquals("1", obj.field1) 7 | assertEquals(2, obj.field2) 8 | assertEquals(42, obj.test()) 9 | } 10 | 11 | def testIncludeNotExistsSource() { 12 | assertFail(def() { 13 | include "test.own" 14 | }) 15 | } 16 | 17 | def testCatchingIncludeNotExistsSource() { 18 | res = try(def() { 19 | include "test.own" 20 | }, def(classname, message) = "ok") 21 | assertEquals("ok", res) 22 | } 23 | 24 | def testIncludeParseErrorSource() { 25 | assertFail(def() { 26 | include "includeParseErrorSource.own.txt" 27 | }) 28 | } 29 | 30 | def testCatchingIncludeParseErrorSource() { 31 | res = try(def() { 32 | include "includeParseErrorSource.own.txt" 33 | }, def(classname, message) = "ok") 34 | assertEquals("ok", res) 35 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/expressions/includeClass.own.txt: -------------------------------------------------------------------------------- 1 | class IncludeClass { 2 | field1 = "1" 3 | field2 = 2 4 | 5 | def test() { 6 | return 42 7 | } 8 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/expressions/includeParseErrorSource.own.txt: -------------------------------------------------------------------------------- 1 | def test() = return println match x { 2 | case case 1 3 | } 4 | value = "passed" -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/expressions/nullCoalesce.own: -------------------------------------------------------------------------------- 1 | def testZero() { 2 | assertEquals(0, 0 ?? 1) 3 | x = 0 4 | assertEquals(0, x ?? 2) 5 | } 6 | 7 | def testObject() { 8 | obj = {"a": 12} 9 | assertEquals(12, obj.a ?? 10) 10 | } 11 | 12 | def testObjectMissingKey() { 13 | obj = {"a": 12} 14 | assertEquals(10, obj.test ?? 10) 15 | } 16 | 17 | def testNestedObjects() { 18 | obj = {"a": {"b": 12}} 19 | assertEquals(12, obj.a.b ?? 10) 20 | } 21 | 22 | def testNestedObjectsMissingKey() { 23 | obj = {"a": {"b": 12}} 24 | assertEquals(1, obj.test ?? 1) 25 | assertEquals(2, obj.a.test ?? 2) 26 | assertEquals(3, obj.test1.test2 ?? 3) 27 | } 28 | -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/expressions/unaryExpressionOnStrings.own: -------------------------------------------------------------------------------- 1 | def testStringReversing() { 2 | assertEquals("tset", -"test") 3 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/expressions/varFuncSameName.own: -------------------------------------------------------------------------------- 1 | def function() = "function" 2 | function = "variable" 3 | 4 | assertEquals("variable", function) 5 | assertEquals("function", function()) -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/date/compareDates.own: -------------------------------------------------------------------------------- 1 | use date 2 | 3 | def testCompareDates() { 4 | assertTrue(newDate(2016, 04, 10) > newDate(2015, 03, 11)) 5 | assertTrue(newDate(2012, 04, 10) < newDate(2015, 03, 11)) 6 | assertTrue(newDate(2015, 03, 11, 0, 0, 0) == newDate(2015, 03, 11)) 7 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/date/dateFormat.own: -------------------------------------------------------------------------------- 1 | use date 2 | 3 | def testDateFormat() { 4 | d = formatDate(newDate(2016, 04, 10), newFormat("yyyy/MM/dd HH:mm:ss")) 5 | assertEquals("2016/05/10 00:00:00", d) 6 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/date/dateParse.own: -------------------------------------------------------------------------------- 1 | use date 2 | 3 | def testDateParse() { 4 | d = parseDate("2016/05/10", newFormat("yyyy/MM/dd")) 5 | assertEquals(2016, d.year) 6 | assertEquals(4, d.month) 7 | assertEquals(10, d.day) 8 | assertEquals(0, d.hour) 9 | } 10 | -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/date/newDate.own: -------------------------------------------------------------------------------- 1 | use date 2 | 3 | def testNewDate() { 4 | d = newDate(2016, 04, 10) 5 | assertEquals(2016, d.year) 6 | assertEquals(4, d.month) 7 | assertEquals(10, d.day) 8 | assertEquals(0, d.hour) 9 | assertEquals(0, d.minute) 10 | assertEquals(0, d.second) 11 | assertEquals(0, d.millisecond) 12 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/files/files.own: -------------------------------------------------------------------------------- 1 | use files, types 2 | 3 | def testFiles() { 4 | // writeLong 5 | f = fopen("test.file", "wb") 6 | writeLong(f, 1002003004005006007) 7 | flush(f) 8 | fclose(f) 9 | 10 | // append & writeFloat 11 | fpNumber = 100200.3004005006007 12 | f = fopen("test.file", "wb+") 13 | writeFloat(f, fpNumber) 14 | flush(f) 15 | fclose(f) 16 | 17 | f = fopen("test.file", "rb") 18 | assertEquals(1002003004005006007, readLong(f)) 19 | assertEquals(float(fpNumber), readFloat(f)) 20 | assertEquals(-1, readInt(f)) // EOF 21 | assertEquals(0, FILES_COMPARATOR(f, f)) 22 | fclose(f) 23 | 24 | f = fopen("test.file", "i") 25 | delete(f) 26 | fclose(f) 27 | } 28 | -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/functional/chain.own: -------------------------------------------------------------------------------- 1 | use functional 2 | 3 | def testFunctionalChain() { 4 | data = [1,2,3,4,5,6,7] 5 | result = chain(data, 6 | ::filter, def(x) = x <= 4, 7 | ::sortby, def(x) = -x, 8 | ::map, def(x) = x * 2, 9 | ) 10 | assertEquals([8,6,4,2], result) 11 | } 12 | -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/functional/foreach.own: -------------------------------------------------------------------------------- 1 | use std, functional 2 | 3 | def testArrayForeachArg() { 4 | sum = 0 5 | foreach([1, 2, 3], def(v) { 6 | sum += v 7 | }) 8 | assertEquals(6, sum) 9 | } 10 | 11 | def testStringForeach1Arg() { 12 | sum = 0 13 | foreach("abcd", def(s) { 14 | sum += s.charAt(0) 15 | }) 16 | assertEquals(394/*97 + 98 + 99 + 100*/, sum) 17 | } 18 | 19 | def testStringForeach2Args() { 20 | str = "" 21 | sum = 0 22 | foreach("abcd", def(s, code) { 23 | str += s.upper 24 | sum += code 25 | }) 26 | assertEquals("ABCD", str) 27 | assertEquals(97 + 98 + 99 + 100, sum) 28 | } 29 | -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/functional/groupby.own: -------------------------------------------------------------------------------- 1 | use std, functional 2 | 3 | def testGroupByKeys() { 4 | data = [ 5 | {"k1": 1, "k2": "a"}, 6 | {"k1": 2, "k2": "b"}, 7 | {"k1": 3, "k2": "c"}, 8 | ] 9 | result = groupby(data, def(e) = e.k2) 10 | assertEquals([{"k1": 1, "k2": "a"}], result.a) 11 | assertEquals([{"k1": 2, "k2": "b"}], result.b) 12 | assertEquals([{"k1": 3, "k2": "c"}], result.c) 13 | } 14 | 15 | def testArraysGroupBy() { 16 | arr = [1, 2, 3, 4, 1, 2, 3, 1, 2, 3] 17 | result = groupby(arr, def(v) = v % 2 == 0) 18 | assertEquals([2, 4, 2, 2], result[true]) 19 | assertEquals([1, 3, 1, 3, 1, 3], result[false]) 20 | } 21 | 22 | def testMapsGroupBy() { 23 | map = {"abc": 123, "test1": 234, "test2": 345, "test3": 456, "def": 567} 24 | result = groupby(map, def(k, v) = k.startsWith("test")) 25 | assertEquals({"test1": 234, "test2": 345, "test3": 456}, result[true]) 26 | assertEquals({"abc": 123, "def": 567}, result[false]) 27 | } 28 | 29 | -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/regex/match.own: -------------------------------------------------------------------------------- 1 | use regex, types 2 | 3 | def testMatchGitUrl() { 4 | pattern = Pattern.compile("https?://((git(hu|la)b\.com)|bitbucket\.org)/?") 5 | assertTrue(pattern.matches("http://github.com")) 6 | assertTrue(pattern.matches("http://github.com/")) 7 | assertTrue(pattern.matches("https://gitlab.com/")) 8 | assertTrue(pattern.matches("https://bitbucket.org/")) 9 | 10 | assertFalse(pattern.matches("http://github.org")) 11 | assertFalse(pattern.matches("https://bithub.com/")) 12 | assertFalse(pattern.matches("http://gitlab.org")) 13 | assertFalse(pattern.matches("ftp://github.com/")) 14 | assertFalse(pattern.matches("http://gitbucket.org/")) 15 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/regex/replaceCallback.own: -------------------------------------------------------------------------------- 1 | use regex, types 2 | 3 | def testReplaceCallback() { 4 | in = "[1-2-3-4]" 5 | pattern = regex("(\d)") 6 | out = pattern.replaceCallback(in, def(m) = m.group() * int(m.group())) 7 | assertEquals("[1-22-333-4444]", out) 8 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/std/default.own: -------------------------------------------------------------------------------- 1 | use std 2 | 3 | def testDefaultNumber() { 4 | assertEquals(123, default(0, 123)) 5 | } 6 | 7 | def testDefaultString() { 8 | assertEquals("123", default("", "123")) 9 | } 10 | 11 | def testDefaultNull() { 12 | use java 13 | assertEquals("not null", default(null, "not null")) 14 | } 15 | 16 | def testOperatorOverloading() { 17 | def `?:`(a, b) = default(a, b) 18 | assertEquals("not null", "" ?: "not null") 19 | } 20 | -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/std/getBytes.own: -------------------------------------------------------------------------------- 1 | use std 2 | 3 | def testGetBytes() { 4 | assertEquals([111, 119, 110, 108, 97, 110, 103], getBytes("ownlang")) 5 | } 6 | 7 | def testGetBytesEmptyString() { 8 | assertEquals([], getBytes("")) 9 | } 10 | -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/std/indexOf.own: -------------------------------------------------------------------------------- 1 | use std 2 | 3 | def testIndexOf() { 4 | assertEquals(3, indexOf("123/456/789", "/")) 5 | } 6 | 7 | def testIndexOfIndex() { 8 | assertEquals(7, indexOf("123/456/789", "/", 4)) 9 | } 10 | 11 | def testIndexOfNonMatch() { 12 | assertEquals(-1, indexOf("123", "/")) 13 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/std/lastIndexOf.own: -------------------------------------------------------------------------------- 1 | use std 2 | 3 | def testLastIndexOf() { 4 | assertEquals(8, lastIndexOf("/123/456/789", "/")) 5 | } 6 | 7 | def testLastIndexOfIndex() { 8 | assertEquals(4, lastIndexOf("/123/456/789", "/", 6)) 9 | } 10 | 11 | def testLastIndexOfNonMatch() { 12 | assertEquals(-1, lastIndexOf("123", "/")) 13 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/std/parseInt.own: -------------------------------------------------------------------------------- 1 | use std 2 | 3 | def testParseInt() { 4 | assertEquals(141, parseInt("141")) 5 | } 6 | 7 | def testParseIntBin() { 8 | assertEquals(141, parseInt("10001101", 2)) 9 | } 10 | 11 | def testParseIntOct() { 12 | assertEquals(141, parseInt("215", 8)) 13 | } 14 | 15 | def testParseIntHex() { 16 | assertEquals(141, parseInt("8D", 16)) 17 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/std/parseLong.own: -------------------------------------------------------------------------------- 1 | use std 2 | 3 | def testParseInt() { 4 | assertEquals(12345654321, parseLong("12345654321")) 5 | } 6 | 7 | def testParseIntBin() { 8 | assertEquals(12345654321, parseLong("1011011111110110111011110000110001", 2)) 9 | } 10 | 11 | def testParseIntOct() { 12 | assertEquals(12345654321, parseLong("133766736061", 8)) 13 | } 14 | 15 | def testParseIntHex() { 16 | assertEquals(#2DFDBBC31, parseLong("2DFDBBC31", 16)) 17 | assertEquals(12345654321, parseLong("2DFDBBC31", 16)) 18 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/std/stringFromBytes.own: -------------------------------------------------------------------------------- 1 | use std 2 | 3 | def testStringFromBytes() { 4 | assertEquals("ownlang", stringFromBytes([111, 119, 110, 108, 97, 110, 103])) 5 | } 6 | 7 | def testStringFromEmptyString() { 8 | assertEquals("", stringFromBytes([])) 9 | } 10 | -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/std/toHexString.own: -------------------------------------------------------------------------------- 1 | use std 2 | 3 | def testToHexString() { 4 | assertEquals("8d", toHexString(141)) 5 | assertEquals("cafebabe", toHexString(#CAFEBABE)) 6 | assertEquals("2dfdbbc31", toHexString(12345654321)) 7 | } 8 | -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/std/try.own: -------------------------------------------------------------------------------- 1 | use std 2 | 3 | def testTryOnly() { 4 | assertEquals(1, try(def() = 1)) 5 | assertEquals(-1, try(def() = parseInt("oops"))) 6 | } 7 | 8 | def testCatchFunction() { 9 | actual = try(def() = parseInt("oops"), def(clazz, cause) = clazz) 10 | assertEquals("java.lang.NumberFormatException", actual) 11 | } 12 | 13 | def testCatchValue() { 14 | actual = try(def() = parseInt("oops"), 42) 15 | assertEquals(42, actual) 16 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/yaml/yamldecode.own: -------------------------------------------------------------------------------- 1 | use std, yaml 2 | 3 | x = yamldecode(" 4 | name: \"std\" 5 | scope: \"both\" 6 | desc: \"Contains common functions\" 7 | desc_ru: \"Содержит вспомогательные функции общего назначения\" 8 | constants: [] 9 | functions: 10 | - 11 | name: \"arrayCombine\" 12 | args: \"keys, values\" 13 | desc: \"creates map by combining two arrays\" 14 | desc_ru: \"создаёт объект на основе двух массивов\" 15 | - 16 | name: \"typeof\" 17 | args: \"value\" 18 | desc: \"returns the type of value\" 19 | desc_ru: \"возвращает тип переданного значения\" 20 | example: |- 21 | print typeof(1) // 1 (NUMBER) 22 | print typeof(\"text\") // 2 (STRING) 23 | print typeof([]) // 3 (ARRAY) 24 | ") 25 | 26 | assertEquals("std", x.name) 27 | assertEquals("both", x.scope) 28 | assertEquals(0, x.constants.length) 29 | assertEquals(2, x.functions.length) 30 | assertEquals("arrayCombine", x.functions[0].name) 31 | assertEquals("возвращает тип переданного значения", x.functions[1].desc_ru) 32 | -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/modules/yaml/yamlencode.own: -------------------------------------------------------------------------------- 1 | use std, yaml 2 | 3 | yml = yamlencode({ 4 | "name": "Yaml Example", 5 | "version": 1, 6 | "arrayData": [ 7 | 1, 2, 3, 4 8 | ], 9 | "objectData": { 10 | "key": "value", 11 | 10: "1000" 12 | } 13 | }) 14 | obj = yamldecode(yml) 15 | 16 | assertEquals("Yaml Example", obj.name) 17 | assertEquals(1, obj.version) 18 | assertEquals(4, length(obj.arrayData)) 19 | assertEquals("value", obj.objectData.key) 20 | assertEquals("1000", obj.objectData["10"]) -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/other/arrayFunctions.own: -------------------------------------------------------------------------------- 1 | def testSetUnknownKey() = assertFail(def() { 2 | arr = [1, 2, 3] 3 | arr.one = 1 4 | }) 5 | 6 | def testSetLengthProperty() = assertFail(def() { 7 | arr = [1, 2, 3] 8 | arr.length = 10 9 | }) 10 | 11 | def testGetLength() { 12 | arr = [1, 2, 3] 13 | assertEquals(3, arr.length) 14 | } 15 | 16 | def testGetLengthInnerArray() { 17 | arr = [[1, 2, 3], [1, 2, [3, 4, 5, 6]]] 18 | assertEquals(2, arr.length) 19 | assertEquals(3, arr[0].length) 20 | assertEquals(4, arr[1][2].length) 21 | } 22 | 23 | def testIsEmpty() { 24 | arr = [1, 2, 3] 25 | assertFalse(arr.isEmpty()) 26 | } 27 | 28 | def testJoinToString() { 29 | arr = [1, 2, 3] 30 | assertEquals("123", arr.joinToString()) 31 | assertEquals("1 2 3", arr.joinToString(" ")) 32 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/other/classScope.own: -------------------------------------------------------------------------------- 1 | use std 2 | 3 | def testThisOnSingleInstance() { 4 | s = new ClassScope({"id": 1}) 5 | assertEquals(1, s.getId()) 6 | assertEquals(1, s.getDataId()) 7 | } 8 | 9 | def testThisOnMultipleInstances() { 10 | s1 = new ClassScope({"id": 1}) 11 | s2 = new ClassScope({"id": 2}) 12 | s3 = new ClassScope({"id": 3}) 13 | assertEquals(1, s1.getId()) 14 | assertEquals(1, s1.getDataId()) 15 | assertEquals(2, s2.getId()) 16 | assertEquals(2, s2.getDataId()) 17 | assertEquals(3, s3.getId()) 18 | assertEquals(3, s3.getDataId()) 19 | } 20 | def testToString() { 21 | s1 = new ClassScope({"id": 1}) 22 | s2 = new ClassScope({"id": 2}) 23 | assertEquals("ClassScope{id=1}", s1.toString()) 24 | assertEquals("ClassScope{id=2}", s2.toString()) 25 | } 26 | 27 | class ClassScope { 28 | def ClassScope(data) { 29 | this.id = data.id 30 | this.data = data 31 | } 32 | 33 | def getId() { 34 | return this.id 35 | } 36 | 37 | def getDataId() { 38 | return this.data.id 39 | } 40 | 41 | def toString() { 42 | return "ClassScope{id=" + this.id + "}" 43 | } 44 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/other/functionChain.own: -------------------------------------------------------------------------------- 1 | def f1() = {"func": ::f2} 2 | def f2() = { 3 | "functions" : { 4 | "add" : def(a, b) = a + b 5 | "mul" : def(a, b) = a * b 6 | "negate" : def(a) = {"result" : -a} 7 | } 8 | } 9 | def f3() = def() = def() = def() = "test" 10 | def f4() = def() = ::f1 11 | 12 | def testFunctionChain() { 13 | assertEquals(5, f1().func().`functions`.add(2, 3)) 14 | assertEquals(6, f1().func().`functions`.mul(2, 3)) 15 | } 16 | 17 | def testCallChain() { 18 | assertEquals("test", f3()()()()) 19 | } 20 | 21 | def testBoth() { 22 | assertEquals(-123, f4()()().func().`functions`.negate(123).result) 23 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/other/recursion.own: -------------------------------------------------------------------------------- 1 | def testFibonacci() { 2 | def fib(n) { 3 | if n < 2 return n 4 | return fib(n-2) + fib(n-1) 5 | } 6 | assertEquals(3, fib(4)) 7 | assertEquals(21, fib(8)) 8 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/other/scope.own: -------------------------------------------------------------------------------- 1 | def testScope() { 2 | x = 5 3 | def func() { 4 | assertEquals(5, x) 5 | x += 10 6 | assertEquals(15, x) 7 | } 8 | func(); 9 | assertEquals(15, x) 10 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/other/types.own: -------------------------------------------------------------------------------- 1 | def testTypes() { 2 | assertSameType(0, 0.0) 3 | } -------------------------------------------------------------------------------- /ownlang-parser/src/test/resources/other/useStatementScope.own: -------------------------------------------------------------------------------- 1 | use std 2 | 3 | def testRegular() { 4 | assertEquals("7f", "127".toHexString()) 5 | } 6 | 7 | def testInCondition() { 8 | if (true) { 9 | use date 10 | } 11 | assertNotEquals("", newDate()) 12 | } 13 | 14 | def testInScope() { 15 | PI = "fallback" 16 | assertEquals("fallback", PI) 17 | 18 | useMath() 19 | assertNotEquals("fallback", PI) 20 | assertEquals(3, abs(-3)) 21 | } 22 | 23 | def useMath() { 24 | use math 25 | } -------------------------------------------------------------------------------- /ownlang-utils/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | } 4 | 5 | group = 'com.annimon' 6 | version = versions.project 7 | 8 | dependencies { 9 | api project(":ownlang-parser") 10 | implementation "jline:jline:${versions.jline}" 11 | 12 | testImplementation platform("org.junit:junit-bom:${versions.junit}") 13 | testImplementation 'org.junit.jupiter:junit-jupiter' 14 | } 15 | 16 | test { 17 | useJUnitPlatform() 18 | } 19 | -------------------------------------------------------------------------------- /ownlang-utils/src/main/java/com/annimon/ownlang/utils/repl/JLineConsole.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.utils.repl; 2 | 3 | import java.io.IOException; 4 | import jline.TerminalFactory; 5 | import jline.console.ConsoleReader; 6 | 7 | public class JLineConsole implements ReplConsole { 8 | 9 | private final ConsoleReader console; 10 | 11 | public JLineConsole() throws IOException { 12 | System.setProperty(ConsoleReader.JLINE_EXPAND_EVENTS, "false"); 13 | console = new ConsoleReader(); 14 | } 15 | 16 | public ConsoleReader getConsole() { 17 | return console; 18 | } 19 | 20 | @Override 21 | public void setPrompt(String prompt) { 22 | console.setPrompt(prompt); 23 | } 24 | 25 | @Override 26 | public String readLine() { 27 | try { 28 | return console.readLine(); 29 | } catch (IOException ex) { 30 | return null; 31 | } 32 | } 33 | 34 | @Override 35 | public void close() { 36 | try { 37 | TerminalFactory.get().restore(); 38 | } catch (Exception ignored) { 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /ownlang-utils/src/main/java/com/annimon/ownlang/utils/repl/ReplConsole.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.utils.repl; 2 | 3 | public interface ReplConsole { 4 | 5 | void setPrompt(String prompt); 6 | 7 | String readLine(); 8 | 9 | void close(); 10 | } 11 | -------------------------------------------------------------------------------- /ownlang-utils/src/main/java/com/annimon/ownlang/utils/repl/SystemConsole.java: -------------------------------------------------------------------------------- 1 | package com.annimon.ownlang.utils.repl; 2 | 3 | import java.util.Scanner; 4 | 5 | public class SystemConsole implements ReplConsole { 6 | 7 | private final Scanner scanner; 8 | 9 | public SystemConsole() { 10 | scanner = new Scanner(System.in); 11 | } 12 | 13 | @Override 14 | public void setPrompt(String prompt) { 15 | System.out.print(prompt); 16 | } 17 | 18 | @Override 19 | public String readLine() { 20 | if (!scanner.hasNextLine()) { 21 | return null; 22 | } 23 | return scanner.nextLine(); 24 | } 25 | 26 | @Override 27 | public void close() { 28 | scanner.close(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'OwnLang' 2 | 3 | include 'ownlang-core' 4 | include 'ownlang-parser' 5 | include 'ownlang-desktop' 6 | include 'ownlang-utils' 7 | include 'docs' 8 | 9 | final def modules = ['main', 'canvasfx', 'jdbc', 'server', 'socket'] 10 | 11 | for (final def module in modules) { 12 | include "modules:$module" 13 | findProject(":modules:$module")?.name = module 14 | } 15 | --------------------------------------------------------------------------------