nqp-2018.03/MANIFEST0000644000175000017500000005716713253720153012303 0ustar alexalexMANIFEST .editorconfig .eslintrc.js .gitignore .travis.yml 3rdparty/asm/LICENSE.txt 3rdparty/asm/asm-4.1.jar 3rdparty/asm/asm-tree-4.1.jar 3rdparty/jline/LICENSE.txt 3rdparty/jline/jline-1.0.jar 3rdparty/jna/ASL2.0 3rdparty/jna/LICENSE.ASL 3rdparty/jna/jna-4.0.0.jar CREDITS Configure.pl LICENSE README.pod VERSION docs/6model/faq.markdown docs/6model/overview.markdown docs/6model/repr-compose-protocol.markdown docs/HACKING-js docs/bootstrapping.pod docs/built-ins.md docs/continuations.pod docs/jvminterop-goals.pod docs/jvminterop.pod docs/nqp-overview.txt docs/ops.markdown docs/qast.markdown docs/release_guide.pod docs/serialization_format.markdown docs/using-pod-in-nqp.md examples/CallFromJava.java examples/fib.nqp examples/hello_world.nqp examples/loops.nqp examples/rubyish/README.md examples/rubyish/examples-rubyish/closure.rbi examples/rubyish/examples-rubyish/fractal-tree.rbi examples/rubyish/examples-rubyish/pi.rbi examples/rubyish/examples-rubyish/template.rbi examples/rubyish/rubyish.nqp examples/rubyish/t/00hello-worldish.t examples/rubyish/t/arrays.t examples/rubyish/t/bitwise.t examples/rubyish/t/contextual.t examples/rubyish/t/functional.t examples/rubyish/t/hashs.t examples/rubyish/t/if-then-else.t examples/rubyish/t/infix.t examples/rubyish/t/inheritance.t examples/rubyish/t/interpolation.t examples/rubyish/t/lambda.t examples/rubyish/t/line-spanning.t examples/rubyish/t/loops.t examples/rubyish/t/modifiers.t examples/rubyish/t/nqp-ops.t examples/rubyish/t/params.t examples/rubyish/t/recursion.t examples/rubyish/t/rubyish-3-tests.t examples/rubyish/t/rubyish-4-tests.t examples/rubyish/t/scoping.t examples/rubyish/t/template.t examples/webpacked/README.md examples/webpacked/example.nqp examples/webpacked/index.html examples/webpacked/webpack.config.js gen/js/.gitignore gen/jvm/.gitignore gen/moar/.gitignore nqp-js-on-js/.gitignore nqp-js-on-js/package.json package.json ports/macports/Portfile ports/macports/README src/HLL/Actions.nqp src/HLL/CommandLine.nqp src/HLL/Compiler.nqp src/HLL/Grammar.nqp src/HLL/World.nqp src/HLL/sprintf.nqp src/NQP/Actions.nqp src/NQP/Compiler.nqp src/NQP/Grammar.nqp src/NQP/Optimizer.nqp src/NQP/World.nqp src/QAST/BVal.nqp src/QAST/Block.nqp src/QAST/Children.nqp src/QAST/CompUnit.nqp src/QAST/CompileTimeValue.nqp src/QAST/IVal.nqp src/QAST/InlinePlaceholder.nqp src/QAST/NVal.nqp src/QAST/Node.nqp src/QAST/NodeList.nqp src/QAST/Op.nqp src/QAST/ParamTypeCheck.nqp src/QAST/Regex.nqp src/QAST/SVal.nqp src/QAST/SpecialArg.nqp src/QAST/Stmt.nqp src/QAST/Stmts.nqp src/QAST/Unquote.nqp src/QAST/VM.nqp src/QAST/Var.nqp src/QAST/VarWithFallback.nqp src/QAST/WVal.nqp src/QAST/Want.nqp src/QRegex/Cursor.nqp src/QRegex/NFA.nqp src/QRegex/P5Regex/Actions.nqp src/QRegex/P5Regex/Compiler.nqp src/QRegex/P5Regex/Grammar.nqp src/QRegex/P6Regex/Actions.nqp src/QRegex/P6Regex/Compiler.nqp src/QRegex/P6Regex/Grammar.nqp src/QRegex/P6Regex/Optimizer.nqp src/core/Hash.nqp src/core/IO.nqp src/core/NQPCapture.nqp src/core/NQPLock.nqp src/core/NQPMu.nqp src/core/NQPRoutine.nqp src/core/NativeTypes.nqp src/core/Regex.nqp src/core/YOUAREHERE.nqp src/core/testing.nqp src/how/Archetypes.nqp src/how/EXPORTHOW.nqp src/how/NQPAttribute.nqp src/how/NQPClassHOW.nqp src/how/NQPConcreteRoleHOW.nqp src/how/NQPCurriedRoleHOW.nqp src/how/NQPModuleHOW.nqp src/how/NQPNativeHOW.nqp src/how/NQPParametricRoleHOW.nqp src/how/RoleToClassApplier.nqp src/how/RoleToRoleApplier.nqp src/vm/js/BlockBarrier.nqp src/vm/js/Chunk.nqp src/vm/js/Compiler.nqp src/vm/js/DWIMYNameMangling.nqp src/vm/js/HLL/Backend.nqp src/vm/js/LoopInfo.nqp src/vm/js/ModuleLoader.nqp src/vm/js/Operations.nqp src/vm/js/QAST.nqp src/vm/js/RegexCompiler.nqp src/vm/js/ReturnInfo.nqp src/vm/js/SerializeOnce.nqp src/vm/js/Utils.nqp src/vm/js/bin/build-sourcemap.js src/vm/js/bin/cross-compile.nqp src/vm/js/bin/gen_sourcemap.js src/vm/js/bin/nqp-js.nqp src/vm/js/bin/run_tests.pl src/vm/js/bin/run_tests_bootstrapped.pl src/vm/js/const_map.nqp src/vm/js/nqp-loader/main.js src/vm/js/nqp-loader/package.json src/vm/js/nqp-runtime/.bignum.js.swo src/vm/js/nqp-runtime/.core.js.swn src/vm/js/nqp-runtime/.core.js.swo src/vm/js/nqp-runtime/.ctx.js.swo src/vm/js/nqp-runtime/.nativecall.js.swo src/vm/js/nqp-runtime/.unicode-props.js.swo src/vm/js/nqp-runtime/BOOT.js src/vm/js/nqp-runtime/bignum.js src/vm/js/nqp-runtime/bootstrap.js src/vm/js/nqp-runtime/capture.js src/vm/js/nqp-runtime/cclass.js src/vm/js/nqp-runtime/code-ref-with-statevars.js src/vm/js/nqp-runtime/code-ref.js src/vm/js/nqp-runtime/codecs.js src/vm/js/nqp-runtime/coercions.js src/vm/js/nqp-runtime/constants.js src/vm/js/nqp-runtime/container-specs.js src/vm/js/nqp-runtime/core.js src/vm/js/nqp-runtime/ctx-just-handler.js src/vm/js/nqp-runtime/ctx-with-static.js src/vm/js/nqp-runtime/ctx.js src/vm/js/nqp-runtime/curlexpad.js src/vm/js/nqp-runtime/deserialization.js src/vm/js/nqp-runtime/exceptions-stack.js src/vm/js/nqp-runtime/graphemes.js src/vm/js/nqp-runtime/hash-iter.js src/vm/js/nqp-runtime/hash.js src/vm/js/nqp-runtime/hll.js src/vm/js/nqp-runtime/io.js src/vm/js/nqp-runtime/iter.js src/vm/js/nqp-runtime/multicache.js src/vm/js/nqp-runtime/native-args.js src/vm/js/nqp-runtime/nativecall.js src/vm/js/nqp-runtime/nfa.js src/vm/js/nqp-runtime/nqp-exception-with-ctx.js src/vm/js/nqp-runtime/nqp-exception.js src/vm/js/nqp-runtime/nqp-int.js src/vm/js/nqp-runtime/nqp-num.js src/vm/js/nqp-runtime/nqp-object.js src/vm/js/nqp-runtime/nqp-str.js src/vm/js/nqp-runtime/null.js src/vm/js/nqp-runtime/null_s.js src/vm/js/nqp-runtime/package-lock.json src/vm/js/nqp-runtime/package.json src/vm/js/nqp-runtime/refs.js src/vm/js/nqp-runtime/repossession.js src/vm/js/nqp-runtime/reprs.js src/vm/js/nqp-runtime/resolve-sourcemap.js src/vm/js/nqp-runtime/runtime.js src/vm/js/nqp-runtime/serialization-context.js src/vm/js/nqp-runtime/serialization.js src/vm/js/nqp-runtime/sixmodel.js src/vm/js/nqp-runtime/static-ctx.js src/vm/js/nqp-runtime/strip-marks.js src/vm/js/nqp-runtime/unicode-data.js src/vm/js/nqp-runtime/unicode-props.js src/vm/jvm/HLL/Backend.nqp src/vm/jvm/ModuleLoader.nqp src/vm/jvm/NQP/Ops.nqp src/vm/jvm/QAST/Compiler.nqp src/vm/jvm/QAST/JASTNodes.nqp src/vm/jvm/runners/nqp-j src/vm/jvm/runners/nqp-j.bat src/vm/jvm/runtime/org/perl6/nqp/io/AsyncFileHandle.java src/vm/jvm/runtime/org/perl6/nqp/io/AsyncProcessHandle.java src/vm/jvm/runtime/org/perl6/nqp/io/AsyncServerSocketHandle.java src/vm/jvm/runtime/org/perl6/nqp/io/AsyncSocketHandle.java src/vm/jvm/runtime/org/perl6/nqp/io/FileHandle.java src/vm/jvm/runtime/org/perl6/nqp/io/IIOAsyncReadable.java src/vm/jvm/runtime/org/perl6/nqp/io/IIOAsyncWritable.java src/vm/jvm/runtime/org/perl6/nqp/io/IIOBindable.java src/vm/jvm/runtime/org/perl6/nqp/io/IIOCancelable.java src/vm/jvm/runtime/org/perl6/nqp/io/IIOClosable.java src/vm/jvm/runtime/org/perl6/nqp/io/IIOEncodable.java src/vm/jvm/runtime/org/perl6/nqp/io/IIOExitable.java src/vm/jvm/runtime/org/perl6/nqp/io/IIOInteractive.java src/vm/jvm/runtime/org/perl6/nqp/io/IIOLineSeparable.java src/vm/jvm/runtime/org/perl6/nqp/io/IIOLockable.java src/vm/jvm/runtime/org/perl6/nqp/io/IIOPossiblyTTY.java src/vm/jvm/runtime/org/perl6/nqp/io/IIOSeekable.java src/vm/jvm/runtime/org/perl6/nqp/io/IIOSyncReadable.java src/vm/jvm/runtime/org/perl6/nqp/io/IIOSyncWritable.java src/vm/jvm/runtime/org/perl6/nqp/io/ProcessChannel.java src/vm/jvm/runtime/org/perl6/nqp/io/ProcessHandle.java src/vm/jvm/runtime/org/perl6/nqp/io/ServerSocketHandle.java src/vm/jvm/runtime/org/perl6/nqp/io/SocketHandle.java src/vm/jvm/runtime/org/perl6/nqp/io/StandardReadHandle.java src/vm/jvm/runtime/org/perl6/nqp/io/StandardWriteHandle.java src/vm/jvm/runtime/org/perl6/nqp/io/SyncHandle.java src/vm/jvm/runtime/org/perl6/nqp/jast2bc/AutosplitMethodWriter.java src/vm/jvm/runtime/org/perl6/nqp/jast2bc/JASTCompiler.java src/vm/jvm/runtime/org/perl6/nqp/jast2bc/JastClass.java src/vm/jvm/runtime/org/perl6/nqp/jast2bc/JastField.java src/vm/jvm/runtime/org/perl6/nqp/jast2bc/JastMethod.java src/vm/jvm/runtime/org/perl6/nqp/jast2bc/JavaClass.java src/vm/jvm/runtime/org/perl6/nqp/runtime/ArgsExpectation.java src/vm/jvm/runtime/org/perl6/nqp/runtime/Base64.java src/vm/jvm/runtime/org/perl6/nqp/runtime/BaseControlException.java src/vm/jvm/runtime/org/perl6/nqp/runtime/BootJavaInterop.java src/vm/jvm/runtime/org/perl6/nqp/runtime/Buffers.java src/vm/jvm/runtime/org/perl6/nqp/runtime/ByteClassLoader.java src/vm/jvm/runtime/org/perl6/nqp/runtime/CallFrame.java src/vm/jvm/runtime/org/perl6/nqp/runtime/CallSiteDescriptor.java src/vm/jvm/runtime/org/perl6/nqp/runtime/CodeRef.java src/vm/jvm/runtime/org/perl6/nqp/runtime/CodeRefAnnotation.java src/vm/jvm/runtime/org/perl6/nqp/runtime/CompilationUnit.java src/vm/jvm/runtime/org/perl6/nqp/runtime/ContextKey.java src/vm/jvm/runtime/org/perl6/nqp/runtime/ControlException.java src/vm/jvm/runtime/org/perl6/nqp/runtime/EvalResult.java src/vm/jvm/runtime/org/perl6/nqp/runtime/ExceptionHandling.java src/vm/jvm/runtime/org/perl6/nqp/runtime/GlobalContext.java src/vm/jvm/runtime/org/perl6/nqp/runtime/HLLConfig.java src/vm/jvm/runtime/org/perl6/nqp/runtime/HandlerInfo.java src/vm/jvm/runtime/org/perl6/nqp/runtime/IOExceptionMessages.java src/vm/jvm/runtime/org/perl6/nqp/runtime/IOOps.java src/vm/jvm/runtime/org/perl6/nqp/runtime/IndyBootstrap.java src/vm/jvm/runtime/org/perl6/nqp/runtime/JavaCallinException.java src/vm/jvm/runtime/org/perl6/nqp/runtime/LibraryLoader.java src/vm/jvm/runtime/org/perl6/nqp/runtime/NativeCallOps.java src/vm/jvm/runtime/org/perl6/nqp/runtime/Ops.java src/vm/jvm/runtime/org/perl6/nqp/runtime/ResumeException.java src/vm/jvm/runtime/org/perl6/nqp/runtime/ResumeStatus.java src/vm/jvm/runtime/org/perl6/nqp/runtime/SaveStackException.java src/vm/jvm/runtime/org/perl6/nqp/runtime/StaticCodeInfo.java src/vm/jvm/runtime/org/perl6/nqp/runtime/ThreadContext.java src/vm/jvm/runtime/org/perl6/nqp/runtime/UnwindException.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/AbstractParametricity.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/BoolificationSpec.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/CodePairContainerConfigurer.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/CodePairContainerSpec.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/ContainerConfigurer.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/ContainerSpec.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/InvocationSpec.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/KnowHOWBootstrapper.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/KnowHOWMethods.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/NativeRefContainerConfigurer.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/NativeRefContainerSpec.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/ParameterizedType.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/ParametricType.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/REPR.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/REPRRegistry.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/STable.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/SerializationContext.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/SerializationReader.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/SerializationWriter.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/SixModelObject.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/StorageSpec.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/TypeObject.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/AsyncTask.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/AsyncTaskInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/CArray.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/CArrayInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/CArrayREPRData.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/CPPStruct.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/CPPStructInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/CPPStructREPRData.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/CPointer.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/CPointerInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/CStr.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/CStrInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/CStruct.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/CStructInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/CStructREPRData.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/CUnion.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/CUnionInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/CUnionREPRData.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/CallCapture.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/CallCaptureInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/CodeRefREPR.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/ConcBlockingQueue.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/ConcBlockingQueueInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/ConditionVariable.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/ConditionVariableInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/ContextRef.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/ContextRefInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/Continuation.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/Decoder.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/DecoderInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/IOHandle.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/IOHandleInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/JavaObjectWrapper.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/JavaWrap.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/KnowHOWAttribute.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/KnowHOWAttributeInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/KnowHOWREPR.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/KnowHOWREPRInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/MultiCache.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/MultiCacheInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/MultiDimArray.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/MultiDimArrayInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/MultiDimArrayInstanceBase.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/MultiDimArrayInstance_i.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/MultiDimArrayInstance_i16.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/MultiDimArrayInstance_i32.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/MultiDimArrayInstance_i8.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/MultiDimArrayInstance_n.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/MultiDimArrayInstance_s.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/MultiDimArrayInstance_u16.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/MultiDimArrayInstance_u32.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/MultiDimArrayInstance_u8.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/MultiDimArrayREPRData.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/NFA.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/NFAInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/NFAStateInfo.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/NativeCall.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/NativeCallBody.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/NativeCallInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/NativeRef.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/NativeRefInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/NativeRefInstanceAttribute.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/NativeRefInstanceIntLex.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/NativeRefInstanceMultidim.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/NativeRefInstanceNumLex.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/NativeRefInstancePositional.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/NativeRefInstanceStrLex.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/NativeRefREPRData.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/P6Opaque.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/P6OpaqueBaseInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/P6OpaqueDelegateInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/P6OpaqueREPRData.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/P6bigint.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/P6bigintInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/P6int.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/P6intInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/P6num.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/P6numInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/P6str.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/P6strInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/ReentrantMutex.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/ReentrantMutexInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/Refreshable.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/SCRef.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/SCRefInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/Semaphore.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/SemaphoreInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/Uninstantiable.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMArray.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMArrayInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMArrayInstanceBase.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMArrayInstance_i.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMArrayInstance_i16.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMArrayInstance_i32.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMArrayInstance_i8.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMArrayInstance_n.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMArrayInstance_s.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMArrayInstance_u16.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMArrayInstance_u32.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMArrayInstance_u8.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMArrayREPRData.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMException.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMExceptionInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMHash.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMHashInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMIter.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMIterInstance.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMThread.java src/vm/jvm/runtime/org/perl6/nqp/sixmodel/reprs/VMThreadInstance.java src/vm/jvm/runtime/org/perl6/nqp/tools/EvalServer.java src/vm/jvm/stage0/JASTNodes.jar src/vm/jvm/stage0/ModuleLoader.jar src/vm/jvm/stage0/NQPCORE.setting.jar src/vm/jvm/stage0/NQPHLL.jar src/vm/jvm/stage0/NQPP6QRegex.jar src/vm/jvm/stage0/QAST.jar src/vm/jvm/stage0/QASTNode.jar src/vm/jvm/stage0/QRegex.jar src/vm/jvm/stage0/nqp.jar src/vm/jvm/stage0/nqpmo.jar src/vm/moar/HLL/Backend.nqp src/vm/moar/ModuleLoader.nqp src/vm/moar/NQP/Ops.nqp src/vm/moar/QAST/QASTCompilerMAST.nqp src/vm/moar/QAST/QASTOperationsMAST.nqp src/vm/moar/QAST/QASTRegexCompilerMAST.nqp src/vm/moar/profiler/template.html src/vm/moar/stage0/MASTNodes.moarvm src/vm/moar/stage0/MASTOps.moarvm src/vm/moar/stage0/ModuleLoader.moarvm src/vm/moar/stage0/NQPCORE.setting.moarvm src/vm/moar/stage0/NQPHLL.moarvm src/vm/moar/stage0/NQPP6QRegex.moarvm src/vm/moar/stage0/QAST.moarvm src/vm/moar/stage0/QASTNode.moarvm src/vm/moar/stage0/QRegex.moarvm src/vm/moar/stage0/nqp.moarvm src/vm/moar/stage0/nqpmo.moarvm t/concurrency/01-thread.t t/concurrency/02-lock.t t/concurrency/03-semaphore.t t/concurrency/04-osr-crash.t t/concurrency/TODO t/docs/opcodes.t t/docs/tests.t t/hll/01-language.t t/hll/02-modules.t t/hll/03-exports.t t/hll/04-import.t t/hll/05-commandline.t t/hll/06-sprintf.t t/js/continuations.t t/js/getcomp-js.t t/js/varint.js t/jvm/01-continuations.t t/jvm/05-decoder.t t/moar/01-continuations.t t/moar/02-qast-references.t t/moar/03-line-seps.t t/moar/04-argument-truncation.t t/moar/05-decoder.t t/moar/07-eqatic.t t/moar/08-indexic.t t/moar/09-concat.t t/moar/10-eqatim.t t/moar/11-decode.t t/moar/50-jit-register-alloc.t t/moar/51-jit-div_i.t t/nativecall/01-basic.t t/nqp/001-literals.t t/nqp/002-if.t t/nqp/003-if-else.t t/nqp/004-unless.t t/nqp/005-comments.t t/nqp/006-args-pos.t t/nqp/007-boolean.t t/nqp/008-blocks.t t/nqp/009-var.t t/nqp/010-cmp.t t/nqp/011-sub.t t/nqp/012-logical.t t/nqp/013-op.t t/nqp/014-while.t t/nqp/015-list.t t/nqp/016-ternary.t t/nqp/017-positional.t t/nqp/018-associative.t t/nqp/019-chars.txt t/nqp/019-file-ops.t t/nqp/020-return.t t/nqp/021-contextual.t t/nqp/022-optional-args.t t/nqp/023-named-args.t t/nqp/024-module.t t/nqp/025-class.t t/nqp/026-methodops.t t/nqp/027-self.t t/nqp/028-subclass.t t/nqp/029-make.t t/nqp/031-grammar.t t/nqp/032-protoregex.t t/nqp/033-init.t t/nqp/034-rxcodeblock.t t/nqp/035-prefix-sigil.t t/nqp/036-callable.t t/nqp/037-slurpy.t t/nqp/038-quotes.t t/nqp/039-pointy.t t/nqp/040-lists.t t/nqp/041-flat.t t/nqp/042-cond-loop.t t/nqp/043-package-var.t t/nqp/044-try-catch.t t/nqp/045-smartmatch.t t/nqp/046-charspec.t t/nqp/047-loop-control.t t/nqp/048-closure.t t/nqp/049-regex-interpolation.t t/nqp/050-regex.t t/nqp/051-multi.t t/nqp/053-knowhow.t t/nqp/055-multi-method.t t/nqp/056-role.t t/nqp/057-construction.t t/nqp/058-attrs.t t/nqp/059-nqpop.t t/nqp/060-bigint.t t/nqp/061-mixin.t t/nqp/062-subst.t t/nqp/063-slurp.t t/nqp/064-native.t t/nqp/065-how.t t/nqp/066-pararole.t t/nqp/067-container.t t/nqp/068-associative-for.t t/nqp/069-js-keywords-as-identifier.t t/nqp/070-invokespec.t t/nqp/071-setboolspec.t t/nqp/072-rolehow.t t/nqp/073-delegation.t t/nqp/074-nfa.t t/nqp/075-curcode.t t/nqp/076-capture.t t/nqp/077-curlexpad.t t/nqp/079-callercode.t t/nqp/080-matches.t t/nqp/081-radix.t t/nqp/082-decode.t t/nqp/083-math.t t/nqp/084-loop-labels.t t/nqp/085-type-tester.t t/nqp/087-parametric-6model.t t/nqp/088-more-if-tests.t t/nqp/089-istype.t t/nqp/090-findmethod.t t/nqp/091-codename.t t/nqp/092-where.t t/nqp/093-oo-ops.t t/nqp/094-clone.t t/nqp/095-cclass.t t/nqp/096-array-methods.t t/nqp/097-hll.t t/nqp/098-boxing.t t/nqp/099-getstaticcode.t t/nqp/100-dispatcher.t t/nqp/101-lexpad-stuff.t t/nqp/102-multidim.t t/nqp/103-typecache.t t/nqp/104-method-cache.t t/nqp/104-refs.t t/nqp/105-multicache.t t/nqp/106-unicodenames.t t/nqp/107-index.t t/nqp/108-vmhash.t t/nqp/109-coercions.t t/nqp/110-normalization.t t/nqp/111-spawnprocasync.t t/nqp/112-call.t t/nqp/113-run-command.t t/nqp/114-pod-panic.t t/nqp/19-readline.txt t/nqp/19-setinputlinesep.txt t/p5regex/01-p5regex.t t/p5regex/rx_basic t/p5regex/rx_captures t/p5regex/rx_charclass t/p5regex/rx_metachars t/p5regex/rx_modifiers t/p5regex/rx_quantifiers t/qast/01-qast.t t/qast/02-manipulation.t t/qregex/01-qregex.t t/qregex/rx_backtrack t/qregex/rx_basic t/qregex/rx_captures t/qregex/rx_charclass t/qregex/rx_goal t/qregex/rx_lookarounds t/qregex/rx_metachars t/qregex/rx_modifiers t/qregex/rx_qcaps t/qregex/rx_quantifiers t/qregex/rx_subrules t/qregex/rx_syntax t/serialization/01-basic.t t/serialization/02-types.t t/serialization/03-closures.t t/serialization/04-repossession.t tools/analyze-parse tools/build/MOAR_REVISION tools/build/Makefile-JVM.in tools/build/Makefile-Moar.in tools/build/Makefile-common.in tools/build/gen-bootstrap.pl tools/build/gen-cat.pl tools/build/gen-js-cross-runner.pl tools/build/gen-js-makefile.nqp tools/build/gen-js-runner.pl tools/build/gen-jvm-properties.pl tools/build/gen-moar-runner.pl tools/build/gen-version.pl tools/build/generate-constants.pl tools/build/install-js-runner.pl tools/build/install-jvm-runner.pl.in tools/build/install-moar-runner.pl tools/build/npm-install-or-link.pl tools/build/process-qregex-tests tools/find-undocumented-ops.p6 tools/jvm/eval-client.pl tools/lib/NQP/Configure.pm tools/missing-js-tests nqp-2018.03/.editorconfig0000644000175000017500000000054713253717146013625 0ustar alexalex# EditorConfig is awesome: http://EditorConfig.org # top-most EditorConfig file root = true # Unix-style newlines with a newline ending every file [*] end_of_line = lf insert_final_newline = true indent_style = space indent_size = 4 trim_trailing_whitespace = true [t/p5regex/*] indent_style = tab [t/qregex/*] indent_style = tab [*.t] indent_style = space nqp-2018.03/.eslintrc.js0000644000175000017500000000070413253717146013402 0ustar alexalexmodule.exports = { "extends": "google", "parserOptions": { "ecmaVersion": 6 }, "rules": { "require-jsdoc": "off", "max-len": "off", "arrow-parens": "off", "no-extend-native": "off", "camelcase": "off", "guard-for-in": "off", "prefer-rest-params": "off", "prefer-spread": "off", "no-invalid-this": "off", "no-throw-literal": "off", "prefer-const": "error" } }; nqp-2018.03/.gitignore0000644000175000017500000000107313253717146013133 0ustar alexalexMakefile /install .*.swp *.o *.so *.RES *.res *.ilk *.obj *.rc *.dll *.dll.a *.manifest *.exp *.lib *.pdb *.dump *.exe *.suo *.bundle *~ /nqp /*.html nqp.c nqp_group.c nqp_group.h src/gen/*.nqp src/gen/*.pm src/gen/*.setting src/stage1 src/stage2 tools/build/install-jvm-runner.pl config.status config.default 3rdparty/dyncall/ConfigVars 3rdparty/dyncall/*/*.a node_modules nqp-runtime.jar *.class *.jar /nqp-j /nqp-p /nqp-m /nqp-js /nqp-js-cross /nqp-j.bat /nqp-p.bat /nqp-m.bat /nqp-js.bat /nqp-js-cross.bat /jvmconfig.properties *.moarvm MoarVM MANIFEST /bin /nqp.bat nqp-2018.03/.travis.yml0000644000175000017500000000131513253717146013253 0ustar alexalexlanguage: "java" sudo: false dist: trusty install: "echo" #install: travis_wait 30 mvn install #script: "perl Configure.pl $NQP_OPTIONS; make test" script: - perl Configure.pl $NQP_OPTIONS - make - travis_wait 30 make test branches: only: - master - /smoke-me/ notifications: irc: channels: - "irc.freenode.net#perl6-dev" on_success: change on_failure: always template: - "NQP build %{result}. %{author} '%{commit_message}'" - "%{build_url} %{compare_url}" env: matrix: - NQP_OPTIONS="--gen-moar" - NQP_OPTIONS="--gen-moar=master" - NQP_OPTIONS="--backends=jvm" #matrix: # allow_failures: # - env: NQP_OPTIONS="--backends=jvm" nqp-2018.03/CREDITS0000644000175000017500000000273513253717146012171 0ustar alexalex=pod Following in the steps of other open source projects that eventually take over the world, here is the partial list of people who have contributed to Rakudo and its supporting works. It is sorted by name and formatted to allow easy grepping and beautification by scripts. The fields are: name (N), email (E), web-address (W), description (D), GitHub username (U) and snail-mail address (S). Thanks, The NQP Team PS: Yes, this looks remarkably like the Linux CREDITS format PPS: This file is encoded in UTF-8 ---------- N: Daniel Arbelo Arrocha U: darbelo E: arbelo@gmail.com D: Minor code contributions (plumage) N: Geoff Broadwell U: japhb E: geoff@broadwell.org D: Initial design and implementation of Plumage. N: Jonathan "Duke" Leto U: leto D: Perl 6 (Rakudo Perl) developer E: jonathan@leto.net N: Jonathan Scott Duff U: perlpilot D: Perl 6 (Rakudo Perl) developer E: duff@pobox.com N: Jonathan Worthington U: jnthn D: Replacing object model with 6model, new multi-dispatcher E: jnthn@jnthn.net N: Patrick R. Michaud U: pmichaud D: Perl 6 (Rakudo Perl) lead developer, pumpking E: pmichaud@pobox.com N: Stefan O'Rear U: sorear D: Lexical persistance, POD, and other miscellaneous contributions E: stefanor@cox.net N: Stephen Weeks U: tene E: tene@allalone.org D: Assorted contributions (plumage) N: Vasily Chekalkin U: bacek D: Work on bringing Settings to NQP based on Plumage's NQPUtils. E: bacek@bacek.com =cut nqp-2018.03/Configure.pl0000755000175000017500000003006113253717146013423 0ustar alexalex#!/usr/bin/env perl # Copyright (C) 2009 The Perl Foundation use 5.008; use strict; use warnings; use Text::ParseWords; use Getopt::Long; use Cwd qw/abs_path cwd/; use File::Spec; use lib "tools/lib"; use NQP::Configure qw(cmp_rev gen_moar fill_template_file fill_template_text probe_node slurp system_or_die verify_install sorry); my @known_backends = qw/moar jvm js/; my %known_backends = map { $_, 1; } @known_backends; my %prefixes = ( moar => 'm', jvm => 'j', js => 'js' ); MAIN: { if (-r "config.default") { unshift @ARGV, shellwords(slurp('config.default')); } my $slash = $^O eq 'MSWin32' ? '\\' : '/'; my %config = (perl => $^X); $config{'nqp_config_status'} = join(' ', map { "\"$_\""} @ARGV); my $exe = $NQP::Configure::exe; my %options; GetOptions(\%options, 'help!', 'prefix=s', 'libdir=s', 'sysroot=s', 'sdkroot=s', 'backends=s', 'no-clean', 'with-moar=s', 'gen-moar:s', 'moar-option=s@', 'with-asm=s', 'with-asm-tree=s', 'with-jline=s', 'with-jna=s', 'make-install!', 'makefile-timing!', 'git-protocol=s', 'ignore-errors', 'link', 'git-depth=s', 'git-reference=s',); # Print help if it's requested if ($options{'help'}) { print_help(); exit(0); } if ($options{'ignore-errors'}) { print "===WARNING!===\nErrors are being ignored.\nIn the case of any errors the script may behave unexpectedly.\n"; } if ($options{'with-asm'}) { if ($options{'with-asm'} ne '-') { $config{'asm'} = $options{'with-asm'}; } } else { $config{'asm'} = "3rdparty/asm/asm-4.1.jar"; } if ($options{'with-asm-tree'}) { if ($options{'with-asm-tree'} ne '-') { $config{'asmtree'} = $options{'with-asm-tree'}; } } else { $config{'asmtree'} = "3rdparty/asm/asm-tree-4.1.jar"; } if ($options{'with-jline'}) { if ($options{'with-jline'} ne '-') { $config{'jline'} = $options{'with-jline'}; } } else { $config{'jline'} = "3rdparty/jline/jline-1.0.jar"; } if ($options{'with-jna'}) { if ($options{'with-jna'} ne '-') { $config{'jna'} = $options{'with-jna'}; } } else { $config{'jna'} = "3rdparty/jna/jna-4.0.0.jar"; } if ($^O eq 'MSWin32') { $config{'asmfile'} = $config{'asm'}; $config{'asmfile'} =~ s/.*\\//; $config{'jlinefile'} = $config{'jline'}; $config{'jlinefile'} =~ s/.*\\//; } else { $config{'asmfile'} = $config{'asm'}; $config{'asmfile'} =~ s/.*\///; $config{'jlinefile'} = $config{'jline'}; $config{'jlinefile'} =~ s/.*\///; } fill_template_file( 'tools/build/install-jvm-runner.pl.in', 'tools/build/install-jvm-runner.pl', %config, ); my $default_backend; my @backends; my %backends; if ($options{backends}) { $options{backends} = join ',', @known_backends if lc($options{backends}) eq 'all'; for my $be (split /,/, $options{backends}) { $be = lc $be; unless ($known_backends{$be}) { die "Unknown backend: '$be'; Known backends: " . join(', ', sort keys %known_backends) . "\n"; } $default_backend ||= $be; push @backends, $be unless $backends{$be}; $backends{$be} = 1; } } if (defined $options{'gen-moar'}) { push @backends, 'moar' unless $backends{moar}; $backends{moar} = 1; $default_backend ||= 'moar'; } unless (%backends) { # TODO: come up with more sensible defaults $backends{moar} = 1; push @backends, 'moar'; $default_backend = 'moar'; } if ($backends{js} and !$backends{moar}) { sorry($options{'ignore-errors'}, "When building the js backend you must also build moar\nPlease build with --backends=moar,js\n"); } # XXX mkpath instead? mkdir($options{'prefix'}) if $options{'prefix'} && $^O =~ /Win32/ && !-d $options{'prefix'}; my $prefix = $options{'prefix'} ? abs_path($options{'prefix'}) : ($options{sysroot} ? '/usr' : File::Spec->catdir(cwd, 'install')); $config{prefix} = $prefix; $config{nqplibdir} = $options{libdir} ? "$options{libdir}/nqp" : '$(NQP_LANG_DIR)/lib'; $config{sysroot} = $options{sysroot}; $config{sdkroot} = $options{sdkroot}; # Save options in config.status unlink('config.status'); if (open(my $CONFIG_STATUS, '>', 'config.status')) { print $CONFIG_STATUS "$^X Configure.pl $config{'nqp_config_status'} \$*\n"; close($CONFIG_STATUS); } $config{'makefile-timing'} = $options{'makefile-timing'}; $config{'stagestats'} = '--stagestats' if $options{'makefile-timing'}; $config{'shell'} = $^O eq 'MSWin32' ? 'cmd' : 'sh'; $config{'bat'} = $^O eq 'MSWin32' ? '.bat' : ''; $config{'cpsep'} = $^O eq 'MSWin32' ? ';' : ':'; $config{'slash'} = $slash; open my $MAKEFILE, '>', 'Makefile' or die "Cannot open 'Makefile' for writing: $!"; my @prefixes = map { $prefixes{$_} } @backends; print $MAKEFILE "\n# Makefile code generated by Configure.pl:\n"; my $launcher = $prefixes{$default_backend} . '-runner-default'; print $MAKEFILE "all: ", join(' ', map("$_-all", @prefixes), $launcher), "\n"; print $MAKEFILE "install: ", join(' ', map("$_-install", @prefixes), $launcher . '-install'), "\n"; for my $t (qw/clean test qregex-test/) { print $MAKEFILE "$t: ", join(' ', map "$_-$t", @prefixes), "\n"; } fill_template_file( 'tools/build/Makefile-common.in', $MAKEFILE, %config, ); if ($backends{moar}) { my @errors; my ($moar_want) = split(' ', slurp('tools/build/MOAR_REVISION')); my ($moar_path, @moar_errors) = gen_moar($moar_want, %config, %options); if (!$moar_path) { push @errors, "No suitable MoarVM (moar executable) found using the --prefix\n" . "(You can get a MoarVM built automatically with --gen-moar.)"; unshift @errors, @moar_errors if @moar_errors; } sorry($options{'ignore-errors'}, @errors) if @errors; # If we ignore errors, normally we'd print out the @moar_errors elsewhere # so make sure to print them out now. Don't print unless errors are ignored print join("\n", '', @moar_errors, "\n") if @moar_errors and $options{'ignore-errors'}; $config{'make'} = `$moar_path --libpath="src/vm/moar/stage0" "src/vm/moar/stage0/nqp.moarvm" -e "print(nqp::backendconfig())"` || 'make'; $config{moar} = $moar_path; $config{moar_prefix} = File::Spec->catpath((File::Spec->splitpath($moar_path))[0, 1], File::Spec->updir); fill_template_file( 'tools/build/Makefile-Moar.in', $MAKEFILE, %config, ); } if ($backends{js}) { system_or_die($config{moar}, '--libpath=src/vm/moar/stage0', 'src/vm/moar/stage0/nqp.moarvm', 'tools/build/gen-js-makefile.nqp', 'gen/js/Makefile-JS.in'); $config{'make'} = $^O eq 'MSWin32' ? 'nmake' : 'make'; $config{link} = $options{link}; my $node = probe_node(); if ($node eq 'nodejs') { sorry($options{'ignore-errors'}, 'You have a broken node.js. Please install node.js as node instead of nodejs.') } elsif (!$node) { sorry($options{'ignore-errors'}, "You don't have node.js. Please install node.js."); } fill_template_file( 'gen/js/Makefile-JS.in', $MAKEFILE, %config, ); } if ($backends{jvm}) { my @errors; my $got; if (!@errors) { my @jvm_info = `java -showversion 2>&1`; my $jvm_found = 0; my $jvm_ok = 0; for (@jvm_info) { print "got: $_"; if (/(?:java|jdk) version "(\d+)(?:\.(\d+))?/) { $jvm_found = 1; if ($1 > 1 || $1 == 1 && $2 >= 8) { $jvm_ok = 1; } $got = $_; last; } } if (!$jvm_found) { push @errors, "No JVM (java executable) in path; cannot continue"; } elsif (!$jvm_ok) { push @errors, "Need at least JVM 1.8 (got $got)"; } } sorry($options{'ignore-errors'}, @errors) if @errors; print "Using $got\n"; $config{'make'} = $^O eq 'MSWin32' ? 'nmake' : 'make'; $config{'runner'} = $^O eq 'MSWin32' ? 'nqp.bat' : 'nqp'; fill_template_file( 'tools/build/Makefile-JVM.in', $MAKEFILE, %config, ); } my $ext = ''; if ($^O eq 'MSWin32') { $ext = '.bat'; } print $MAKEFILE qq[t/*/*.t: all\n\tprove -r -v --exec ./nqp$ext \$\@\n]; close $MAKEFILE or die "Error while writing to 'Makefile': $!"; my $make = fill_template_text('@make@', %config); unless ($options{'no-clean'}) { no warnings; print "Cleaning up ...\n"; if (open my $CLEAN, '-|', "$make clean") { my @slurp = <$CLEAN>; close($CLEAN); } } if ($options{'make-install'}) { system_or_die($make); system_or_die($make, 'install'); print "\nNQP has been built and installed.\n"; } else { print "You can now use '$make' to build NQP.\n"; print "After that, '$make test' will run some tests and\n"; print "'$make install' will install NQP.\n"; } exit 0; } # Print some help text. sub print_help { my $backends = join ',',keys %known_backends; print <<"END"; Configure.pl - NQP Configure General Options: --help Show this text --prefix=dir Install files in dir --sdkroot=dir When given, use for searching build tools here, e.g. nqp, java etc. --sysroot=dir When given, use for searching runtime components here --backends=list Backends to use: $backends --gen-moar Download, build, and install a copy of MoarVM to use before writing the Makefile --moar-option='--option=value' Options to pass to MoarVM configuration for --gen-moar --with-moar='/path/to/moar' Provide path to already installed moar binary --with-asm='/path/to/jar' --with-asm-tree='/path/to/jar' --with-jline='/path/to/jar' --with-jna='/path/to/jar' Provide paths to already installed jars --git-protocol={ssh,https,git} Protocol to use for git clone. Default: https --make-install Immediately run `MAKE install` after configuring --git-depth= Use the --git-depth option for git clone with parameter number --git-reference= Use --git-reference option to identify local path where git repositories are stored For example: --git-reference=/home/user/repo/for_perl6 Folders 'nqp', 'MoarVM' with corresponding git repos should be in for_perl6 folder --ignore-errors Can ignore errors such as what version MoarVM or the JVM is. May not work for other errors currently. Please note that the --gen-moar option is there for convenience only and will actually immediately - at Configure time - compile and install moar. Moar will live under the path given to --prefix, unless other targeting options are used. Configure.pl also reads options from 'config.default' in the current directory. END return; } # Local Variables: # mode: cperl # cperl-indent-level: 4 # fill-column: 100 # End: # vim: expandtab shiftwidth=4: nqp-2018.03/LICENSE0000644000175000017500000002547213253717146012161 0ustar alexalex The Artistic License 2.0 Copyright (c) 2000-2006, The Perl Foundation. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble This license establishes the terms under which a given free software Package may be copied, modified, distributed, and/or redistributed. The intent is that the Copyright Holder maintains some artistic control over the development of that Package while still keeping the Package available as open source and free software. You are always permitted to make arrangements wholly outside of this license directly with the Copyright Holder of a given Package. If the terms of this license do not permit the full use that you propose to make of the Package, you should contact the Copyright Holder and seek a different licensing arrangement. Definitions "Copyright Holder" means the individual(s) or organization(s) named in the copyright notice for the entire Package. "Contributor" means any party that has contributed code or other material to the Package, in accordance with the Copyright Holder's procedures. "You" and "your" means any person who would like to copy, distribute, or modify the Package. "Package" means the collection of files distributed by the Copyright Holder, and derivatives of that collection and/or of those files. A given Package may consist of either the Standard Version, or a Modified Version. "Distribute" means providing a copy of the Package or making it accessible to anyone else, or in the case of a company or organization, to others outside of your company or organization. "Distributor Fee" means any fee that you charge for Distributing this Package or providing support for this Package to another party. It does not mean licensing fees. "Standard Version" refers to the Package if it has not been modified, or has been modified only in ways explicitly requested by the Copyright Holder. "Modified Version" means the Package, if it has been changed, and such changes were not explicitly requested by the Copyright Holder. "Original License" means this Artistic License as Distributed with the Standard Version of the Package, in its current version or as it may be modified by The Perl Foundation in the future. "Source" form means the source code, documentation source, and configuration files for the Package. "Compiled" form means the compiled bytecode, object code, binary, or any other form resulting from mechanical transformation or translation of the Source form. Permission for Use and Modification Without Distribution (1) You are permitted to use the Standard Version and create and use Modified Versions for any purpose without restriction, provided that you do not Distribute the Modified Version. Permissions for Redistribution of the Standard Version (2) You may Distribute verbatim copies of the Source form of the Standard Version of this Package in any medium without restriction, either gratis or for a Distributor Fee, provided that you duplicate all of the original copyright notices and associated disclaimers. At your discretion, such verbatim copies may or may not include a Compiled form of the Package. (3) You may apply any bug fixes, portability changes, and other modifications made available from the Copyright Holder. The resulting Package will still be considered the Standard Version, and as such will be subject to the Original License. Distribution of Modified Versions of the Package as Source (4) You may Distribute your Modified Version as Source (either gratis or for a Distributor Fee, and with or without a Compiled form of the Modified Version) provided that you clearly document how it differs from the Standard Version, including, but not limited to, documenting any non-standard features, executables, or modules, and provided that you do at least ONE of the following: (a) make the Modified Version available to the Copyright Holder of the Standard Version, under the Original License, so that the Copyright Holder may include your modifications in the Standard Version. (b) ensure that installation of your Modified Version does not prevent the user installing or running the Standard Version. In addition, the Modified Version must bear a name that is different from the name of the Standard Version. (c) allow anyone who receives a copy of the Modified Version to make the Source form of the Modified Version available to others under (i) the Original License or (ii) a license that permits the licensee to freely copy, modify and redistribute the Modified Version using the same licensing terms that apply to the copy that the licensee received, and requires that the Source form of the Modified Version, and of any works derived from it, be made freely available in that license fees are prohibited but Distributor Fees are allowed. Distribution of Compiled Forms of the Standard Version or Modified Versions without the Source (5) You may Distribute Compiled forms of the Standard Version without the Source, provided that you include complete instructions on how to get the Source of the Standard Version. Such instructions must be valid at the time of your distribution. If these instructions, at any time while you are carrying out such distribution, become invalid, you must provide new instructions on demand or cease further distribution. If you provide valid instructions or cease distribution within thirty days after you become aware that the instructions are invalid, then you do not forfeit any of your rights under this license. (6) You may Distribute a Modified Version in Compiled form without the Source, provided that you comply with Section 4 with respect to the Source of the Modified Version. Aggregating or Linking the Package (7) You may aggregate the Package (either the Standard Version or Modified Version) with other packages and Distribute the resulting aggregation provided that you do not charge a licensing fee for the Package. Distributor Fees are permitted, and licensing fees for other components in the aggregation are permitted. The terms of this license apply to the use and Distribution of the Standard or Modified Versions as included in the aggregation. (8) You are permitted to link Modified and Standard Versions with other works, to embed the Package in a larger work of your own, or to build stand-alone binary or bytecode versions of applications that include the Package, and Distribute the result without restriction, provided the result does not expose a direct interface to the Package. Items That are Not Considered Part of a Modified Version (9) Works (including, but not limited to, modules and scripts) that merely extend or make use of the Package, do not, by themselves, cause the Package to be a Modified Version. In addition, such works are not considered parts of the Package itself, and are not subject to the terms of this license. General Provisions (10) Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License. By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license. (11) If your Modified Version has been derived from a Modified Version made by someone other than you, you are nevertheless required to ensure that your Modified Version complies with the requirements of this license. (12) This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder. (13) This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counterclaim) against any party alleging that the Package constitutes direct or contributory patent infringement, then this Artistic License to you shall terminate on the date that such litigation is filed. (14) Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Portions Copyright (c) 2011 Alexander Shtuchkin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Portions Copyright (c) 2008-2009 Bjoern Hoehrmann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. nqp-2018.03/README.pod0000644000175000017500000001145113253717146012605 0ustar alexalex=pod =head1 NQP - Not Quite Perl (6) NQP is Copyright (C) 2009-2018 by The Perl Foundation. See F for licensing details. This is "Not Quite Perl" -- a lightweight Perl 6-like environment for virtual machines. The key feature of NQP is that it's designed to be a very small environment (as compared with, say, perl6 or Rakudo) and is focused on being a high-level way to create compilers and libraries for virtual machines like MoarVM [1], the JVM, and others. Unlike a full-fledged implementation of Perl 6, NQP strives to have as small a runtime footprint as it can, while still providing a Perl 6 object model and regular expression engine for the virtual machine. [1] https://github.com/MoarVM/MoarVM =head2 Building from source =for HTML To build NQP from source, you'll just need a C utility and Perl 5.8 or newer. To automatically obtain and build MoarVM you may also need a git client. To obtain NQP directly from its repository: $ git clone git://github.com/perl6/nqp.git If you don't have git installed, you can get a tarball or zip of NQP from github by visiting http://github.com/perl6/nqp/tree/master and clicking "Download". Then unpack the tarball or zip. NQP can run on three different backends: MoarVM, the JVM, and JavaScript. The JVM and JavaScript backends are currently experimental. The JVM backend requires JDK 1.8. Decide on which backends you want it to run, and configure and build it as follows: Once you have a copy of NQP, build it as follows: $ cd nqp $ perl Configure.pl --backends=moar,jvm $ make If you don't have an installed MoarVM, you can have Configure.pl build one for you by passing the C<--gen-moar> option to it as well. The C step will create a "nqp" or "nqp.exe" executable in the current directory. Programs can then be run from the build directory using a command like: $ ./nqp hello.nqp By default, NQP searches for the MoarVM executable and installs to the directory C<./install>. You can change that with the C<--prefix> option to Configure.pl. Once built, NQP's C target will install NQP and its libraries into the same location as the MoarVM installation that was used to create it. Until this step is performed, the "nqp" executable created by C above can only be reliably run from the root of NQP's build directory. After C is performed the executable can be run from any directory. If the NQP compiler is invoked without an explicit script to run, it enters a small interactive mode that allows statements to be executed from the command line. Each line entered is treated as a separate compilation unit, however (which means that subroutines are preserved after they are defined, but variables are not). =head2 Troubleshooting =head3 OS X On OS X, it appears that configuration fails in some configurations: 3rdparty/libuv/include/uv-darwin.h:26:11: fatal error: 'mach/mach.h' file not found Should this happen to you, then a solution might be the following: $ cd MoarVM/3rdparty/libuv/include $ ln -s /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/mach $ cd ../../../.. $ # run the Configure again Note that the 10.9 in the above, represents the major version of OS X being used. On Mavericks use 10.9 (like above), on Yosemite use 10.10. =head2 Using NQP B there's B for NQP and the behaviour can change without notice. It's a tool for writing Perl 6 compilers, not a low-level module for Perl 6 programmers. The L is a good place to start, with the L and other files. Opcodes are listed in L. NQP also has built-in routines listed in L. You can use NQP from this release, it will be already installed if you have built Perl6 from scratch. =head2 JavaScript backend The best thing before playing with it/hacking on it is to contact pmurias via IRC at #perl6 on irc.freenode.org. It's recommended that you use node 7.10.0 as it's the version of node.js the JavaScript backend is developed with. Building the JavaScript backend currently requires building the moar backend: $ perl Configure.pl --backends=moar,js $ make Currently it needs to be run like: $ ./nqp-js file.nqp If you are developing nqp-js, you may want to pass the --link option to configure to have the nqp-runtime linked instead of installed $ cd src/vm/js/nqp-runtime; npm link . $ perl Configure --backends=moar,js =cut nqp-2018.03/VERSION0000644000175000017500000000001013253717231012174 0ustar alexalex2018.03 nqp-2018.03/docs/6model/faq.markdown0000644000175000017500000000366013253717146015600 0ustar alexalex# FAQ This document tries to collect various frequently asked questions about 6model and provides answers to them. ## My language has objects but no methods; is 6model a bad fit? Not really. You are not forced to implement methods in the meta-object to handle add_method or find_method, and of course your meta-object would thus have no storage allocated for a method table. The only wastage is that you have no usage for the method cache and v-table slots in the s-table. That means that per type (not per object instance) you'd have a couple of wasted slots, each a pointer in size. Thus on a 32-bit machine, 100 classes/types later, you've still yet to "waste" a kilobyte. ## What is a type object, and how should I use it? When you pair a meta-class and a representation together, an s-table is created along with a type object. The type-object becomes your "handle" to that pairing, and can be used to create instances of the object. In some languages (Perl 6), you would also install the type object into the namespace (package or lexpad), as the user's view of the type. However, that is not at all needed. For a prototype-based language, you probably only ever make one type object. You can just stash it somewhere and use it whenever you need to create a new instance. If new instances are always just clones anyway, then you needn't even keep hold of it, just install the initial instance you create wherever it should go and then it can be cloned from there on in. For a class based language, you'll always need to have *some* way of identifying a class to make an instance of. The type object is your handle for creating an instance. You may make a lookup table of the type objects and map instantiations of types through that table. If you install a "class object" of some kind and it is what manages the instantiation then you could have it holding the handle. How you handle it is really very dependent on how your language exposes object creation. nqp-2018.03/docs/6model/overview.markdown0000644000175000017500000002336313253717146016701 0ustar alexalex# Overview ## What is 6model? 6model is a framework for building implementations of object oriented features. It does not contain an implementation of classes, interfaces, roles, prototype objects and so forth. Instead, it provides you with a set of building blocks that enable you to build these kinds of features as your language needs them. To make an analogy with another part of the compiler toolkit, we don't provide a full grammar for your language, but do provide a grammar engine, quote parser, operator precedence parser and other helpful building blocks to get you started. ## 6model Workflow In general, when you want to implement the object system for your language using 6model, you will go through the following workflow. 1. Identify the kinds of object oriented types your language has and that you will need to implement. For example, in Perl 6 we have - amongst other things - classes, roles and enumerations. In Java, you'd have classes and interfaces. In JavaScript, you have prototype objects. 2. For each of them, pick a suitable in-memory representation. 6model provides some of these out of the box. In a language where you know all of the attributes an object will have "up-front", you can pick a representation that will just allocate a single piece of memory for an object and map each attribute to a slot. In a language where you can get attributes coming to exist at any point and an object is just like a hash table or dictionary, you can pick a representation where your object works just like a hash. 3. Having picked a representation - which will control how your objects are allocated and store/fetch attributes - you now implement meta-objects for each kind of object oriented type in your language. A meta-object is simply an object that responds to different events that occur in the life of a type. For example, at declaration time we create a new type, add methods, add a parent type (e.g. inheritance), add attributes and so forth. Later on we may locate methods, do dynamic type checks (e.g. is-a). Implementing a meta-object just involves writing a method for to handle each of these "events". 4. Compile the language's type declarations to calls on the meta-object. Thus it's the meta-object that decides how most aspects of a type work: whether it can accept having more than one parent (if it does at all), how it dispatches methods (if it does at all) and so forth. However, certain aspects are handled by the representation: allocation of an object, storage of attributes and boxing/unboxing. In some VMs the representation also needs to interact with the GC. Essentially, meta-object + representation = full implementation of some kind of object oriented type (e.g. a class). Usually, you will just pick an existing representation and implement the meta-object yourself. Notice this means that you can factor your implementation of OO features using OO principles. ## Representations Representations are low-level things that are closely tied to the underlying VM. They do expose a common API over all of them, however. The following operations are available on representations. * **type_object_for** takes a meta-object and creates a type object that represents the association of the provided meta-object and this representation. What you do with the type object depends on your language. You could see it as an "instantiation handle" - you use it in order to create an instance of an object with the given meta-object/representation pairing. * **instance_of** does exactly what it says - creates a new instance. You can supply it with the type object OR with any existing instance, it doesn't matter. Note that this really does just allocate space in memory for the object; it's not any kind of "high level" instantiation where attributes get pre-populated with empty containers. * **defined** basically tells you if the thing you have is a type object or an actual instance. For many languages you'll not need this. It may come in useful in some languages to distinguish static and instance method dispatch. * **get_attribute** looks up an attribute. It expects to give back an object of some kind. There are variants for native types: **get_attribute_int**, **get_attribute_num** and **get_attribute_str**. All of them expect the object to access the attribute of. You may also pass one or all of an attribute name, a class handle (for when attributes are not keyed just one name) and an index for when it's possible to access an attribute by an offset rather than just by name. What's needed varies by representation. * **bind_attribute** and its native friends are basically what you'd expect, and look largely like get_attribute apart from they take something to store in the attribute rather than doing a lookup. * **hint_for** takes an attribute name and optional a class handle and will hand back - if available - an offset that can also be used to look up the attribute. Interesting in gradual or static typing scenarios. * 6model representations may provide direct support for boxing and unboxing native types. This is exposed through [set|get]_[int|num|str] operations. Details vary by representation. ## Meta-objects A meta-object is simply an object that says how another object works. Critically, it's __just an object__. It may have attributes and it will certainly have methods. Like any other object, it also has a meta-object that says how it works. You may be tempted to start categorising objects into "normal objects" versus "meta-objects", but it's important to understand that 6model makes no distinction. A meta-object isn't any kind of special object, it's just an object that happens to be serving a particular role. Meta-objects contain methods that relate to certain events in the lifetime of a type. Let's imagine we have a really simple language where we can declare some kind of type that has methods, make instances of it and call the methods (granted, the instances are a bit useless until we add attributes). We could write (in NQP) the following. class SimpleMetaObject { has %!methods; method new_type() { my $meta-object := self.new(); return nqp::newtype($meta-object, 'HashAttrStore'); } method add_method($type, $name, $code) { %!methods{$name} := $code; } method find_method($type, $name) { %!methods{$name} } } Notice how this meta-object is just written as a normal class. Of course, the class itself has a meta-object saying how it works - it's meta-objects all the way down. The next thing to note is that we just used a hash in order to implement our method store. Creating a new type and adding a method are just methods that we include. Here's how we can use the meta-object to define a new type, and add a method. # Create a new type. my $type := SimpleMetaObject.new(); # Add a method. $type.HOW.add_method($type, 'drink', -> $self { say("mmmm...Starobrno!") }); The .HOW macro in NQP maps to the gethow opcode and it is used to get hold of the meta-object. We just pass a simple lambda expression in to add_method in order to specify what happens when the method is called. Finally, we can make an instance and call the method. # Make an instance. my $obj := nqp::create($type); # Call the method. $obj.drink(); Notice that we didn't ever call our find_method method ourselves. In fact, this is the only place so far where 6model has actually given us any kind of restriction on how our meta-object should look; the names of add_method and new_type are conventions. Under the hood, 6model has gone and used the find_method method on the meta-object when we did a method call on the object. A question you may be left with is why the convention to take the type object as the first parameter in all of the methods of the meta-object. This isn't needed when doing something more "class based", but is important in a more prototype-OO-ish setting. ## Performance So far we have seen that one would tend to implement things like method dispatch and type checking by writing methods in the meta-object. This may feel a little heavyweight for an operation that is very common, and indeed it is. 6model thus provides you with the possibility to publish some "caches" that expose views of certain aspects of the meta-object in a more low-level way to the VM. Thus it is able to perform some frequent opertaions much more quickly. Of note, it is possible to publish: * A name to method cache, which is useful for dispatching methods by name. This is useful in most dynamic languages, and means that method dispatch can just be looking in a hash table. Generally this presents a flat view, with the methods from any parent types included so there is no walking required at runtime. * A v-table, which you can also thing of as an index to method cache. This is useful in static or gradually typed languages, where a method call can be mapped to looking for the method in a certain slot. Since this boils down to a (machine-level) array lookup, it's fast. * A type check cache. This is useful for doing fast type checks. It contains references to other types that the current one could be considered as "doing"/"being". Thus it is useful for is-a and does-a operations. The current 6model implementation has it being scanned linearly. This is a low-level loop over a low-level array, so it should be pretty fast, however. The word "cache" is chosen very deliberately. It's important that the meta-object knows that it is responsible for the updating of any of the extra views of itself that it chooses to publish. Put another way, in 6model the meta-object itself is always the authoritative source. nqp-2018.03/docs/6model/repr-compose-protocol.markdown0000644000175000017500000001123313253717146021276 0ustar alexalex# REPR Compose Protocol In 6model, representations take responsibility for memory layout, while a meta-object is responsible for other aspects of type-ness (dispatch, type checking, etc.) Typically, meta-objects are at some point "composed" - that is, they reach a point where the definition of the type they represent is either closed or complete in some sense. Certain representations need to be (or in some cases, may be) configured. For example, a representation that does efficient storage of an object's attributes needs configuring with information about the things it should store. This configuration happens through the REPR composition protocol. The meta-object uses the nqp::composetype primitive in order to do this. The protocol is defined entirely in terms of arrays and hashes. Why? So we don't have to define a particularly complex object system in order to define an object system. :-) At the top level, a hash is always passed. Its keys indicate the configuration for a specific part of the protocol, the values are a data structure providing the appropriate information. The rest of this documentation describes these protocols; each heading is a key that can appear in the top level hash, and what's below it indicates what should fall beneath those keys. ## attribute The value is an array with one element for each entry in the MRO, from most derived to least derived. Each element is in turn an array, consisting of 3 elements: * The type object of the type at this point in the MRO * An array of hashes, each hash describing an attribute * The immediate parents, if any, of the type object in question The hash describing an individual attribute must have the following keys: * name - provides the name of the attribute It may optionally have these keys: * type - type object specifying the type of the attribute. Note that this is used purely for the purpose of allocation. It is NOT up to the REPR to do type checks. Anything without this is assumed to be a reference type. In fact, anything that's a reference type is uninteresting in terms of this key since it just gets a reference to another object. It's native types or other compact things that can be flattened into the object body that are interesting. * box_target - if this key is present, then this type is a target for boxing or unboxing. * auto_viv_container - if this key is present then an access to an attribute that is uninitialized will cause that attribute to be set to a clone of the value under this key. If the value is a type object, the clone will not take place, since cloning a type object is meaningless. * positional_delegate - if this key is present, the given (reference type) attribute will be delegated to if the positional part of the REPR API is used with the object. This is primarily to support languages that need to reasonably efficiently provide positional things that can also be mixed in to in arbitrary ways. * associative_delegate - the same as positional_delegate, for the associative part of the REPR API. ## integer A hash that may have the following keys: * bits - the number of bits. Which sizes are supported are up to the REPR. * unsigned - if this key exists, then the stored value is unsigned. ## float A hash that may have the following keys: * bits - the number of bits. Which sizes are supported are up to the REPR. ## array A hash that may have the following keys: * type - the type of the array attributes. While all reference types are equivalent, native types will be inlined, leading to a compact array. A REPR is free to decide what types it supports, and just because it is asked to be an array of 1-bit ints in no way obligates it to use a bit of storage for each element. Again, this is just about layout and lookup, not about type checking. * dimensions - the number of dimensions an array of this type supports. A given REPR may be restricted to only support a single dimensions. ## hash A hash that may have the following keys: * valuetype - the type of the hash value. As with arrays, it's up to the REPR how or if it handles native types. Just about layout. * keytype - the type of the hash key. REPRs are likely to be restrictive here, since they need to understand the representation of the provided key well enough to hash it. This will probably mean strings and whatever REPR ObjAt in Perl 6 (or some other language's variant) has. ## nativeref A hash that must have the following two keys: * type - the native type that is being referenced. Must have a storage spec that claims to be a native int/num/str. * refkind - the kind of reference, specified as a string. Must be one of 'attribute', 'positional', or 'lexical'. nqp-2018.03/docs/HACKING-js0000644000175000017500000000456213253717146013502 0ustar alexalex# Hacking tips for the JS backend of NQP # Source layout. src/vm/js/Compiler.nqp - turns QAST into JavaScript code src/vm/js/nqp-runtime - a node module the code we generate uses src/vm/js/bin/run_tests.pl - a list of tests we use # Overview The bulk of the compilation is done by QAST::CompilerJS.as_js($node, :$want). It takes a QAST::Node and produces a Chunk. A chunk is created by Chunk.new($type, $expr, @setup, :$node). $expr represents a sideffect free JavaScript expression. $type describes what the expression evaluates to. $type can be one of $T_OBJ, $T_INT, $T_STR, $T_BOOL, $T_VOID, $T_NONVAL. @setup is the JavaScript code that needs to be run for $expr to be valid. @setup is an array containing either string literals with JavaScript code or other Chunks. $node is a QAST::Node we get line positions from when emitting source maps. Simple chunk examples: Chunk.new($T_STR, "'Hello World'", []); my $foo := Chunk.new($T_STR, "foo", ["var foo = 'Hello World';\n"]); Chunk.new($T_VOID, "", [$foo, "alert({$foo.expr});\n"]); # We don't ever use the $expr of $T_VOID chunks When compiling a QAST::Block we need to keep a bunch of information about it. We store it in a dynamic $*BLOCK variable (which contains a BlockInfo). my $tmp := $*BLOCK.add_tmp(); # that creates a temporary variable in js function we are emitting. my $foo := Chunk.new($T_STR, $tmp, ["$tmp = 'Hello World';\n"]); # NQP op codes QAST::OperationsJS.compile_op(QAST::CompilerJS $compiler, QAST::Node $node, :$want) handles the compilation of QAST::Op nodes The more complex ops are defined using add_op. add_op($op, sub ($comp, $node, :$want) { # this should return a chunk }); The simpler ones are defined using add_simple_op($op, $return_type, @argument_types, $cb, :$sideffects). $cb gets a bunch of .expr parts from the Chunks the op arguments are compiled to. $cb should return a string containing a JavaScript expression. If the op has sideffects the :$sideffects flag needs to be true. Example: add_simple_op('lc', $T_STR, [$T_STR], sub ($string) {"$string.toLowerCase()"}); Looking at src/vm/jvm/runtime/org/perl6/nqp/runtime/Ops.java and src/vm/jvm/QAST/Compiler.nqp can provide insight on how the various ops should work. # Reading generated code ./nqp-js --target=js --beautify -e 'say("Hello World")' # t/js JS backend specific tests should placed here nqp-2018.03/docs/bootstrapping.pod0000644000175000017500000000173313253717146015475 0ustar alexalex=head1 Bootstrapping procedure for NQP NPQ is a bootstrapped compiler, which means that it uses itself to compile itself. To make the first compilation possible under MoarVM, a compiled version of the compiler is stored in F and included in the source code repository. When you make changes to the compiler, eventually you need to update these I files. Here is how you proceed to update the MoarVM bootstrapping files in NQP. At any stage, if C fails, don't go ahead with the following steps, but fix the problem first. (Note: the equivalent under JVM is F with the update target C) =over =item * Make your changes, run C =item * Run C and C =item * Commit the non-bootstrap files that you modified yourself =item * Commit the bootstrap files =back Historic References: L nqp-2018.03/docs/built-ins.md0000644000175000017500000000647113253717146014332 0ustar alexalex# NQP Built-in Subs List The following subroutines are available to use in `nqp` programs. They are described in `nqp/src/core` modules. # From `nqp/src/core/Regex.nqp` ## match * `match($text, $regex, :$global? --> @array)` Match `$text` against `$regex`. If the `$global` flag is given, then return an array of all non-overlapping matches. ## subst * `subst($text, $regex, $replacement, :$global? --> str)` Substitute a match of `$regex` in `$text` with `$replacement`, returning the substituted string. If `$global` is given, then perform the replacement on all matches of `$text`. # From `nqp/src/core/IO.nqp` ## open * `open($filename, :$r, :$w, :$a, :$bin, :$enc, :$chomp --> $filehandle)` Open file `$filename`. Options: + :w - open for writing + :r - open for reading (default) + :a - open for appending + :bin - open in binary mode + :enc - define encoding (default: `utf8`) + :chomp - strip ending newlines (default: true) ## close * `close($fh)` Close the file attached to file handle `$fh`. ## slurp * `slurp($filename --> str)` Returns the contents of `$filename` as a single string. ## spurt * `spurt($filename, $contents)` Write the string value of `$contents` to `$filename`. ## say * `say($string)` Write `$string` to `stdout` with a newline added. ## note * `note($string)` Write `$string` to `stderr` with a newline added. ## join * `join($delim, @array --> str)` Returns a string formed by joining each element of `@array` with the `$delim`. ## print * `print($string)` Write `$string` to `stdout`. ## stdin * `stdin(--> $filehandle)` Returns a file handle to `stdin`. ## stdout * `stdout(--> $filehandle)` Returns a file handle to `stdout`. ## stderr * `stderr(--> $filehandle)` Returns a file handle to `stderr`. # File handle (fh) methods Some methods available on the file handle (fh) returned from `open`. Other methods available of lesser interest not documented below are: + flush + seek + set-encoding + set-nl-in + slurp + t + tell + wrap ## fh.get * `$fh.get()` Reads a line from the file attached to file handle `$fh`. ## fh.print * `$fh.print($string)` Write `$string` to the file attached to file handle `$fh`. An ending newline is not added. ## fh.say * `$fh.say($string)` Write `$string` to the file attached to file handle `$fh`. An ending newline is added. ## fh.close * `$fh.close()` Close the file attached to file handle `$fh`. ## fh.readchars * `$fh.readchars($nchars)` Read `$nchars` characters from file handle `$fh`. ## fh.eof * `$fh.eof` Returns true if end-of-file has been reached on file handle `$fh`. # From `nqp/src/core/testing.nqp` ## plan * `plan($quantity --> str)` ## ok * `ok($condition, $descrip?)` ## is * `is($got, $expected, $desc?)` ## todo * `todo($reason, $count)` ## skip * `skip($desc, $count=1)` ## bug-workaround * `bug-workaround($code)` If `$code` dies, returns the error message. ## dies-ok * `dies-ok($code, $description, :$message)` ## run-command * `run-command($command, :$stdout, :$stderr --> @array)` Returns an array of two elements, one for each stream, with element 1 being output from `stdout` (file descriptor 1) and 2 being output from `stderr` (fd 2). Both `stdout` and `stderr` output are always captured, but only the desired stream(s) is(are) returned in the array. The element for a stream will be the empty string if it wasn't requested. nqp-2018.03/docs/continuations.pod0000644000175000017500000001532613253717146015502 0ustar alexalex=head1 Continuations in NQP This document describes the ability to freeze and thaw stack frames for implementing unusual control flow. =head2 Interface I think that the best abstraction to what I'm trying to build here is the B (L). =over 4 =item nqp::continuationreset($tag, { ... }) Executes the argument, marking the stack for a subsequent C operation within the dynamic scope. The C<$tag> is an object which can be used later by C to find a specific reset frame. =item nqp::continuationcontrol($protect, $tag, -> $cont { ... }) Slices off the part of the evaluation stack between the current call frame and the innermost enclosing C. If C<$tag> is not null, only resets with the same tag are considered; otherwise the innermost reset will be taken regardless of tag. If there is no such reset, or if there is a non-saveable frame (aka continuation barrier) between the current position and the matching reset, an error occurs. The sliced-off part of the stack is wrapped in an NQP object and passed to the callback function; it is removed from the stack, so B to return immediately with the returned value>. C<$protect> is an integer. If it is 1, the reset will be retained on the stack while the handler is being executed; if it is 0, the reset will be removed. Other values are undefined. In no case will the matched reset be included in the captured continuation object (although an unmatched reset which is skipped over would be). Thus, C corresponds to the standard C operator, while C corresponds to the standard C operator. To simulate C and C, manually add the reset before invoking the continuation. =item nqp::continuationinvoke($cont, $inject) Pastes the saved call frames onto the current stack, such that C calls C<$inject> within the restored dynamic scope and returns its return value. Control returns to the caller when the callback to C returns, with the same value. This can be used multiple times on the same continuation. (Actually, delimited continuations are traditionally represented as functions, so this operator is implicit and unnamed. But sixmodel makes that slightly tricky.) =item nqp::continuationclone($cont) Produces a shallow clone of the passed continuation. This is presently necessary in situations where a continuation must be active twice at the same time. At present, lexical variables will remain shared but locals will not. B
# should be 3 * 3 * 10 = 90 # will infinite loop if the clones are removed my $cont := nqp::continuationreset(nqp::null(), { 3 * nqp::continuationcontrol(0, nqp::null(), -> $k { $k }); }); my $val := nqp::continuationinvoke(nqp::continuationclone($cont), { nqp::continuationinvoke(nqp::continuationclone($cont), { 10 }) }); =back By way of example, here is Scheme's call/cc implemented using NQP delimited continuations: # for proper R5RS semantics, run this once wrapping your main function sub run_main($f) { nqp::continuationreset(nqp::null(), $f); } sub callcc($f) { # first get the current continuation nqp::continuationcontrol(1, nqp::null(), -> $dcont { my $scheme_cont := -> $val { # when the scheme continuation is invoked, we need to *replace* # the current continuation with this one nqp::continuationcontrol(1, nqp::null(), -> $c { nqp::continuationinvoke($dcont, { $val }) }); }; nqp::continuationinvoke($dcont, { $f($scheme_cont) }); }); } And here is something resembling gather/take: my $SENTINEL := []; sub yield($value) { nqp::continuationcontrol(0, nqp::null(), -> $dcont { [$value, { nqp::continuationinvoke($dcont, {0}) }] }); } sub start_iter($body) { my $state := { $body(); yield($SENTINEL) }; -> { my $pkt := nqp::continuationreset(nqp::null(), $state); $state := $pkt[1]; $pkt[0]; } } Complete examples may be found in t/jvm/01-continuations.t in the source distribution. =head1 Conjectures =head2 Lazy recursive reinstate optimization Consider the following (Perl 6): my $N = 10000; sub flatten($x) { multi go(@k) { go($_) for @k } multi go($k) { take $k } gather go($x); } my $list = [^$N]; $list = [$list] for ^$N; say flatten($list).perl; This takes O(N^2) time on the current implementation. Why? Because each time take is invoked, we are N frames deep, so each take does O(N) work, and there are N calls to take. We can improve this to O(N) by doing the continuation operations B. That is, when reinstating a continuation only reinstate the top frame(s) that will be executed, and skip the work of reinstating the non-top frames only to resave them later. The design of this is a bit handwavey at the moment. =head2 Multiple callers There are two sensible ways to define the caller of a call frame. Either the frame which caused this frame to exist (henceforth, the static caller) or the frame which caused this frame to be active (henceforth, the dynamic caller). They are the same for most frames, but differ in the case of the top frame of a gather. The static caller of such a frame is the frame containing C; the dynamic caller is the frame corresponding to C. We need both: contextuals use the static caller (TimToady has said so quite explicitly), while exceptions and control flow ought to use the dynamic caller (people expect lazy exceptions to show up and backtrace at the point where the list is used). So we might need to B. Niecza does precisely this, and I think parrot is doing something similar. =head2 Saner cloning C is bad because it exposes details of what the JVM implementation can do without warning and what requires warnings. It would be better to expliclty declare exactly what you intend to do with the continuation as a bit flag passed to control and/or invoke, and let the op set figure out itself when cloning is needed. Flags could include "I don't intend to call this at all" (control used as an escape operator), "I intend to call this exactly once" (coroutines), "I may use this more than once, but only on one thread", "I want to use this on several threads". nqp-2018.03/docs/jvminterop-goals.pod0000644000175000017500000001500313253717146016075 0ustar alexalex=encoding utf8 =head1 Introduction This infodump describes aspects of the most desirable interface for Java interop functionality in Rakudo, based on experience with CLR interop functionality in Niecza. No aspects of implementation I are discussed. =head1 Unsolved questions Mainly, "should wrapper types be invariant or covariant?". A wrapper of type C is not necessarily substitutable for a wrapper of type C, because the subclass may have added methods which cause an ambiguous overload (in the CLR we have the additional issue of shadowing, but that doesn't apply here). Invariant wrappers (all wrappers are assignable to C and C) are the safer option, but covariant wrappers (reflecting the Java type DAG) would be more convenient in some cases. I did covariant wrappers for Niecza and was not quite happy with the associated traps; I'd like to see if invariant wrappers can be made to work. =head1 General object features We'd probably like fields to be reflected as rw methods, returning C objects that handle get and set. (This works well in Niecza). Methods, including C aka C<< >>, should be bundled by short name and then turned into overload-resolving sets. This can be done either by creating a composite method that implements Java overload rules, or by building a Perl 6 multi-dispatcher that wraps a set of methods. Using a multi-dispatcher would probably require covariant wrappers. Java objects should be made to implement core Perl 6 roles whenever reasonable. Cs and single-method interfaces (including the new single-method interfaces being added to Java 1.8 for use with lambdas) should function as Callable with a C<< postcircumfix:<( )> >>. Arrays and objects assignable to C should present a C interface. Objects assignable to C should present an C interface. C things should probably be iterable, somehow. It may additionally be useful to provide semantics for C and C, but that's I overkill. It shall be possible to throw and catch Java exceptions as Perl 6 exceptions. (The current implementation is limited such that you can throw OR catch Java exceptions - a thrown Java exception is treated as a direct control operator to the innermost callback, and cannot be caught by Perl 6 exception handlers until it propagates to a callout point.) Since a C is effectively the root of a class namespace, it shall be possible to obtain a PsuedoStash-like object for a class loader which can be used to fetch type objects representing the classes defined in it. Each such PsuedoStash holds a class loader and a package prefix; fetches attempt a lookup and return a concatenated stash, along with either a type or some kind of C thing. =head1 Marshal-in (boxing) rules Most objects should box as Cs. In order for the interface to be most generally usable, it must be possible to get the exact same Java object out of the box - boxings that lose object identity are probably unacceptable. Primitive types have no such restriction and should be translated into Perl 6 types freely. C already translates all primitives that correspond to 6model types in the obvious way, additionally C is returned as a one-character C and C is mapped to an C of 0 or 1. The former is probably correct for Perl 6, but the latter should be overridden to be C or C. It is acceptable to map reference types in cases where the Perl 6 object wraps the corresponding type, so it can be recovered identically from the box. This is the case for C / C and C / C, and of course C. =head1 Marshal-out (unboxing / coercion) rules In all cases it is acceptable to pass a JavaObject wrapper to code which expects a reference type. The type of the wrapper is ignored, except possibly for overload resolution. Primitive types and naturally-boxed types, including C, are marshaled out in the obvious way. It may be useful to provide special handling for functions which take an argument of type C or C; note that C specially allows passing C in such cases with an interpretation of "current". Alternatively mechanisms can be provided in the library for retrieving the current context objects. Wherever an Iterator or Iterable is needed, it shall be acceptable to pass an iterable Perl 6 object; a wrapper Iterable shall be constructed which iterates the underlying Perl 6 object. Wherever a List is needed, it shall be acceptable to pass any Perl 6 object that does Positional. Note that implementing the List interface faithfully requires C and C in addition to C, while C and C are not used. Wherever a Map is needed, it shall be acceptable to pass any Perl 6 object that does Associative. Wherever any non-collection interface type is needed, it shall be acceptable to pass a hash mapping string method names to code references, which results in the creation of a proxy class (as per C). If the interface has a single method, passing a single code reference is acceptable. Collection interfaces must be proxied explicitly, to avoid ambiguity with the (presumably more common) case where the programmer wishes to treat a hash as a collection itself. =head1 Access It is inevitable that some general utilities not tied to a specific class will be needed. A setting module (C) is probably the sanest way to approach this. Access to the class hierarchy is bootstrapped through a special symbol in the setting which is bound to a psuedo stash representing the combination of the NQP class loader (the class loader through which C, C, and (through delegation) C are visible) and the empty package prefix. Since the NQP class loader itself cannot and should not be serialized, it will probably be necessary to represent this in a special way, for instance by allowing a null class loader to represent the NQP class loader. (Note that this is inconsistent with the usual behavior of Java core methods to treat a null class loader as a reference to the bootstrap class loader. That might be a problem.) C can be wired up to bind class and package symbols into the current scope. Additionally we can support adverbs which trigger dynamic loading of jars. nqp-2018.03/docs/jvminterop.pod0000644000175000017500000000543113253717146014776 0ustar alexalex=head1 Basic principles We have some degree of interop with native Java objects provided through the C class. The name Boot is intended to suggest that it will not be used directly in most cases; instead it can either be subclassed to provide useful (and fast) HLL-specific marshaling, or it can be wrapped with NQP code to achieve a similar effect. =head1 Common data model All Java objects are wrapped in instances of the C REPR. The instances share a common methodless class by default, but a subclass of C can define an alternate class, or even dynamically create one class per Java type. Marshalling and unmarshalling is done automatically on calls in a statically type-directed way: a function which declares a return type of C will appear to return C, but a function with a declared type of C will wrap any return value even if it is a string. When dynamically creating classes, return values are wrapped in a wrapper type appropriate to their B type. To use methods that exist only on a specific run-time subclass, a cast must be used. This is a deliberate difference from the Niecza CLR interop system, intended to avoid several related semantic traps that exist in Niecza. For instance: $obj.method1.method2; If C is declared to return an object of type C with a single method C, and actually does so, this works in both NQP and Niecza. But if C is changed to return an object of type C which is a subclass of C but has additional methods that shadow or ambiguously overload C, it will B work in Niecza because the binding is effectively too late to reflect host-language subclass substitutability; but it will work on NQP, because the binding is forced to be done based on the static type. When handling these "pseudostatic types", no attempt is made to reflect Java's generic type system. That would just be crazy. =head1 Direct use my $interop := nqp::jvmbootinterop(); $interop.typeForName('java.lang.Thread').unbox( $interop.implementClass( [ ['extends','java.lang.Thread'], ['instance_method', 'run', '()V', method () { say("Hello thread") }] ], ).newInstance ).start; $interop.typeForName('java.lang.Thread')."constructor/new/(Ljava/lang/Runnable;Ljava/lang/String;)V"( $interop.proxy('java.lang.Runnable', nqp::hash( 'run', sub () { say("hello thread2") } )), "AnotherThread" ).start; =head1 Subclassing The C class has a large number of protected methods which can be overriden to support things like automatic creation of Cs to permit method calls against Java objects and types, or adding marshalling rules like C <-> C. =head1 Wrapping TBD nqp-2018.03/docs/nqp-overview.txt0000644000175000017500000000165213253717146015301 0ustar alexalex Overview of nqp repository directory structure: 3rdparty/ - 3rd-party Java libraries asm/ - bytecode generation jna/ - Java Native Access jline/ - console input handling docs/ - documentation for NQP examples/ - NQP examples (some obsolete) src/ core - NQP core runtime types HLL - base classes for building HLL compilers how - the NQP metamodel NQP - NQP compiler sources QAST - code for QAST types, QAST to VM compiler QRegex - P6Regex to QAST compiler vm - backend-specific code (code-gen, runtime libraries) t/ - tests hll/ - HLL library tests (?) nqp/ - NQP language tests qregex/ - QRegex tests qast/ - QAST tests p5regex/ - P5 regex tests serialization/ - NQP serializer tests tools/ - scripts and Perl 5 libraries for configure+build NQP nqp-2018.03/docs/ops.markdown0000755000175000017500000023253513253717146014454 0ustar alexalex List ## Table of Contents - [NQP Opcodes](#nqp-opcodes) - [Arithmetic Opcodes](#-arithmetic-opcodes) * [abs](#abs) * [add](#add) * [div](#div) * [gcd](#gcd) * [lcm](#lcm) * [mod](#mod) * [mul](#mul) * [neg](#neg) * [sub](#sub) - [Numeric Opcodes](#-numeric-opcodes) * [base](#base) * [ceil](#ceil) * [exp](#exp) * [floor](#floor) * [inf](#inf) * [log_n](#log_n) * [expmod](#expmod) * [nan](#nan) * [neginf](#neginf) * [pow](#pow) * [rand](#rand) * [srand](#srand) * [sqrt](#sqrt) - [Trigonometric Opcodes](#-trigonometric-opcodes) * [asec](#asec) * [asin](#asin) * [acos](#acos) * [atan](#atan) * [atan2](#atan2) * [cos](#cos) * [cosh](#cosh) * [sin](#sin) * [sinh](#sinh) * [sec](#sec) * [sech](#sech) * [tan](#tan) * [tanh](#tanh) - [Relational / Logic Opcodes](#-relational--logic-opcodes) * [cmp](#cmp) * [eqat](#eqat) * [eqatic `moar`](#eqatic-moar) * [eqatim `moar`](#eqatim-moar) * [eqaticim `moar`](#eqaticim-moar) * [iseq](#iseq) * [isgt](#isgt) * [isge](#isge) * [islt](#islt) * [isle](#isle) * [isne](#isne) * [not_i](#not_i) - [Array Opcodes](#-array-opcodes) * [atpos](#atpos) * [bindpos](#bindpos) * [atposref](#atposref) * [elems](#elems) * [existspos](#existspos) * [list](#list) * [push](#push) * [pop](#pop) * [setelems](#setelems) * [shift](#shift) * [splice](#splice) * [unshift](#unshift) * [iterator](#iterator) - [Hash Opcodes](#-hash-opcodes) * [atkey](#atkey) * [bindkey](#bindkey) * [existskey](#existskey) * [deletekey](#deletekey) * [iterkey](#iterkey) * [iterval](#iterval) - [String Opcodes](#-string-opcodes) * [chars](#chars) * [chr](#chr) * [codepointfromname](#codepointfromname) * [getstrfromname](#getstrfromname) * [concat](#concat) * [decode](#decode) * [decodetocodes `moar`](#decodetocodes-moar) * [encode](#encode) * [encodefromcodes `moar`](#encodefromcodes-moar) * [encodenorm](#encodenorm) * [escape](#escape) * [fc](#fc) * [findcclass](#findcclass) * [findnotcclass](#findnotcclass) * [flip](#flip) * [hash](#hash) * [index](#index) * [indexic](#indexic) * [indexim `moar`](#indexim-moar) * [indexicim `moar`](#indexicim-moar) * [iscclass](#iscclass) * [join](#join) * [lc](#lc) * [normalizecodes](#normalizecodes) * [ord](#ord) * [ordbaseat](#ordbaseat) * [radix](#radix) * [replace](#replace) * [rindex](#rindex) * [rindexfromend `jvm`](#rindexfromend-jvm) * [split](#split) * [sprintf](#sprintf) * [sprintfdirectives](#sprintfdirectives) * [sprintfaddargumenthandler](#sprintfaddargumenthandler) * [strfromcodes](#strfromcodes) * [strtocodes](#strtocodes) * [substr](#substr) * [substr2 `jvm`](#substr2-jvm) * [substr3 `jvm`](#substr3-jvm) * [tc](#tc) * [uc](#uc) * [unicmp_s](#unicmp_s) - [Parameters:](#parameters) * [x](#x) - [Unicode Property Opcodes](#-unicode-property-opcodes) * [getuniname](#getuniname) * [getuniprop_int `moar`](#getuniprop_int-moar) * [getuniprop_str](#getuniprop_str) * [getuniprop_bool `moar`](#getuniprop_bool-moar) * [matchuniprop `moar`](#matchuniprop-moar) * [unipropcode](#unipropcode) * [unipvalcode `moar`](#unipvalcode-moar) * [hasuniprop `moar`](#hasuniprop-moar) - [VM-Provided Streaming Decoder Opcodes](#-vm-provided-streaming-decoder-opcodes) * [decoderconfigure](#decoderconfigure) * [decodersetlineseps](#decodersetlineseps) * [decoderaddbytes](#decoderaddbytes) * [decodertakechars](#decodertakechars) * [decodertakeallchars](#decodertakeallchars) * [decodertakeavailablechars](#decodertakeavailablechars) * [decodertakeline](#decodertakeline) * [decoderbytesavailable](#decoderbytesavailable) * [decodertakebytes](#decodertakebytes) * [decoderempty](#decoderempty) - [Conditional Opcodes](#-conditional-opcodes) * [if](#if) * [unless](#unless) - [Loop/Control Opcodes](#-loopcontrol-opcodes) * [control](#control) * [defor](#defor) * [for](#for) * [ifnull](#ifnull) * [repeat_until](#repeat_until) * [repeat_while](#repeat_while) * [stmts](#stmts) * [until](#until) * [while](#while) - [Exceptional Opcodes](#-exceptional-opcodes) * [backtrace](#backtrace) * [backtracestrings](#backtracestrings) * [die](#die) * [exception](#exception) * [getextype](#getextype) * [getmessage](#getmessage) * [getpayload](#getpayload) * [newexception](#newexception) * [resume](#resume) * [rethrow](#rethrow) * [setextype](#setextype) * [setmessage](#setmessage) * [setpayload](#setpayload) * [throw](#throw) - [Input/Output Opcodes](#-inputoutput-opcodes) * [closefh](#closefh) * [eoffh](#eoffh) * [filenofh](#filenofh) * [flushfh](#flushfh) * [getstderr](#getstderr) * [getstdin](#getstdin) * [getstdout](#getstdout) * [open](#open) * [openasync `jvm`](#openasync-jvm) * [print](#print) * [readfh](#readfh) * [say](#say) * [seekfh](#seekfh) * [tellfh](#tellfh) * [writefh](#writefh) - [File / Directory / Network Opcodes](#-file--directory--network-opcodes) * [chdir](#chdir) * [chmod](#chmod) * [closedir](#closedir) * [copy](#copy) * [cwd](#cwd) * [fileexecutable](#fileexecutable) * [fileislink](#fileislink) * [filereadable](#filereadable) * [filewritable](#filewritable) * [link](#link) * [mkdir](#mkdir) * [nextfiledir](#nextfiledir) * [opendir](#opendir) * [rename](#rename) * [rmdir](#rmdir) * [stat](#stat) * [stat_time](#stat_time) * [lstat](#lstat) * [lstat_time](#lstat_time) * [symlink](#symlink) * [unlink](#unlink) - [Type/Conversion Opcodes](#-typeconversion-opcodes) * [bool](#bool) * [bootarray `jvm` `moar`](#bootarray-jvm-moar) * [boothash `jvm` `moar`](#boothash-jvm-moar) * [bootint `jvm` `moar`](#bootint-jvm-moar) * [bootintarray `jvm` `moar`](#bootintarray-jvm-moar) * [bootnum `jvm` `moar`](#bootnum-jvm-moar) * [bootnumarray `jvm` `moar`](#bootnumarray-jvm-moar) * [bootstr `jvm` `moar`](#bootstr-jvm-moar) * [bootstrarray `jvm` `moar`](#bootstrarray-jvm-moar) * [box](#box) * [decont](#decont) * [defined](#defined) * [fromnum](#fromnum) * [fromstr](#fromstr) * [isbig](#isbig) * [isconcrete](#isconcrete) * [iscont](#iscont) * [isfalse](#isfalse) * [ishash](#ishash) * [isint](#isint) * [isinvokable](#isinvokable) * [islist](#islist) * [isnanorinf](#isnanorinf) * [isnull](#isnull) * [isnum](#isnum) * [isprime](#isprime) * [isstr](#isstr) * [istrue](#istrue) * [istype](#istype) * [null](#null) * [jvmisnull `jvm`](#jvmisnull-jvm) * [tostr](#tostr) * [tonum](#tonum) * [unbox](#unbox) - [OO/SixModel Opcodes](#-oosixmodel-opcodes) * [attrinited](#attrinited) * [bindattr](#bindattr) * [bindcomp](#bindcomp) * [callmethod](#callmethod) * [can](#can) * [clone](#clone) * [create](#create) * [eqaddr](#eqaddr) * [findmethod](#findmethod) * [getattr](#getattr) * [getcomp](#getcomp) * [how](#how) * [rebless](#rebless) * [reprname](#reprname) * [setwho](#setwho) * [who](#who) * [what](#what) * [where](#where) - [Bit Opcodes](#-bit-opcodes) * [bitand](#bitand) * [bitneg](#bitneg) * [bitor](#bitor) * [bitshiftl](#bitshiftl) * [bitshiftr](#bitshiftr) * [bitxor](#bitxor) - [Context Introspection Opcodes](#-context-introspection-opcodes) * [ctx](#ctx) * [ctxcaller](#ctxcaller) * [ctxlexpad](#ctxlexpad) * [curlexpad](#curlexpad) * [ctxouter](#ctxouter) * [lexprimspec](#lexprimspec) * [savecapture](#savecapture) * [usecapture](#usecapture) * [getlex](#getlex) * [getlexref](#getlexref) * [bindlex](#bindlex) * [getlexdyn](#getlexdyn) * [bindlexdyn](#bindlexdyn) * [getlexouter](#getlexouter) * [getlexcaller](#getlexcaller) * [getlexrel](#getlexrel) * [getlexreldyn](#getlexreldyn) * [getlexrelcaller](#getlexrelcaller) - [Variable Opcodes](#-variable-opcodes) * [bind](#bind) - [Miscellaneous Opcodes](#-miscellaneous-opcodes) * [locallifetime](#locallifetime) * [const](#const) * [debugnoop `jvm`](#debugnoop-jvm) * [exit](#exit) * [getenvhash](#getenvhash) * [backendconfig](#backendconfig) * [getpid](#getpid) * [getppid `moar`](#getppid) * [jvmclasspaths `jvm`](#jvmclasspaths-jvm) * [sha1](#sha1) * [sleep](#sleep) * [takeclosure](#takeclosure) * [time](#time) - [Native Call / Interoperability Opcodes](#-native-call--interoperability-opcodes) * [nativecallrefresh](#nativecallrefresh) - [Asynchronous Operations](#-asynchronous-operations) * [permit](#permit) * [cancel](#cancel) * [timer](#timer) * [signal](#signal) * [watchfile](#watchfile) * [asyncconnect](#asyncconnect) * [asynclisten](#asynclisten) * [asyncwritestr](#asyncwritestr) * [asyncwritebytes](#asyncwritebytes) * [asyncreadchars](#asyncreadchars) * [asyncreadbytes](#asyncreadbytes) * [spawnprocasync `moar`](#spawnprocasync-moar) * [killprocasync `moar`](#killprocasync-moar) - [Atomic Operations](#-atomic-operations) * [cas `moar`](#cas-moar) * [cas_i `moar`](#cas_i-moar) * [atomicinc_i `moar`](#atomicinc_i-moar) * [atomicdec_i `moar`](#atomicdec_i-moar) * [atomicadd_i `moar`](#atomicadd_i-moar) * [atomicload `moar`](#atomicload-moar) * [atomicload_i `moar`](#atomicload_i-moar) * [atomicstore `moar`](#atomicstore-moar) * [atomicstore_i `moar`](#atomicstore_i-moar) * [barrierfull `moar`](#barrierfull-moar) # NQP Opcodes Opcodes (ops) are used both directly when writing NQP, and during code generation in QAST nodes. When invoking them directly, you'll need to prefix them with nqp::, e.g. nqp::mul_i(6,9); The ops are listed below by type. Each entry shows the name of the op, its variants, and their arguments and types, and may provide a short description. Some opcodes differ only by argument types - in that case, they are listed under their common name (e.g. `mul`), with each of their variants (e.g. `mul_i`, `mul_n`) together with a single description. Opcode variants may contain a type suffix, which usually indicates: * `_i` argument is a native int * `_u` argument is an unsigned int * `_n` argument is a native float * `_s` argument is a native string * `_b` argument is a code block * `_I` argument is a Big Integer They may also have a numeric suffix, which typically indicates the number of arguments required. In opcode signatures below, we use the following types, which may not correspond directly to NQP types. * int - native int * num - native float * str - native string * Int - BigInt * Mu - any NQP or VM object * Mu:T - a type object, e.g. `Int` * Exception - an Exception object * Handle - an I/O Handle object * Iterable - something iterable * Context - a Context object * LexPad - a Context object * @ - this sigil indicates an array parameter * % - this sigil indicates a hash parameter * ... - indicates variable args are accepted VM-specific opcodes are denoted with a `jvm`, e.g. on the same line as the header. No annotation indicates this opcode should be supported on all nqp backends. Some individual opcodes may be marked with _Internal_ or _Deprecated_. Both of these indicate the opcodes are not intended to be used. Deprecated opcodes will eventually be removed from NQP. Internal opcodes are typically used at compile time to replace opcodes that take a variable number of arguments. The opcodes are grouped into the following categories: * [Arithmetic Opcodes](#arithmetic) * [Numeric Opcodes](#numeric) * [Trigonometric Opcodes](#trig) * [Relational / Logic Opcodes](#logic) * [Array Opcodes](#array) * [Hash Opcodes](#hash) * [String Opcodes](#string) * [Unicode Property Opcodes](#unicode) * [Conditional Opcodes](#conditional) * [Loop/Control Opcodes](#control) * [Exceptional Opcodes](#exceptions) * [Input/Output Opcodes](#io) * [External command Opcodes](#extern) * [File / Directory / Network Opcodes](#filedirnet) * [Type/Conversion Opcodes](#type) * [OO/SixModel Opcodes](#sixmodel) * [Bit Opcodes](#bit) * [Context Introspection Opcodes](#context) * [Variable Opcodes](#variable) * [Miscellaneous Opcodes](#misc) * [Native Call / Interoperability Opcodes](#nativecall) * [Asynchronous operations](#async) * [Atomic operations](#atomic) # Arithmetic Opcodes ## abs * `abs_i(int $i --> int)` * `abs_n(num $n --> num)` * `abs_I(Int $i, Mu:T $type --> Int)` Return the absolute value of a number. `_I` variant returns an object of the given type. ## add * `add_i(int $l, int $r --> int)` * `add_n(num $l, num $r --> num)` * `add_I(Int $l, Int $r, Mu:T $type --> Int)` Add two numbers together, returning the result. `_I` variant returns an object of the given type. ## div * `div_i(int $l, int $r --> int)` * `div_n(num $l, num $r --> num)` * `div_I(Int $l, Int $r, Mu:T $type --> Int)` * `div_In(Int $l, Int $r --> num)` Divide $l by $r, returning the result. `_I` variant returns an object of the given type. The `_In` variant returns a native num, using a scale of 309, and a rounding mode equivalent to Java's `ROUND_HALF_UP`. ## gcd * `gcd_i(int $l, int $r --> int)` * `gcd_I(Int $l, Int $r, Mu:T $type --> Int)` Return the greatest common multiple of two numbers. `_I` variant returns an object of the given type. ## lcm * `lcm_i(int $l, int $r --> int)` * `lcm_I(Int $l, Int $r, Mu:T $type --> Int)` Return the lowest common multiple of two numbers. `_I` variant returns an object of the given type. ## mod * `mod_i(int $l, int $r --> int)` * `mod_n(num $l, num $r --> num)` * `mod_I(Int $l, Int $r, Mu:T $type --> Int)` Return the modulus of $l by $r. `_I` variant returns an object of the given type. ## mul * `mul_i(int $l, int $r --> int)` * `mul_n(num $l, num $r --> num)` * `mul_I(Int $l, Int $r, Mu:T $type --> Int)` Multiply two numbers, returning the result. `_I` variant returns an object of the given type. ## neg * `neg_i(int $i --> int)` * `neg_n(num $n --> num)` * `neg_I(Int $i, Mu:T $type --> Int)` Return the negative of a number. `_I` variant returns an object of the given type. ## sub * `sub_i(int $l, int $r --> int)` * `sub_n(num $l, num $r --> num)` * `sub_I(Int $l, Int $r, Mu:T $type --> Int)` Subtract $r from $l, returning the result. `_I` variant returns an object of the given type. # Numeric Opcodes ## base * `base_I(Int $i, int $radix --> str)` Returns a string representing the integer `$i` in base `$radix` ## ceil * `ceil_n(num $n --> num)` Return the ceiling of a number. ## exp * `exp_n(num $exponent --> num)` Return the value of `e` raised to $exponent. ## floor * `floor_n(num $n --> num)` Return the floor of a number. ## inf * `inf(--> num)` Return infinity. ## log_n * `log_n(num $n --> num)` Return the natural logarithm (base 𝑒) of a number. ## expmod * `expmod_I(Int $base, Int $exponent, Int $modulus, Mu:T $type --> Int)` Return a bigint that is `$base` raised to `$exponent` modulus `$modulus`. `_I` variant returns an object of the given type. ## nan * `nan(--> num)` Return NaN. ## neginf * `neginf(--> num)` Return negative infinity. ## pow * `pow_n(num $base, num $exponent --> num)` * `pow_I(Int $base, Int $exponent, Mu:T $type_num, Mu:T $type_bigint --> Int)` Return the value of $base raised to $exponent; `_I` variant returns an object of `$type_num` for negative exponents, and of type `$type_bigint` for positive exponents. ## rand * `rand_n(num $n --> num)` * `rand_I(Int $i, Mu:T $type --> Int)` Returns a psuedo-random bigint up to the value of the given number. `_I` variant returns an object of the given type. ## srand * `srand(int $n)` Sets and returns seed number for `nqp::rand_*` variants. Decimal numbers will be silently truncated, `nqp::srand(1)` and `nqp::srand(1.1)` are the same so always pass `nqp::srand` an integer. ## sqrt * `sqrt_n(num $n--> num)` # Trigonometric Opcodes Each opcode corresponds directly to the trigonometric function of the same name. `h` indicates a hyperbolic variant. ## asec * `asec_n(num $n --> num)` ## asin * `asin_n(num $n --> num)` ## acos * `acos_n(num $n --> num)` ## atan * `atan_n(num $n --> num)` ## atan2 * `atan2_n(num $l, num $r --> num))` ## cos * `cos_n(num $n --> num))` ## cosh * `cosh_n(num $n --> num))` ## sin * `sin_n(num $n --> num))` ## sinh * `sinh_n(num $n --> num))` ## sec * `sec_n(num $n --> num))` ## sech * `sech_n(num $n --> num))` ## tan * `tan_n(num $n --> num))` ## tanh * `tanh_n(num $n --> num))` # Relational / Logic Opcodes ## cmp * `cmp_i(int $l, int $r --> int)` * `cmp_n(num $l, num $r --> int)` * `cmp_s(str $l, str $r --> int)` * `cmp_I(Int $l, Int $r --> int)` Compare two values, returns -1 if $l is greater than $r, 0 if they are equal, and 1 if $r is greater than $l. ## eqat * `eqat(str $haystack, str $needle, int $pos --> int)` Return 1 if the string `$haystack` has the string `$needle` at position `$pos`, otherwise return 0. ## eqatic `moar` * `eqatic(str haystack, str $needle, int $pos --> int)` Case-insensitive `eqat` ## eqatim `moar` * `eqatim(str haystack, str $needle, int $pos --> int)` Ignore-mark `eqat`, NFD decomposes and matches the base codepoint Example: `eqat("á", "a", 0) → 1` ## eqaticim `moar` * `eqaticim(str haystack, str $needle, int $pos --> int)` Case-insensitive and ignore-mark `eqat` ## iseq * `iseq_i(int $l, int $r --> int)` * `iseq_n(num $l, num $r --> int)` * `iseq_s(str $l, str $r --> int)` * `iseq_I(Int $l, Int $r --> int)` Return 1 if the two parameters are equal, 0 otherwise. ## isgt * `isgt_i(int $l, int $r --> int)` * `isgt_n(num $l, num $r --> int)` * `isgt_s(str $l, str $r --> int)` * `isgt_I(Int $l, Int $r --> int)` Return 1 if the two parameters are equal if $l is greater than $r, otherwise 0. ## isge * `isge_i(int $l, int $r --> int)` * `isge_n(num $l, num $r --> int)` * `isge_s(str $l, str $r --> int)` * `isge_I(Int $l, Int $r --> int)` Return 1 if $l is greater than or equal to $r, otherwise 0. ## islt * `islt_i(int $l, int $r --> int)` * `islt_n(num $l, num $r --> int)` * `islt_s(str $l, str $r --> int)` * `islt_I(Int $l, Int $r --> int)` Return 1 if $l is less than $r, otherwise 0. ## isle * `isle_i(int $l, int $r --> int)` * `isle_n(num $l, num $r --> int)` * `isle_s(str $l, str $r --> int)` * `isle_I(Int $l, Int $r --> int)` Return 1 if $l is less than or equal to $r, otherwise 0. ## isne * `isne_i(int $l, int $r --> int)` * `isne_n(num $l, num $r --> int)` * `isne_s(str $l, str $r --> int)` * `isne_I(Int $l, Int $r --> int)` Return 1 if the two parameters are not equal, otherwise 0. ## not_i * `not_i(int $val --> int)` Return 1 if `$val` is 0, 0 otherwise. # Array Opcodes ## atpos * `atpos(@arr, int $i --> Mu)` * `atpos_i(@arr, int $i --> int)` * `atpos_n(@arr, int $i --> num)` * `atpos_s(@arr, int $i --> str)` Return whatever is bound to @arr at position $i. ## bindpos * `bindpos(@arr, int $i, Mu $v --> Mu)` * `bindpos_i(@arr, int $i, int $v --> int)` * `bindpos_n(@arr, int $i, num $v --> num)` * `bindpos_s(@arr, int $i, str $v --> str)` Bind $v to @arr at position $i and return $v. ## atposref * atposref_i(@arr, int $idx --> int) * atposref_n(@arr, int $idx --> num) * atposref_s(@arr, int $idx --> str) Returns a container (of type `IntPosRef`, `NumPosRef`, or `StrPosRef`) that you can assign to or read from which will directly access `@arr` at index `$idx`. ## elems * `elems(@arr --> int)` * `elems(%hash --> int)` Return the number of elements in `@arr`, or the number of keys in `%hash`. ## existspos * `existspos(@arr, int $i --> int)` Return 1 if anything is bound to `@arr` at position `$i`, 0 otherwise. ## list * `list(... --> Mu)` * `list_i(... --> Mu)` * `list_n(... --> Mu)` * `list_s(... --> Mu)` * `list_b(... --> Mu)` Create a list of the given parameters. If no arguments are passed, an empty list is created. If a typed variant is used, the parameters are coerced to the appropriate type. ## push * `push(@arr, Mu $v --> Mu)` * `push_i(Array int @arr, int $v --> int)` * `push_n(Array num @arr, num $v --> num)` * `push_s(Array str @arr, str $v --> str)` "Push $v onto the end of @arr." Bind $v to @arr at the position at the end of @arr, i.e., the position that is just after the last position of @arr that has been bound to. Return value is not currently defined. ## pop * `pop(@arr --> Mu)` * `pop_i(@arr --> int)` * `pop_n(@arr --> num)` * `pop_s(@arr --> str)` "Pop the last value off the end of @arr." Return the value of @arr at its last bound position, and unbind @arr at that position. ## setelems * `setelems(@arr, int $i --> @arr)` Set the size of `@arr` to `$i` elements. If less than the current size, any elements after the new last position are unbound. If greater, the empty elements at the end are bound with potentially VM specific null entries. ## shift * `shift(@arr --> Mu)` * `shift_i(@arr --> int)` * `shift_n(@arr --> num)` * `shift_s(@arr --> str)` "Shift the first value from the beginning of @arr." Return the value of @arr at index 0, unbind @arr at index 0, and move all other bindings of @arr to the index one below what they were previously bound to. ## splice * `splice(@arr, @from, int $offset, int $count --> @arr)` Remove the elements in `@arr` starting at `$offset`, for `$count` positions. Replace them with all the elements from `@from`. ## unshift * `unshift(@arr, Mu $v --> $v)` * `unshift_i(@arr, int $v --> $v)` * `unshift_n(@arr, num $v --> $v)` * `unshift_s(@arr, str $v --> $v)` "Shift $v into the beginning of @arr." Bind $v to @arr at index 0, move all other bindings of @arr to the index one above what they were previously bound to. Return the $v on JVM. ## iterator * `iterator()` Returns an iterator object to iterate over a list's items. For example: ```perl my $list := nqp::list('a', 'b', 'c'); my $iter := nqp::iterator($list); while $iter { say(nqp::shift($iter)); } ``` You can also use `nqp::iterator()` to iterate over a hash's key-value pairs. # Hash Opcodes ## atkey * `atkey(%hash, str $key --> Mu)` * `atkey_i(%hash, str $key --> int)` * `atkey_n(%hash, str $key --> num)` * `atkey_s(%hash, str $key --> str)` Return the value of %hash at key $key. ## bindkey * `bindkey(%hash, str $key, Mu $v --> $v)` * `bindkey_i(%hash, str $key, int $v --> $v)` * `bindkey_n(%hash, str $key, num $v --> $v)` * `bindkey_s(%hash, str $key, str $v --> $v)` Bind key $key of %hash to $v and return $v. ## existskey * `existskey(%hash, str $key --> int)` Return 1 if %hash has key $key bound to something, otherwise 0. ## deletekey * `deletekey(%hash, str $key --> %hash)` Delete the given key from %hash. ## iterkey * `iterkey_s($pair --> str)` Returns the key associated with the given key-value pair. For example: ```perl for %hash { say(nqp::iterkey_s($_), ' => ', nqp::iterval($_)); } ``` ## iterval * `iterval($pair --> Mu)` Returns the value associated with the given key-value pair. # String Opcodes ## chars * `chars(str $str --> int)` Return the number of characters in the string. ## chr * `chr(int $codepoint --> str)` Given a unicode codepoint, return a str containing its character. Will throw an exception on invalid codepoints. ## codepointfromname * `codepointfromname(str $name --> int)` Returns the codepoint for the given unicode character name, or -1 if no match was found. ## getstrfromname * `getstrfromname(str $name --> str)` (Currently only on MoarVM) Like `codepointfromname` except it returns a string instead of a codepoint. This function is able to return not just Unicode codepoints by name, but also Unicode Named Sequences, including Emoji Sequences and Emoji ZWJ Sequences and Name Aliases. In addition it is also case-insensitive, unlike codepointfromname See these links for a full list of [Named Sequences][Named-Sequences], [Emoji Sequences][Emoji-Sequences], [Emoji ZWJ Sequences][Emoji-ZWJ-Sequences] and [Name Aliases][Name-Aliases]. [Named-Sequences]: http://www.unicode.org/Public/UCD/latest/ucd/NamedSequences.txt [Emoji-Sequences]: http://www.unicode.org/Public/emoji/4.0/emoji-sequences.txt [Emoji-ZWJ-Sequences]: http://www.unicode.org/Public/emoji/4.0/emoji-zwj-sequences.txt [Name-Aliases]: http://www.unicode.org/Public/UCD/latest/ucd/NameAliases.txt ## concat * `concat(str $l, str $r --> str)` Return a string that is the concatenation of the two passed in strings. ## decode * `decode($buffer, str $encoding --> str)` Returns an (NFG) string resulting from decoding the specified buffer assuming the specified encoding. ## decodetocodes `moar` * `decodetocodes($buffer, str $encoding, int $normalization, $codes)` Decodes the bytes in the specified buffer using the provided encoding. Applies normalization as requested (must be one of the nqp::const::NORMALIZE_* values; use nqp::const::NORMALIZE_NONE to apply no normalization). Places the code points into $codes, which should be some VMArray holding 32-bit integers. ## encode * `encode(str $string, str $encoding, $buffer)` Encodes an (NFG) string into the specified encoding, writing into the buffer provided. The data written is normalized according to NFC. ## encodefromcodes `moar` * `encodefromcodes($codes, str $encoding, $buffer)` Takes a 32-bit integer array of Unicode codepoints, encodes them using the chosen encoding, and writes them into the buffer. No normalization is applied. ## encodenorm * `encode(str $string, str $encoding, int $normalization, $buffer)` Encodes an (NFG) string into the specified encoding, writing into the buffer provided. The data written is normalized according to the normalization value passed (which must be one of the nqp::const::NORMALIZE_* values). Specifying NORMALIZE_NONE is equivalent to NFC. ## escape * `escape(str $str --> str)` Given a string, return an escaped version that replaces the following characters with their escaped equivalents: "\\", "\b", "\n", "\r", "\t", "\f", "\"", "\a", and "\e". ## fc * `fc(str $str --> str)` Returns a Unicode "fold case" operation copy of string, suitable for doing caseless string comparisons. ## findcclass * `findcclass(int $class, str $str, int $i, int $count --> int)` Search the string starting at the `$i`th character, for `$count` characters. Return the position of the first character that is of the specified class (`nqp::const::CCLASS_*`). If no characters match, return the position of the first character after the given range, or the length of the string, whichever is smaller. ## findnotcclass * `findnotcclass(int $class, str $str, int $i, int $count --> int)` Search the string starting at the `$i`th character, for `$count` characters. Return the position of the first character that is not of the specified class (`nqp::const::CCLASS_*`). If no characters match, return the position of the first character after the given range, or the length of the string, whichever is smaller. ## flip * `flip(str $str --> str)` Return a string with the characters of `$string` in reverse order. ## hash * `hash(... --> Mu)` Return a hash. The first argument is a key, the second its value, and so on. Be sure to pass an even number of arguments, a VM specific error may occur otherwise. ## index * `index(str $haystack, str $needle --> int)` * `index(str $haystack, str $needle, int $pos --> int)` Return the position in `$haystack` at which `$needle` appears, or -1 if `$needle` does not appear. Begin searching at position `$pos` if specified, or at 0, otherwise. * `indexfrom(str $haystack, str $needle, int $pos)` _Internal_ `index` is converted to this internal opcode by the compiler. ## indexic * `indexic(str $haystack, str $needle, int $pos --> int)` This op has the same arguments and functionality as nqp::index, except it is case-insensitive. For now we only have it under MoarVM, but the plan is to support it on other platforms as well. On MoarVM uses proper Unicode foldcase type comparison. ## indexim `moar` * `indexim(str $haystack, str $needle, int $pos --> int)` Like index but decomposes and matches against the base character. Example: `indexim("bcá", "a", 0) → 2` ## indexicim `moar` * `indexicim(str $haystack, str $needle, int $pos)` Ignorecase and ignoremark `index` ## iscclass * `iscclass(int $class, str $str, int $i --> int)` Return 1 if the `$i`th character of $str is of the specified class, (`nqp::const::CCLASS_*`), 0 otherwise. ## join * `join(str $delim, @arr --> str)` Joins the separate strings of `@arr` into a single string with fields separated by the value of EXPR, and returns that new string. ## lc * `lc(str $str --> str)` Return lowercase copy of string. ## normalizecodes * `normalizecodes($codes-in, int $normalization, $codes-out)` Takes the codepoints in $codes-in, applies the specified normalization, and places the result into the $codes-out array. Both arrays of codepoints must be 32-bit integer arrays. ## ord * `ord(str $str --> int)` * `ord(str $str, int $i --> int)` Return the unicode codepoint of the first character in `$str`, or at the `$i`th character, if it's specified. * `ordat(str $str, int $i --> int)` _Internal_ * `ordfirst(str $str --> int)` _Internal_ `ord` is converted to these internal opcodes by the compiler. ## ordbaseat `ordbaseat(str $str, int $pos --> int)` Returns the Unicode codepoint which is the base (non extend/prepend character at that position). If it is a degenerate, and contains no base character, it then returns the first codepoint in that grapheme. ## radix * `radix(int $radix, str $str, int $pos, int $flags --> Mu)` * `radix_I(int $radix, str $str, int $pos, int $flags, Mu:T $type --> Mu)` Convert string $str into a number starting at offset $pos and using radix $radix. The result of the conversion returns an array with out[0] = converted value out[1] = $radix ** $number-of-digits-converted out[2] = offset after consuming digits, -1 if no digits consumed The opcode skips single underscores between pairs of digits, per the Perl 6 specification. The $flags is a bitmask that modifies the parse and/or result: 0x01: negate the result (useful if you've already parsed a minus) 0x02: parse a leading +/- and negate the result on - 0x04: parse trailing zeroes but do not include in result (for parsing values after a decimal point) ## replace * `replace(str $str, int $offset, int $count, str $replacement --> str)` Return a copy of `$str` where the characters starting at `$offset` for `$count` characters have been replaced with the `$replacement` string. ## rindex * `rindex(str $haystack, str $needle --> int)` * `rindex(str $haystack, str $needle, int $pos --> int)` Searching backwards through the `$haystack`, return the position at which `$needle` appears, or -1 if it does not. Begin searching at `$pos` if specified, otherwise start from the last position. * `rindexfrom(str $haystack, str $needle, int $pos)` _Internal_ `rindex` is converted to this internal opcode by the compiler. ## rindexfromend `jvm` * `rindexfromend(str $haystack, str $needle)` _Internal_ `rindex` is converted to this internal opcode by the compiler. ## split * `split(str $delimiter, str $string --> Mu)` Splits the string `$string` into an array of elements; these elements are the substrings between delimiters in the original string. If the original string begins or ends with the delimiter, the resulting array will begin or end with an empty element. ## sprintf * `sprintf(str $pattern, @values --> str)` Returns a string formatted by the printf conventions similar to Perl 5 / C. Machine sized numeric types, their limits and therefore overflows are not implemented though. ## sprintfdirectives * `sprintfdirectives(str $pattern)` This takes the same pattern as `sprintf` does, and computes the needed value-count that `sprintf` would have to provide. ## sprintfaddargumenthandler * `sprintfaddargumenthandler(Mu $handler)` Lets you register a handler-instance that supports the sprintf op when it has to numify custom types. This handler has to provide two methods, `mine` and `int`. `mine` gets the the value in question and returns true if this handler is in charge for this type, false otherwise. The method `int` does the conversion for patterns like %d. ```perl my class MyHandler { method mine($x) { $x ~~ MyType } method int($x) { $x.Int } } ``` ## strfromcodes * `strfromcodes($codes --> str)` Returns an (NFG) string built from the specified codepoints, which must be provided as a 32-bit integer array. ## strtocodes * `strtocodes(str $str, int $normalization, $codes)` Takes an NFG string, and places the codepoints from it into the codes array, which must be a 32-bit integer array. Applies the specified normalization, specified as one of the nqp::const::NORMALIZE_* values; NORMALIZE_NONE is not allowed. ## substr * `substr(str $str, int $position --> str)` * `substr(str $str, int $position, int $length --> str)` Return the portion of the string starting at the given position. If `$length` is specified, only return that many characters. The numbered variants required the args specified - the unnumbered version may use either signature. ## substr2 `jvm` * `substr2(str $str, int $position)` _Internal_ A JVM specific internal opcode for `substr`. ## substr3 `jvm` * `substr3(str $str, int $position, int $length)` _Internal_ A JVM specific internal opcode for `substr`. ## tc * `tc(str $str --> str)` Return titlecase copy of string. ## uc * `uc(str $str --> str)` Return uppercase copy of string. ## unicmp_s * `unicmp_s(str, str, int, int, int --> int)` (Currently only on MoarVM) Compares strings using the [Unicode Collation Algorithm][UCA] (UCA). #### Parameters: ``` str, str, # strings to compare int, # collation mode (bitmask) int, # ISO 639 Language code int # ISO 3166 Country code ``` The collation mode defines whether we use Primary, Secondary, Tertiary and/or Quaternary sorting. Compares two strings, using the Unicode Collation Algorithm Return values: 0 The strings are identical for the collation levels requested -1/1 String a is less than string b/String a is greater than string b `collation_mode` acts like a bitfield. Each of primary, secondary and tertiary collation levels can be either: disabled, enabled, reversed. In the table below, where + designates sorting normal direction and - indicates reversed sorting for that collation level. ``` Collation level | bitfield value Primary+ | 1 Primary- | 2 Secondary+ | 4 Secondary- | 8 Tertiary+ | 16 Tertiary- | 32 Quaternary+ | 64 Quaternary- | 128 ``` While the Primary, Secondary and Tertiary mean different things for different scripts, for the Latin script used in English they mostly correspond with Primary being Alphabetic, Secondary being Diacritics and Tertiary being Case. Setting 0 for language and country will collate all scripts according to their own distinctions for Primary, Secondary, and Tertiary, although it will not take into account certain languages. For example, some language based differences in collation: * “…include ch as in traditional Spanish, ä as in traditional German, and å as in Danish” ― [Unicode Technical Report 10][UCA]. For more information see [Unicode TR10][UCA]. *** Note *** - Currently only language and country insensitive sorting methods are implemented. [UCA]: http://unicode.org/reports/tr10/ ## x * `x(str $str, int $count --> str)` Return a new string containing `$count` copies of `$str`. # Unicode Property Opcodes ## getuniname * `getuniname(int $codepoint --> str)` Translate a codepoint to its Unicode name. ## getuniprop_int `moar` * `getuniprop_int(int $codepoint, int $propcode --> int)` Uses the table found by unipropcode to look up an integer property value for a given codepoint. Note that many properties that are officially numeric are really stored as strings, and if you try to use this op on such a property, you'll get a meaningless position in an enum table instead of the value you want. ## getuniprop_str * `getuniprop_str(int $codepoint, int $propcode --> str)` Same thing, but fetches a string property value. ## getuniprop_bool `moar` * `getuniprop_bool(int $codepoint, int $propcode --> int)` Same thing, but fetches a boolean property value. ## matchuniprop `moar` * `matchuniprop(int $codepoint, int $propcode, int $pvalcode --> int)` Looks up a codepoint property and return 1 if it matches the pval, 0 otherwise. The propcode and pvalcode may be looked up with the opcodes above. (Note that you can use the property value name (e.g. Nd) for both lookups.) ## unipropcode * `unipropcode(str $propname --> int)` Translates a property name to the backend's property code. This is not distinct across backends and is expected to change over time. For the most part only useful for calling getuniprop_int, get_uniprop_str or get_uniprop_bool or comparing whether two unicode property names resolve to the same propcode, for example 'Alpha', 'alpha', 'alphabetic' and 'Alphabetic' should return the same property code. ## unipvalcode `moar` * `unipvalcode(int $propcode, str $propname --> int)` Looks up a property name in its property category, and returns which table within that category to use. ## hasuniprop `moar` * `hasuniprop(str $string, int offset, int propcode, int pvalcode --> int)` Checks if the string has a specific property value at a specific offset. Requires both the propcode and the pvalcode to work. # VM-Provided Streaming Decoder Opcodes ## decoderconfigure * `decoderconfigure(Decoder $dec, str $encoding, VMHash $config)` Configures the decoder with an encoding. The `$config` hash parameter is currently unused, and an empty hash or an `nqp::null` should be passed. ## decodersetlineseps * `decodersetlineseps(Decoder $dec, VMArray $separators)` Sets the line separators to be used for line-based reading. It should be a string array (`nqp::list_s(...)`). ## decoderaddbytes * `decoderaddbytes(Decoder $dec, VMArray $blob)` Adds bytes to the decoder's internal buffer. Must have VMArray REPR, and must have elements of type `int8` or `uint8`. ## decodertakechars * `decodertakechars(Decoder $dec, int $num-chars --> str)` Returns an NFG string consisting of `$num-chars` graphemes, provided that many are available after decoding. If less than `$num-chars` characters can be decoded, then `nqp::null_s` will be returned. Note that a decoded codepoint at the end of a byte buffer may not be available as a character if the encoding allows the next character to be a combining character. ## decodertakeallchars * `decodertakeallchars(Decoder $dec --> str)` Decodes all remaining undecoded bytes, and flushes the normalization buffer. Returns an NFG string consisting of the decoded characters. This is suitable to use when the end of a stream of bytes to decode has been reached (for example, EOF when reading a file). ## decodertakeavailablechars * `decodertakeavailablechars(Decoder $dec -->int)` Decodes all remaining undecoded bytes. Returns an NFG string consisting of the decoded characters. Does not flush the normalization buffer. This is suitable when performing streaming decoding, and a later byte buffer may provide a combining character. ## decodertakeline * `decodertakeline(Decoder $dec, int $chomp, int $incomplete-ok --> str)` Decodes bytes until a line separator is reached, or all bytes have been decoded. If `$incomplete-ok` is zero and the separator was not found, then `nqp::null_s` will be returned. (Thus, `$incomplete-ok` is appropriate only when knowing that the end of the stream has been reached.) If `$chomp` is non-zero, then the separator--if present--will not be included in the resulting string. ## decoderbytesavailable * `decoderbytesavailable(Decoder $dec --> int)` Returns the number of undecoded bytes available inside of the decoder. This is useful in the case that chunks of the input should also be pulled out as bytes, and may be useful for doing tuning or pre-fetching in various other cases. Note that the result does not include bytes that were decoded but have not yet been taken as characters, or that were decoded to code points that are still in the normalization buffer. Thus the result is only accurate before reading any chars or after `decodertakechars` or after `decodertakeline` with `$incomplete-ok` passed a non-zero value. ## decodertakebytes * `decodertakebytes(Decoder $dec, VMArray $blob_type, int $bytes)` Takes up to `$bytes` bytes from the decode stream's undecoded buffer, makes an instance of the `$blob_type`, and places the bytes in it. The same set of caveats about decoded-but-untaken bytes in `decoderbytesavailable` apply. ## decoderempty * `decoderempty(Decoder $dec --> int)` Returns 1 if the decoder is empty (this means that there are no undecoded bytes, no decoded but untaken chars, and nothing in the normalization buffer). Otherwise returns 0. # Conditional Opcodes ## if * `if(Block $condition, Block $then)` * `if(Block $condition, Block $then, Block $else)` If the `$condition` evaluates to a non-zero value, run the `$then` block. If not, and an `$else` block is present, run that instead, if it's absent, return result of `$condition`. ## unless * `unless(Block $condition, Block $then)` * `unless(Block $condition, Block $then, Block $else)` If the `$condition` evaluates to 0, run the `$then` block. If not, and an `$else` block is present, run that instead, if it's absent, return result of `$condition`. # Loop/Control Opcodes ## control * `QAST::Op.new(:op, :name);` * `QAST::Op.new(:op, :name);` * `QAST::Op.new(:op, :name);` Not callable directly from NQP, but used in languages via QAST to perform loop control. The specific kind of loop control desired is specified via the `:name` attribute; either `next`, `last`, or `redo`. ## defor * `defor(Block $cond, Block $body)` If the `$cond` evaluates to defined value, return it, otherwise, evaluate the `$body`. ## for * `for(Iterable $iter, Block $body)` Invoke the `$body` for every item available in `$iter`. ## ifnull * `ifnull(Block $cond, Block $body)` If the `$cond` evaluates to null, evaluate the `$body`, otherwise return the result of `$cond`. ## repeat_until * `repeat_until(Block $condition, Block $body)` * `repeat_until(Block $condition, Block $body, Block $post)` First run the `$body`. Then, enter a loop, running the `$body` only if the condition returns 0. If a `$post` block is present, run that at the end, regardless of `$condition`. ## repeat_while * `repeat_while(Block $condition, Block $body)` * `repeat_while(Block $condition, Block $body, Block $post)` First run the `$body`. Then, enter a loop, running the `$body` only if the condition returns a non-0 value. If a `$post` block is present, run that at the end, regardless of `$condition`. ## stmts * `stmts(...)` Executes the given statements sequentially. For example: ```perl nqp::stmts((my $a := nqp::chars("foo")), say($a), say("bar")); # 3 # bar ``` Note that `:=` statements must be surrounded by parentheses. ## until * `until(Block $condition, Block $body)` * `until(Block $condition, Block $body, Block $post)` Enter a loop, running the `$body` only if the condition returns 0. If a `$post` block is present, run that at the end, regardless of `$condition`. ## while * `while(Block $condition, Block $body)` * `while(Block $condition, Block $body, Block $post)` Enter a loop, running the `$body` only if the condition returns a non-0 value. If a `$post` block is present, run that at the end, regardless of `$condition`. # Exceptional Opcodes ## backtrace * `backtrace(Exception $ex)` Return an array of hashes, describing the backtrace of the given exception. ## backtracestrings * `backtracestrings(Exception $ex)` Return an array of strings, describing the backtrace of the given exception. ## die * `die(str $message)` * `die_s(str $message)` Create and throw an exception using the given message. ## exception * `exception()` Return the current exception object. ## getextype * `getextype(Exception $ex)` Gets the exception category (`nqp::const::CONTROL_*`) ## getmessage * `getmessage(Exception $ex)` Gets the exception message. ## getpayload * `getpayload(Exception $ex)` Gets the exception payload. ## newexception * `newexception()` Return a new exception object. ## resume * `resume(Exception $ex)` Resume the exception, if possible. ## rethrow * `rethrow(Exception $ex)` Re-throw the exception. ## setextype * `setextype(Exception $ex, int $type)` Sets the exception category (`nqp::const::CONTROL_*`) ## setmessage * `setmessage(Exception $ex, str $message)` Sets the exception message. ## setpayload * `setpayload(Exception $ex, Mu $obj)` Sets the exception payload. ## throw * `throw(Exception $ex)` Throw the exception. # Input/Output Opcodes ## closefh * `closefh(Handle $fh)` Close the filehandle. ## eoffh * `eoffh(Handle $fh --> int)` Return 1 if this filehandle is at the end of the file, otherwise 0. ## filenofh * `filenofh(Handle $fh --> int)` Returns the filehandle number. Not usable on the JVM (always returns -1). ## flushfh * `flushfh(Handle $fh)` Flushes the filehandle, forcing it to write any buffered output. ## getstderr * `getstderr()` Return the filehandle for standard error. ## getstdin * `getstdin()` Return the filehandle for standard input. ## getstdout * `getstdout()` Return the filehandle for standard output. ## open * `open(str $filename, str $mode)` Open the specified file in the given mode. Valid modes include `r` for read, `w` for write, and `wa` for write with append. Returns a filehandle. ## openasync `jvm` _Experimental_ * `openasync(str $filename, str $mode)` Open the specified file in the given mode for async IO. See `open` for valid modes. ## print * `print(str $str)` Output the given string to stdout. ## readfh * `readfh(Handle $fh, @arr, long $count)` Given a readable `$fh`, and an array of `Buf[int8]` or a `Buf[uint8]`, read in the next `$count` bytes from the filehandle and store them in the array. ## say * `say(str $str)` Output the given string to stdout, followed by a newline. ## seekfh * `seekfh(Handle $fh, int $offset, int $whence)` Seek in the filehandle to the location specified by the offset and whence. ## tellfh * `tellfh(Handle $fh)` Return current access position for an open filehandle. ## writefh * `writefh(Handle $fh, Mu $str)` Output the given object to the filehandle. Returns the number of bytes written. # File / Directory / Network Opcodes ## chdir * `chdir(str $path)` Change the working directory to the given path. ## chmod * `chmod(str $path, int $mode)` Change the permissions of `$path` to the posix style permissions of `$mode`. Returns 0 on success, throws an exception on failure. ## closedir * `closedir(Handle $)` Close the given directory handle. ## copy * `copy(str $from, str $to)` Copy file `$from` to file `$to`. Return 0 on success, throw an exception on failure. ## cwd * `cwd(--> str)` Return a string containing the current working directory. ## fileexecutable * `fileexecutable(str $str --> int)` If the specified filename refers to an executable file, returns 1. If not, returns 0. If an error occurs, return -1. ## fileislink * `fileislink(str $str --> int)` If the specified filename refers to a symbolic link, returns 1. If not, returns 0. If an error occurs, return -1. ## filereadable * `filereadable(str $str --> int)` If the specified filename refers to a readable file, returns 1. If not, returns 0. If an error occurs, return -1. ## filewritable * `filewritable(str $str --> int)` If the specified filename refers to a writeable file, returns 1. If not, returns 0. If an error occurs, return -1. ## link * `link(str $before, str $after)` Create a link from `$after` to `$before` ## mkdir * `mkdir(str $name, int $mode)` Create a directory of the given name. Use posix-style mode on non-windows platforms. Returns 0, or throws an exception. ## nextfiledir * `nextfiledir(handle $iterator)` Given the result of an opendir, return the next path from that directory. When no more items are available, return a null string. (check with `null_s`) ## opendir * `opendir(str $path)` Return a directory handle on the given directory path. Throw an exception if `$path` is not a directory. ## rename * `rename(str $from, str $to)` Rename file `$from` to file `$to`. Return 0 on success, throw an exception on failure. ## rmdir * `rmdir(str $path)` Delete the given directory $path. Returns 0 on success, -2 if the directory didn't exist. May throw an exception. ## stat * `stat(str $path, int $code)` Given a path and a code, return an int describing that path using the OS's stat() function. Any of these variants may throw an exception if the platform does not support them. (JVM does not support `STAT_PLATFORM_BLOCKSIZE` or `STAT_PLATFORM_BLOCKS`). * `nqp::const::STAT_EXISTS` Returns 1 if the path exists, 0 otherwise. * `nqp::const::STAT_FILESIZE` Returns the size of the file in bytes. * `nqp::const::STAT_ISDIR` Returns 1 if the path is a directory, 0 otherwise, -1 if an exception occurred processing the request. * `nqp::const::STAT_ISREG` Returns 1 if the path is a regular file, 0 otherwise, -1 if an exception occurred processing the request. * `nqp::const::STAT_ISDEV` Returns 1 if the path is a special file, 0 otherwise, -1 if an exception occurred processing the request. * `nqp::const::STAT_ISLNK` Returns 1 if the path is a symbol link, 0 otherwise, -1 if an exception occurred processing the request. * `nqp::const::STAT_CREATETIME` * `nqp::const::STAT_ACCESSTIME` * `nqp::const::STAT_MODIFYTIME` * `nqp::const::STAT_CHANGETIME` Returns respective time attribute in seconds since epoch, or -1 if an exception occurred. * `nqp::const::STAT_BACKUPTIME` Returns -1. * `nqp::const::STAT_GID` * `nqp::const::STAT_UID` Returns the user id and group id of the path, respectively. Returns -1 if an exception occurred. * `nqp::const::STAT_PLATFORM_DEV` Returns the device number of filesystem associated with the path. Returns -1 if an exception occurred. * `nqp::const::STAT_PLATFORM_INODE` Returns the inode. Returns -1 if an exception occurred. * `nqp::const::STAT_PLATFORM_MODE` Returns unix style mode. Returns -1 if an exception occurred. * `nqp::const::STAT_PLATFORM_NLINKS` Returns number of hard links to the path. Returns -1 if an exception occurred. * `nqp::const::STAT_PLATFORM_DEV` Returns the device identifier. Returns -1 if an exception occurred. * `nqp::const::STAT_PLATFORM_BLOCKSIZE` Returns preferred I/O size in bytes for interacting with the file. * `nqp::const::STAT_PLATFORM_BLOCKS` Returns number of system-specific blocks allocated on disk. ## stat_time * `stat_time(str $path, int $code)` Given a path and one of the `STAT_*TIME` codes, return that time attribute as a num, using the OS's stat() function. ## lstat * `lstat(str $path, int $code)` Same as stat, but internally uses the OS's lstat() function, which does *not* follow symlinks. ## lstat_time * `stat_time(str $path, int $code)` Same as stat_time, but internally uses the OS's lstat() function, which does *not* follow symlinks. ## symlink * `symlink(str $before, str $after)` Create a symbolic link from `$after` to `$before` ## unlink * `unlink(str $path)` Delete the given file $path. Returns 0 on success, -2 if the file didn't exist. May throw an exception. # Type/Conversion Opcodes ## bool * `bool_I(Int $val)` Returns 0 if `$val` is 0, otherwise 1. ## bootarray `jvm` `moar` * `bootarray()` Returns a VM specific type object for a native array. ## boothash `jvm` `moar` * `boothash()` Returns a VM specific type object for a native hash. ## bootint `jvm` `moar` * `bootint()` Returns a VM specific type object that can box a native int. ## bootintarray `jvm` `moar` * `bootintarray()` Returns a VM specific type object for a native array of int. ## bootnum `jvm` `moar` * `bootnum()` Returns a VM specific type object that can box a native num. ## bootnumarray `jvm` `moar` * `bootnumarray()` Returns a VM specific type object for a native array of num. ## bootstr `jvm` `moar` * `bootstr()` Returns a VM specific type object that can box a native str. ## bootstrarray `jvm` `moar` * `bootstrarray()` Returns a VM specific type object for a native array of str. ## box * `box_i(int $val, Mu:T $type)` * `box_n(num $val, Mu:T $type)` * `box_s(str $val, Mu:T $type)` Given a native value, return a perl 6 object of the given type with the same value. ## decont `decont(Mu $val --> Mu)` Extract, or **de**-**cont**ainerize, a value from a `Scalar` container: use nqp; my $a = (1, 2, 3); .say for $a; # OUTPUT: «(1, 2, 3)␤» .say for nqp::decont($a); # OUTPUT: «1␤2␤3␤» ## defined * `defined(Mu $obj --> int)` Returns 1 if the object is not null and is not a Type object, 0 otherwise. ## fromnum * `fromnum_I(num $val, Mu:T $type --> Int)` Convert float value to a Big Integer of the given type, discarding any decimal portion. ## fromstr * `fromstr_I(str $val, Mu:T $type --> Int)` Convert string value to a Big Integer of the given type. ## isbig * `isbig_I(Int $obj --> int)` Returns a 1 if the object's numerical representation requires a big int, 0 otherwise. ## isconcrete * `isconcrete(Mu $obj --> int)` Returns a 1 if the object is not a type object, 0 otherwise. ## iscont * `iscont(Mu $obj --> int)` Returns a 1 if the object is a container type, 0 otherwise. ## isfalse * `isfalse(Mu $obj --> int)` Returns a 0 if the object has a truthy value, 1 otherwise. ## ishash * `ishash(Mu $obj --> int)` Returns a 1 if the object is a Hash, 0 otherwise. ## isint * `isint(Mu $obj --> int)` Returns a 1 if the object is an int type, 0 otherwise. ## isinvokable * `isinvokable(Mu $obj --> int)` Returns a 1 if the object represents something executable, 0 otherwise. ## islist * `islist(Mu $obj)` Returns a 1 if the object is an Array, 0 otherwise. ## isnanorinf * `isnanorinf(num $n --> int)` Return truth value indicating if this number represents any of the special values, positive infinity, negative infinity, or NaN. ## isnull * `isnull(Mu $obj --> int)` * `isnull_s(str $obj --> int)` Returns a 1 if the object is a null, 0 otherwise. ## isnum * `isnum(Mu $obj --> int)` Returns a 1 if the object is a float type, 0 otherwise. ## isprime * `isprime_I(Int $obj, Int $rounds --> int)` Returns a 1 if the integer value of the object is prime, 0 otherwise. Performs up to `$rounds` of Miller-Rabin tests if necessary. ## isstr * `isstr(Mu $obj --> int)` Returns a 1 if the object is a str type, 0 otherwise. ## istrue * `istrue(Mu $obj --> int)` Returns a 1 if the object has a truthy value, 0 otherwise. ## istype * `istype(Mu $obj, Mu:T $obj --> int)` Returns a 1 if the object is of the given type, 0 otherwise. ## null * `null(--> Mu)` * `null_s(--> str)` Generate a null value. `null_s` returns a null string value that can be stored in a native str. The value returned by `null_s` is VM dependant. Notably, it may stringify differently depending on the backend. ## jvmisnull `jvm` * `jvmisnull(Mu $obj)` Returns a 1 if the object is an NQP Type object *or* the underlying JVM object is null. Returns 0 otherwise. ## tostr * `tostr_I(Int $val --> str)` Convert Big Integer value to a native string. ## tonum * `tonum_I(Int $val --> num)` Convert Big Integer value to a native number. ## unbox * `unbox_i(Mu $val --> int)` * `unbox_n(Mu $val --> num)` * `unbox_s(Mu $val --> str)` Given a Perl 6 object, return a native with the same value, of the type indicated by the opcode suffix. # OO/SixModel Opcodes ## attrinited * `attrinited(Mu $obj. Mu:T $type, str $attributename --> int)` Test if the attribute of name `$attributename` of object `$obj` has been bound, see `bindattr`. Note that any access to the atribute that results in a `getattr` call causes it to be inited. ## bindattr * `bindattr(Mu $obj, Mu:T $type, str $attributename, Mu $new_value)` * `bindattr_i(Mu $obj, Mu:T $type, str $attributename, int $new_value)` * `bindattr_n(Mu $obj, Mu:T $type, str $attributename, num $new_value)` * `bindattr_s(Mu $obj, Mu:T $type, str $attributename, str $new_value)` Binds `$new_value` to the attribute of name `$attributename` of object `$obj`, where the attribute was declared in type `$type`. The notes in the `getattr` documentation also apply to `bindattr`. ## bindcomp * `bindcomp(str $base-class, Mu $compiler --> Mu)` Registers `$compiler` as the compiler for the language named `$base-class`, as in: my $lang = My::Lang::Compiler.new(); nqp::bindcomp('My::Lang', $lang); In general, though, `$lang` will inherit from `HLL::Compiler`, and the above will be achieved via: $lang.language('My::Lang'); ## callmethod * `callmethod(Mu $obj, str $methodname, *@pos, *%named --> Mu)` Uses `findmethod` to locate method `$methodname` on object `$obj`, and `call` to invoke the method with positional arguments `*@pos` and named arguments `*%named`. Example: class A { method x($a, $b, :$c) { say("$a $b $c") } } nqp::callmethod(A, 'x', '4', '2', c => 'foo'); # same as: A.x(4, 2, c => 'foo') ## can * `can(Mu $obj, str $method --> int)` If the object has a method of the given name, return 1. Otherwise, return 0. Returns 1 if ``$obj`` object has FALLBACK method. ## clone * `clone(Mu $obj --> Mu)` Return a clone of the passed in object. ## create * `create(Mu:T $type --> Mu)` Returns a newly allocated instance of type `$type`. ## eqaddr * `eqaddr(Mu $l, Mu $r --> int)` Returns 1 if the objects are the same object in the underlying VM, 0 otherwise. ## findmethod * `findmethod(Mu $obj, str $method --> Mu)` If the object has a method of the given name, return it. Otherwise, throw an exception. ## getattr * `getattr(Mu $obj, Mu:T $type, str $attributename --> Mu)` * `getattr_i(Mu $obj, Mu:T $type, str $attributename --> int)` * `getattr_n(Mu $obj, Mu:T $type, str $attributename --> num)` * `getattr_s(Mu $obj, Mu:T $type, str $attributename --> str)` Returns the attribute of name `$attributename` of object `$obj`, where the object was declared in class `$type`. The `_n`, `_i`, and `_s` variants are for natively typed attributes. The following example demonstrates why the type object needs to passed along, and cannot be inferred from the object: class A { has str $!x } class B is A { has str $!x } my $obj := nqp::create(B); nqp::bindattr_s($obj, A, '$!x', 'A.x'); nqp::bindattr_s($obj, B, '$!x', 'B.x'); nqp::say(nqp::getattr_s($obj, A, '$!x')); nqp::say(nqp::getattr_s($obj, B, '$!x')); Throws an exception if there is no such attribute in the class, the attribute is of the wrong type, or the object doesn't conform to the type. Note that in languages that support a full-blown container model, you might need to decontainerize `$obj` before passing it to `getattr`, unless you actually want to access an attribute of the container. ## getcomp * `getcomp(str $base-class --> Mu)` Returns the compiler class registered for that `$base-class`. See `bindcomp` for more information. ## how * `how(Mu $obj --> Mu)` NQP equivalent for Perl 6's `$obj.HOW`. ## rebless * `rebless(Mu $obj, Mu:T $type --> Mu)` Convert `$obj` to be an object of the new `$type`. ## reprname * `reprname(Mu $obj --> str)` Return the name of the REPR for the given object. ## setwho * `setwho(Mu $obj, Mu $who --> Mu)` Replace `$obj`'s WHO. Return `$obj`. ## who * `who(Mu $obj --> Mu)` NQP equivalent for Perl 6's `$obj.WHO`. ## what * `what(Mu $obj --> Mu)` NQP equivalent for Perl 6's `$obj.WHAT`. ## where * `where(Mu $obj --> int)` Return a unique ID for this `$obj`. # Bit Opcodes ## bitand * `bitand_i(int $l, int $r)` * `bitand_s(str $l, str $r --> str)` * `bitand_I(Int $l, Int $r, Mu:T $type --> Int)` AND the bits in `$l` and `$r`. `_I` variant returns an object of the given type. ## bitneg * `bitneg_i(int $bits --> int)` * `bitneg_I(Int $bits, Mu:T $type --> Int)` Negate the bits in `$bits`. `_I` variant returns an object of the given type. ## bitor * `bitor_i(int $l, int $r)` * `bitor_s(str $l, str $r --> str)` * `bitor_I(Int $l, Int $r, Mu:T $type --> Int)` OR the bits in `$l` and `$r`. `_I` variant returns an object of the given type. ## bitshiftl * `bitshiftl_i(int $bits, int $count --> int)` * `bitshiftl_I(Int $bits, int $count, Mu:T $type --> Int)` Signed left shift of `$bits` by `$count`. `_I` variant returns an object of the given type. ## bitshiftr * `bitshiftr_i(int $bits, int $count --> int)` * `bitshiftr_I(Int $bits, int $count, Mu:T $type --> Int)` Signed right shift of `$bits` by `$count`. `_I` variant returns an object of the given type. ## bitxor * `bitxor_i(int $l, int $r --> int)` * `bitxor_s(str $l, str $r --> str)` * `bitxor_I(Int $l, Int $r, Mu:T $type --> Int)` XOR the bits in `$l` and `$r`. `_I` variant returns an object of the given type. # Context Introspection Opcodes ## ctx * `ctx(--> Context)` Return the object representing the current context. ## ctxcaller * `ctxcaller(Context $ctx)` Given a context, return the caller context, or null. ## ctxlexpad * `ctxlexpad(Context $ctx)` Given a context, return its lexpad ## curlexpad * `curlexpad()` Return the current lexpad. ## ctxouter * `ctxouter(Context $ctx)` Given a context, return the outer context, or null. ## lexprimspec * `lexprimspec(LexPad $pad, str $name --> int)` Given a lexpad and a name, return the name's primitive type. The primitive types are 1 for int, 2 for num and 3 for str. 0 is any object. ## savecapture * `savecapture()` Gets hold of the argument capture passed to the current block. Commits to doing enough copying that the list is valid any amount of time. See usecapture for a version of the op that doesn't promise that. Used by the multi-dispatcher. ## usecapture * `usecapture()` Gets hold of the argument capture passed to the current block. (a future usecapture may invalidate it) It's valid to implement this exactly the same way as savecapture if there's no performance benefit to be had in a split. Used by the multi-dispatcher. ## getlex * `getlex(str $name)` * `getlex_i(str $name)` * `getlex_n(str $name)` * `getlex_s(str $name)` Looks up the lexical with the specified name and the specified type. Searching in the outer frames, starting at the current. An error is thrown if it does not exist or if the type is incorrect. ## getlexref * `getlexref_i(str $name)` * `getlexref_n(str $name)` * `getlexref_s(str $name)` Looks up the native type lexical with the specified name and the specified type. Searching in the outer frames, starting at the current. An error is thrown if it does not exist or if the type is incorrect. Lexicalref is a mechanism that allows us to treat a native value stored in a lexpad as if it were a read-writable container we can pass around. the lexicalref that gets created holds a reference to the frame in question and any access to it acts like `getlex` from the frame it originated in ## bindlex * `bindlex(str $name, Mu $value)` * `bindlex_i(str $name, int $value)` * `bindlex_n(str $name, num $value)` * `bindlex_s(str $name, str $value)` Binds $value to the lexical specified by name and type. Searching in the outer frames, starting at the current. An error is thrown if it does not exist or if the type is incorrect. ## getlexdyn * `getlexdyn(str $name)` Looks up the contextual with the specified name in the caller chain, starting at the calling frame. ## bindlexdyn * `bindlexdyn(str $name, Mu $value)` Binds $value to the contextual with the specified name, searching for it in the call-chain, starting at the calling frame. ## getlexouter * `getlexouter(str $name)` Looks up the lexical with the specified name and the specified type. Searching in the outer frames, starting at outer. ## getlexcaller * `getlexcaller(str $name)` Looks up the lexical with the specified name, starting at the calling frame. It checks all outer frames of the caller chain. ## getlexrel * `getlexrel(Mu $context, str $name)` Looks up the lexical with the specified name and the specified type. Searching in the outer frames, starting at the given $context. ## getlexreldyn * `getlexreldyn(Mu $context, str $name)` Looks up the contextual with the specified name in the caller chain, starting at the given $context. ## getlexrelcaller * `getlexrelcaller(Mu $context, str $name)` Looks up the lexical with the specified name, starting at the given $context. It checks all outer frames of the caller chain. # Variable Opcodes ## bind * `bind(Mu $variable, Mu $value)` Binds `$value` to the `$variable`. Dies if `$variable` isn't actually a variable. Same as the `:=` operator in NQP. # Miscellaneous Opcodes ## locallifetime ```perl6 QAST::Op.new( :op('locallifetime'), :node($/), QAST::Stmt.new(...)) ``` Defines when local variables can be considered dead. E.g. the temporary setting of `$_` on the right side of `~~` uses that. ## const * `const()` Not actually an opcode, but a collection of several constants. Each of the constants below can be used in nqp as (e.g.) `nqp::const::CCLASS_ANY`. * CCLASS_ANY * CCLASS_UPPERCASE * CCLASS_LOWERCASE * CCLASS_ALPHABETIC * CCLASS_NUMERIC * CCLASS_HEXADECIMAL * CCLASS_WHITESPACE * CCLASS_PRINTING * CCLASS_BLANK * CCLASS_CONTROL * CCLASS_PUNCTUATION * CCLASS_ALPHANUMERIC * CCLASS_NEWLINE * CCLASS_WORD * HLL_ROLE_NONE * HLL_ROLE_INT * HLL_ROLE_NUM * HLL_ROLE_STR * HLL_ROLE_ARRAY * HLL_ROLE_HASH * HLL_ROLE_CODE * CONTROL_TAKE * CONTROL_LAST * CONTROL_NEXT * CONTROL_REDO * CONTROL_SUCCEED * CONTROL_PROCEED * CONTROL_WARN * STAT_EXISTS * STAT_FILESIZE * STAT_ISDIR * STAT_ISREG * STAT_ISDEV * STAT_CREATETIME * STAT_ACCESSTIME * STAT_MODIFYTIME * STAT_CHANGETIME * STAT_BACKUPTIME * STAT_UID * STAT_GID * STAT_ISLNK * STAT_PLATFORM_DEV * STAT_PLATFORM_INODE * STAT_PLATFORM_MODE * STAT_PLATFORM_NLINKS * STAT_PLATFORM_DEVTYPE * STAT_PLATFORM_BLOCKSIZE * STAT_PLATFORM_BLOCKS * TYPE_CHECK_CACHE_DEFINITIVE * TYPE_CHECK_CACHE_THEN_METHOD * TYPE_CHECK_NEEDS_ACCEPTS The JVM only supports SIG_INT and SIG_KILL. On the MoarVM all of those signal constants below are defined. * SIG_HUP * SIG_INT * SIG_QUIT * SIG_ILL * SIG_TRAP * SIG_ABRT * SIG_EMT * SIG_FPE * SIG_KILL * SIG_BUS * SIG_SEGV * SIG_SYS * SIG_PIPE * SIG_ALRM * SIG_TERM * SIG_URG * SIG_STOP * SIG_TSTP * SIG_CONT * SIG_CHLD * SIG_TTIN * SIG_TTOU * SIG_IO * SIG_XCPU * SIG_XFSZ * SIG_VTALRM * SIG_PROF * SIG_WINCH * SIG_INFO * SIG_USR1 * SIG_USR2 * SIG_THR * SIG_STKFLT * SIG_PWR * SIG_BREAK ## debugnoop `jvm` * `debugnoop(Mu $a)` Returns `$a`. Does nothing, exists only to provide a breakpoint location for debugging. ## exit * `exit(int $status)` Exit nqp, using the given status as the compiler's exit value. ## getenvhash * `getenvhash(--> Mu)` Returns a hash containing the environment variables. Changing the hash doesn't affect the environment variables ## backendconfig * `backendconfig(--> Mu)` Returns a hash containing backend-specific information, like backend-version, configure and build flags. ## getpid * `getpid(--> int)` Return the current process id, an int. ## getppid `moar` * `getppid(--> int)` Return the process id of the parent process, an int. ## jvmclasspaths `jvm` * `jvmclasspaths(--> Mu)` Converts the JVM property `java.class.path` into a list of paths, returns it. ## sha1 * `sha1(str $str -> str)` Given a UTF-8 string, return the SHA-1 digest for that string. This op is built for the specific purpose of hashing source code for dependency management purposes, and isn't intended to be used more widely. ## sleep * `sleep(num $seconds --> num)` Sleep for the given number of seconds (no guarantee is made how exact the time sleeping is spent.) Returns the passed in number. ## takeclosure * `takeclosure(Block $innerblock)` Creates a lexical closure from the block's outer scope. ## time * `time_i(--> int)` * `time_n(--> num)` Return the time in seconds since January 1, 1970 UTC. `_i` variant returns an integral number of seconds, `_n` returns a fractional amount. # Native Call / Interoperability Opcodes ## nativecallrefresh Refresh the C-based data backing the Perl 6 object. This op should only be used if changes have been made to the C-data, and these changes are not being reflected in the Perl 6 object. # Asynchronous Operations The various asynchronous operations, such as timers and asynchronous I/O, take a concurrent queue to push a work item into at an appropriate time. This may be a code object to be invoked, or it may be an array of a code item and some arguments to supply to it. Asynchronous operations are represented by some object with the AsyncTask REPR, the exact details of which are highly specific to a given backend. The type to use for that is given as $handle_type. [As of 2014.04, these are very new and subject to revision and additions.] ## permit * `permit(AsyncTask $handle, int $channel, int $permits)` Takes something with the AsyncTask REPR (the `$handle` parameter) and permits it to emit up to `$permits` more notifications. This is used as a back-pressure mechanism for asynchronous tasks that produce a stream of events, such as representing data arriving over a socket. Some kinds of tasks may emit on multiple channels, for example an asynchronous process may emit events for STDOUT (channel 1) and STDERR (channel 2) if both are of interest. The `$channel` argument is used to specify which channel is to get the permits if needed (use a separate `permit` stament for each channel of interest). If `$permits` is less than zero (e.g., `permit($task, $channel, -1)`, then it means there is no limit to the emits. If `$permits` is set to any value greater than or equal to zero, then: * In the case unlimited emits were permitted previously, the permits will be set to the new value. If the new value is zero, then the reader will be stopped. * Otherwise the number of permits will be incremented by the specified value. If the resulting number of permits allowed is greater than zero and the reader is not running, it will be started. ## cancel * `cancel(AsyncTask $handle)` Takes something with the AsyncTask REPR and tries to cancel it, if it is possible to do so. If it is somehow not possible (for example, the operation already completed anyway), then nothing will happen. This is to avoid race conditions. ## timer * `timer($queue, $schedulee, int $timeout, int $repeat, $handle_type)` Starts a timer. If timeout is zero, the $schedulee is immediately pushed to the queue. Otherwise, it is pushed after the timeout period. If repeat is non-zero, after the initial timeout period it will then be pushed again at the repeat interval. Returns an object of type $handle_type, which has a AsyncTask REPR. Cancellation stops the timer ever repeating again. ## signal * `signal($queue, $schedulee, int [nqp::cosnt::SIG_], $handle_type)` Sets up a signal handler for the given signal. Whenever it occurs, an array is pushed to the queue containing the schedulee and the signal number. Cancel to stop handling it. ## watchfile * `watchfile($queue, $schedulee, str $filename, $handle_type)` Watches an individual file for changes. Pushes an array to the queue when a change is detected, consisting of the schedulee, the filename that changed if provided by the underlying watcher mechanism, a 0 if the file changed, and a 1 if it was renamed. Cancel to stop watching. ## asyncconnect * `asyncconnect($queue, $schedulee, str $host, int $port, $handle_type)` Creates an asynchronous client socket and commences a connection operation. Upon connection, the queue will be passed an array consisting of the schedulee, a handle if the connection was successful (a type object if not) and an error string (some type object if no error). Returns an AsyncTask representing the connection attempt. ## asynclisten * `asynclisten($queue, $schedulee, str $host, int $port, $handle_type)` Creates an asynchronous server socket listening on the specified host and port. Each time a connection arrives, the queue will be passed an array consisting of the schedulee and the newly created asynchronous socket, for communicating with the connecting client. Returns an AsyncTask that can be cancelled to stop listening, or throws an exception if there is an error starting to listen. ## asyncwritestr * `asyncwritestr($handle, $queue, $schedulee, str $to_write, $handle_type)` Writes a string to some handle capable of asynchronous operations. Once the write is complete, the queue will be passed an array consisting of the schedulee, an integer containing the number of bytes written or a type object if there was an error, and a string containing an error or some type object if none. ## asyncwritebytes * `asyncwritebytes($handle, $queue, $schedulee, $to_write, $handle_type)` Writes a byte array to some handle capable of asynchronous operations. Once the write is complete, the queue will be passed an array consisting of the schedulee, an integer containing the number of bytes written or a type object if there was an error, and a string containing an error or some type object if none. ## asyncreadchars * `asyncreadchars($handle, $queue, $schedulee, $handle_type)` Starts reading chars from the handle. When a packet is received and decoded, an array will be pushed to the queue containing the schedulee, a squence number that starts at 0, the string if anything was decoded (type object on error) and an error string (some type object if no error). If EOF is reached, a sequence number of -1 is sent. Cancel to stop reading. ## asyncreadbytes * `asyncreadbytes($handle, $queue, $schedulee, $buf_type, $handle_type)` Starts reading bytes from the handle. When a packet is received, a $buf_type will be constructed and point to the received memory. An array will be pushed to the queue containing the schedulee, a sequence number that starts at 0, the buffer or just its type object on error, and an error string (type object if no error). If EOF is reached, a sequence number of -1 is sent. Cancel to stop reading. ## spawnprocasync `moar` * `spawnprocasync($queue, $args, $cwd, %env, $callbacks)` Replaced *shell* and *spawn*. See t/nqp/111-spawnprocasync.t for an example of use. ## killprocasync `moar` * `killprocasync($handle, $signal)` # Atomic Operations ## cas `moar` * `cas(ObjectContainer $cont, Mu $expected, Mu $new --> Mu)` Takes an object which has a container spec set on it that knows how to do an atomic compare and swap, and performs an atomic compare and swap operation. The operation atomically compares the `$expected` object with what is currently held in the container. If they are the same object, then it replaces it with `$new`. If not, no change takes place. The original object stored in the container is returned, which can be used with `eqaddr` to check if it is the same as the `$expected` object. The container may perform type checks on the `$new` object before it attempts the operation. ## cas_i `moar` * `cas_i(NativeIntRef $i, int64 $expected, int64 $new --> int)` Takes an object with the `NativeRef` representation, which must point to an integer of the machine's atomic operation size. Casts the expected and new parameters to the machine's atomic operation size, and then uses them to perform an atomic compare and swap operation on the referenced integer. The operation atomically compares the `$expected` value with the value currently at the referenced location. If they are equal, it replaces the value with `$new`. If they are not equal, nothing happens. The operation evaluates to the value originally at the location (which can be compared with `$expected` to see if the operation was a success). ## atomicinc_i `moar` * `atomicinc_i(NativeIntRef $i --> int)` Takes an object with the `NativeRef` representation, which must point to an integer of the machine's atomic operation size. Performs an atomic increment of the referenced integer. Returns the value **before** it was incremented. ## atomicdec_i `moar` * `atomicdec_i(NativeIntRef $i --> int)` Takes an object with the `NativeRef` representation, which must point to an integer of the machine's atomic operation size. Performs an atomic decrement of the referenced integer. Returns the value **before** it was decremented. ## atomicadd_i `moar` * `atomicadd_i(NativeIntRef $i, int $value --> int)` Takes an object with the `NativeRef` representation, which must point to an integer of the machine's atomic operation size. Performs an atomic addition of the provided value, which will be cast to the machine's atomic operation size before the operation is performed. Returns the value at the memory location **before** the addition was performed. ## atomicload `moar` * `atomicload(ObjectContainer $c)` Takes an object which has a container spec set on it that knows how to do an atomic load (that is, with appropriate barriering to ensure the latest value is read). Performs the atomic load, and returns the loaded object. ## atomicload_i `moar` * `atomicload_i(NativeIntRef $i --> int)` Takes an object with the `NativeRef` representation, which must point to an integer of the machine's atomic operation size. Performs an atomic load (that is, with appropriate barriering to ensure the latest value is read). ## atomicstore `moar` * `atomicstore(ObjectContainer $c, Mu $value)` Takes an object which has a container spec set on it that knows how to do an atomic load. Performs the atomic store, which may fail if the value being stored does not, for example, meet type constraints. Evaluates to the stored value. The store performs appropriate barriering to ensure the changed value is "published". ## atomicstore_i `moar` * `atomicstore_i(NativeIntRef $i, int64 $value)` Takes an object with the `NativeRef` representation, which must point to an integer of the machine's atomic operation size. Performs an atomic store (that is, with appropriate barriering to ensure the changed value is "published"). ## barrierfull `moar` * `barrierfull()` Performs a full memory barrier. nqp-2018.03/docs/qast.markdown0000644000175000017500000004035413253717146014614 0ustar alexalex# QAST Nodes The "Q" Abstract Syntax Tree is a set of nodes used to represent the runtime behavior of a program that is being compiled. Parsing a program with a grammar produces a parse tree. As the name suggests, this is strongly tied to the syntax of the language being parsed, and reflects the structure of the grammar. This parse tree is mapped by action methods into an abstract syntax tree - a tree of QAST nodes. Rather than talking about program syntax, they talk about what happens at runtime: loops, conditionals, variable lookups, etc. This document describes the available nodes and what they are for. ## QAST::CompUnit While it's not mandatory, most QAST trees that are produced should have a QAST::CompUnit at the top. It should have a single QAST::Block child. This child block represents the outermost scope of the compilation unit. QAST::CompUnit incorporates information that is relevant to the entire unit of code that is being compiled. This includes: * **hll** - the name of the high level language that this QAST tree was produced from, for example, "perl6", "tcl", "bf". * **load** - code to evaluate at the point that the compilation unit is loaded as a module (but not if it is invoked as a mainline program). Happens after any deserialization and deserialization related actions have executed. * **main** - like load, but instead this contains code to execute if the compilation unit is invoked as the mainline Example usage: QAST::CompUnit.new( # Set the HLL. :hll('perl6'), # This variable contains the outermost QAST::Block of the # program. $top_block, # If we run the program as the mainline, then call the top # block automatically. :main(QAST::Op.new( :op('call'), QAST::BVal.new( :value($top_block) ) )) ) Additional adverbs that can be set on a QAST:CompUnit relate to bounded serialization, which will be covered separately. ## QAST::Block A QAST::Block is both a unit of invocation and a unit of lexical scoping. To clarify, this means that if: * You want to create something that can be called, such as a subroutine, closure block or method * You want a fresh lexical environment Then you want to use a QAST::Block. A block can have as many children as you wish, and the final child should evaluate to the return value of the block. A simple example of a block is: QAST::Block.new( QAST::IVal.new( :value(42) ) ) This will compile to a block of code that, when invoked, returns 42. A block can be given a name and a compilation unit unique ID. The name is user facing, and will appear in any automatically generated backtraces. It does not need to be unique within the compilation unit. The compilation unit unique ID, as the name suggests, does need to be. You typically do not need to worry about it much, however; it will be generated for you the first time it is needed, if you do not specify it up front. Often, you will not need to worry about it at all. Here's a block with a name. QAST::Block.new( :name('answer'), QAST::IVal.new( :value(42) ) ) Note that giving a block a name does not imply *any* installation of the block under this name. It's not installed automatically for you as a method or subroutine. That's for you to do. Blocks can be placed within other blocks. This nesting represents the static chain that will be used for the lookup of lexically scoped variables. When you nest one block inside of another, you may wish for it to be invoked automatically when it is encountered, or you may wish that it be treated as an object (for example, which you can bind somewhere). You can configure this by setting the blocktype: QAST::Block.new( QAST::Op.new( :op('say'), QAST::SVal.new( :value('before') ) ), QAST::Block.new( :blocktype('immediate'), QAST::Op.new( :op('say'), QAST::SVal.new( :value('nested') ) ) ), QAST::Op.new( :op('say'), QAST::SVal.new( :value('after') ) ) ) Here, 'immediate' indicates that this block should be executed immediately, whenever it is reached during program execution. The default, 'declaration', does not have these semantics. You use it when you plan to bind the block, for example into a lexical variable, or you are installing it elsewhere (for example, as a method). Block has one more handy feature: it can be used to maintain a symbol table. It provides you with a hash per symbol (typically, these correspond to the variables you declare in the block, but you can put whatever you want in it). $my_block.symbol('$foo', :scope('lexical'), :readonly(1)); The named parameters you pass are stored in a hash for the symbol '$foo'. If you call it again: $my_block.symbol('$foo', :scope('local'), :optimized(1)); Then the updated value for 'scope' will be put in place, the new 'optimized' value will be stored and the existing 'readonly' value will be left intact. That is to say, you can safely add extra information over time. To get all the known facts about a symbol in a hash, simply do: my %sym := $my_block.symbol('$foo'); Two keys have special significance to the QAST to VM compiler: * **scope** is used to find a default scope for a variable, if none is set in a QAST::Var node that is doing a lookup * **type** is used to know the type of the variable. This matters if the variable is natively typed, since it influences the code generation ## QAST::Stmts and QAST::Stmt Often you will wish to execute a sequence of statements, one after the other. This is what QAST::Stmts is for. It simply contains a sequence of other QAST nodes that will be executed in order. The QAST::Stmts node as a whole will evaluate to the last statement. For example, you may have a program along the lines of: if pints < 4 { say "MORE BEER!"; pint = pint + 1; } There are two statements inside of the if node. This probably compiles to something like: QAST::Op.new( :op('if'), # Here comes the condition. QAST::Op.new( :op('lt_i'), QAST::Var.new( :name('pints') ), QAST::IVal.new( :value(4) ) ), # We want to do multiple statements if the condition is true, # so wrap them in a QAST::Stmts. QAST::Stmts.new( QAST::Op.new( :op('say'), QAST::SVal.new( :value('MORE BEER!') ) ), QAST::Op.new( :op('bind'), QAST::Var.new( :name('pints') ), QAST::Op.new( :op('add_i'), QAST::Var.new( :name('pints') ), QAST::IVal.new( :value(1) ) ) ) ) ) Occasionally, you may want the overall sequence of statements to evaluate to something other than the last child. In this case, use resultchild. QAST::Op.new( :op('say'), QAST::Stmts.new( :resultchild(0), QAST::SVal.new( :value('omg a kangaroo!!!') ), QAST::Op.new( :op('call'), :name('prepare_bbq') ) ) ) Here, the call to 'prepare_bbq' will be run, but the 'say' operation will be given the SVal, not the result of the call. Essentially, resultchild is the zero-based index of which child to use as the result of the QAST::Stmts node overall. There is a variant of QAST::Stmts, which is QAST::Stmt. While the first has no impact on the allocation of temporaries, QAST::Stmt marks a register allocation boundary; beyond it, any temporaries are free to be reused. You do not need to use QAST::Stmt, but it can lead to better code generation if used correctly. Incorrect use can, of course, lead to incorrect code generation. Like QAST::Stmts, it also can have multiple children and supports resultchild. ## QAST::IVal, QAST::NVal and QAST::SVal Perhaps the simplest nodes in QAST, these represent literal, native values. All of them expect the literal to be specified by setting value. QAST::IVal represents an integer literal, QAST::NVal represents a numeric (floating point) literal, and QAST::SVal represents a string literal. QAST::IVal.new( :value(42) ) QAST::NVal.new( :value(3.14) ) QAST::SVal.new( :value('keming') ) And that's about all there is to say about them. Simples! ## QAST::Op This node captures the very general notion of "an operation". Operations range from addition to method calls to exception handling. The operations themselves are documented separately; the form of the node itself is pretty consistent, however. The children are the operands for the operation. For example: QAST::Op.new( :op('mul_i'), QAST::IVal.new( :value(2) ), QAST::IVal.new( :value(21) ) ) Represents an integer multiplication. Some operations also take a name, such as callmethod: QAST::Op.new( # Call the method 'can_haz'... :op('callmethod'), :name('can_haz'), # ...on the object in $i... QAST::Var.new( :name('$i') ), # ...and pass a string argument, kthxbai. QAST::SVal.new( :value('cheezburger') ) ) There are hundreds of operations, and QAST::Op nodes will be amongst the most common in your trees. By the way, the nqp::op(...) syntax in the NQP language is actually just sugar for a QAST::Op node. This means that any op you've used when writing NQP code is now available to you when writing your compiler, which is a nice bit of knowledge re-use. It's almost like we designed this thing! ## QAST::Var The QAST::Var node is used for declaring and lookup up variables. If you are using QAST's handling of parameters, then a QAST::Var node is also used to declare these. At its simplest, a QAST::Var node needs to specify the name of the variable to look up. QAST::Var.new( :name('$bar') ) In this case, the symbol table of the enclosing blocks must supply a scope. A variable can always be explicitly marked with its scope. QAST::Var.new( :name('$bar'), :scope('lexical') ) Variable declarations look similar: QAST::Var.new( :name('$bar'), :scope('lexical'), :decl('var') ) Note that the declaration for each variable should only show up once, and all subsequent usages should not set decl. A declaration evaluates to the variable itself, and can also be bound to. QAST::Op.new( :op('bind'), QAST::Var.new( :name('$x'), :scope('lexical'), :decl('var'), :returns(int) ), QAST::IVal.new( :value(0) ) ) Note also that returns can be used to specify the type of the variable. It is not needed in general, but is important for native types. Note that you should supply a 6model type object for the type, not a string type name! It is only used to determine the type of storage to allocate for the variable, and type constraints besides those implied by the nature of the storage will not be enforced. Aside from lexicals, it is also possible to declare locals. These are not visible from nested blocks. They are cheaper than lexicals as a result of this, but naturally more restricted. A compiler may start out with everything lexical and in an optimization phase turn some of those into locals if it can prove that this will not be problematic for the execution of the program. They look pretty much the same as lexicals otherwise. QAST::Op.new( :op('bind'), QAST::Var.new( :name('$nom'), :scope('local'), :decl('var'), :returns(str) ), QAST::SVal.new( :value('bacon') ) ) While you can rely on any object variables being initialized to null for you, there are no such promises for any of the native types. Thus if your language promises that a fresh integer variable will start with 0, you should set it (once again, possibly optimizing away that initialization if you can prove that a user assignment renders it useless). Parameters work in a similar way; just set decl to 'param' instead of 'var'. For example, the following block takes two positional parameters. QAST::Block.new( QAST::Var.new( :name('$x'), :scope('lexical'), :decl('param') ), QAST::Var.new( :name('$y'), :scope('lexical'), :decl('param') ), ... ) Parameter declarations can also be given: * :default(...) - a QAST tree that produces a default value for the parameter if it is not passed. This makes it optional. * :slurpy(1) - specifies that the parameter is slurpy. Use :named(1) also for a named slurpy parameter. Finally, there are a couple of other values of decl that work with lexicals. * static - means that the lexical should be given the value specified in the :value(...) argument. There are no restrictions on re-binding. * contvar - means that the lexical should be initialized to a clone of the :value(...) argument. Presumably, this represents some kind of container type. There are no restrictions on re-binding. * typevar - means that the implementation is allowed to assume that the type of the variable doesn't change * statevar - same as for contvar, except the container created will be used for all given closure clones. To be clear, cloning a code ref doesn't bring state variables along. On the initial call, containers are formed in the way that contvar forms them: by cloning the :value(...) argument. ## QAST::VarWithFallback In the context of a bind, or with native types, this is exactly the same as a QAST::Var. For fetches of object types, if a null is produced, the QAST tree in :fallback(...) will be run and the value that it evaluates to produced instead. ## QAST::BVal A QAST::Block can only appear once in the QAST tree. So what if you want to refer to a block from elsewhere? The answer is to use a QAST::BVal. QAST::BVal.new( :value($some_block) ) The $some_block should be a QAST::Block. Note that this only works if the block is in the same compilation unit as the one where the BVal is used (though it's hard to think of a typical situation where you'd end up trying to do anything else). ## QAST::WVal This node, known as a World Value, references an object that lives in a serialization context. It may be associated with the current compilation unit or some other compilation unit. QAST::WVal is typically used to talk about objects that you create to represent declarative elements of your program. For example, if the program declares a class, you would create an object describing that type as you compile the program, and then use a QAST::WVal to refer to the type from the compiled program code. Usage looks like: QAST::WVal.new( :value($the_object) ) While obscure at first, QAST::WVal is an extremely powerful tool. Much in NQP and even more in the Rakudo Perl 6 compiler hangs of the notion of bounded serialization and a World that builds up a model of the declarative aspects of a program. ## QAST::Want QAST::Want nodes will appear in the QAST tree whenever you emit a value, but don't yet know in what context it will be used, like 123. Each sub-node of the Want provides a value for one context out of int, str, num, object and void. When a context is known, the last of the matching sub-nodes will be used, or the first one if none match. In the case of 123, it may be a boxed integer or a native integer value: QAST::Want.new( QAST::WVal.new( :value($boxed_constant) ), # default boxed value 'Ii', QAST::IVal.new( :value($the_value) ) # native int value ) The type indicators are `Ii`, `Nn`, and `Ss` for native int, num, and str, respectively. The `v` indicates void context. This notion of context is very code-generation centric, so a want-value of 123 would not create a sub-node for string context, or else my str $x = 123; would work without complaining. ## QAST::ParamTypeCheck Used by rakudo to enforce signatures at runtime. It is placed inside QAST::Vars with a 'param' decls. If the assertion inside returns 0 the 'bind_error' of the current HLL is called. QAST::Var.new( :decl('param'), :scope('lexical') QAST::ParamTypeCheck.new( QAST::Op.new( :op('isconcrete'), QAST::Var.new( :name($name), :scope('local') ) ) ) ) ## QAST::VM --------------------------------------- ## Third-Party Resources * [Perl 6 Core Hacking: QASTalicious blog post](https://rakudo.party/post/Perl-6-Core-Hacking-QASTalicious) nqp-2018.03/docs/release_guide.pod0000644000175000017500000000546113253717146015401 0ustar alexalex=head1 release_guide.pod - guide to NQP releases NQP's development release cycle is intimately tied to Rakudo's release cycle. There is no separate, independent release cycle for NQP; NQP must be tagged for release immediately prior to the Rakudo release. =head2 Steps to create an NQP release =over 4 =item 1. MoarVM tries to have a synchronised release schedule with NQP and Rakudo, so there's most probably a release tag for MoarVM that you can bump F to. Release tags for MoarVM have the format C<2016.02>. Find all of the tags with C in an up-to-date MoarVM checkout. =item 2. Change the C file in nqp: echo '2016.12' > VERSION git commit -m 'bump VERSION to 2012.12' VERSION git push =item 3. Make sure everything compiles and runs from a known clean state. This step is especially important if C was changed in step 1 above. make realclean perl Configure.pl --gen-moar --backend=moar,jvm make make m-test make j-test Resolve any problems that may still exist. Issues that impact backends other than MoarVM may potentially be ignored for a release for now. Check on the IRC channel if you have any questions. You will need JVM installed for the jvm tests to run. On Debian, you can install it with: sudo apt-get install openjdk-7-jdk openjdk-7-jre =item 4. Create a release tarball (needed for the Rakudo Star release) by entering C, where YYYY.MM is the month for which the release is being made. This will create a tarball file named C. =item 5. Unpack the tar file into another area, and test that it builds and runs properly using the same process in step 3 above. If there are any problems, fix them and go back to step 3. =item 6. Tag NQP by its release month ("YYYY.MM") git tag -u -s -a -m "tag release YYYY.MM" YYYY.MM # e.g., 2016.12 git push --tags The C<-s> tells git to sign the tag with your GPG key. Please see Rakudo's release guide for links on how to create GPG keys and upload them to GitHub, should it be necessary. =item 7. Sign the tarball: gpg -b --armor nqp-YYYY.MM.tar.gz =item 8 Upload the release tarball and signature to L and L: scp nqp-2013.12.tar.gz nqp-2013.12.tar.gz.asc \ rakudo@rakudo.org:public_html/downloads/nqp/ scp nqp-2013.12.tar.gz nqp-2013.12.tar.gz.asc \ rakudo@www.p6c.org:public_html/downloads/nqp/ If you do not have permissions for that, ask one of (pmichaud, jnthn, FROGGS, masak, tadzik, moritz, PerlJam/perlpilot, [Coke], lizmat, timotimo, fsergot, hoelzro, Zoffix) on #perl6 or #perl6-dev to do it for you. =item 9. If creating the NQP release as part of a Rakudo release, continue with the Rakudo release process. =back nqp-2018.03/docs/serialization_format.markdown0000644000175000017500000004354113253717146020072 0ustar alexalex# Serialization Format ## Header The header contains a version, followed by offsets (from the start of the data blob) where various tables can be located. +---------------------------------------------------------+ | Version | | 32-bit integer | +---------------------------------------------------------+ | Offset (from start of data) of the dependencies table | | 32-bit integer | +---------------------------------------------------------+ | Number of entries in the dependencies table | | 32-bit integer | +---------------------------------------------------------+ | Offset (from start of data) of the STables table | | 32-bit integer | +---------------------------------------------------------+ | Number of entries in the STables table | | 32-bit integer | +---------------------------------------------------------+ | Offset (from start of data) of the STables data | | 32-bit integer | +---------------------------------------------------------+ | Offset (from start of data) of the objects table | | 32-bit integer | +---------------------------------------------------------+ | Number of entries in the objects table | | 32-bit integer | +---------------------------------------------------------+ | Offset (from start of data) of the objects data | | 32-bit integer | +---------------------------------------------------------+ | Offset (from start of data) of the closures table | | 32-bit integer | +---------------------------------------------------------+ | Number of entries in the closures table | | 32-bit integer | +---------------------------------------------------------+ | Offset (from start of data) of the contexts table | | 32-bit integer | +---------------------------------------------------------+ | Number of entries in the contexts table | | 32-bit integer | +---------------------------------------------------------+ | Offset (from start of data) of the contexts data | | 32-bit integer | +---------------------------------------------------------+ | Offset (from start of data) of the repossessions table | | 32-bit integer | +---------------------------------------------------------+ | Number of entries in the repossessions table | | 32-bit integer | +---------------------------------------------------------+ | Offset (from start of data) of the parameterization | | interns data | | 32-bit integer | +---------------------------------------------------------+ | Number of entries in the parameterization intern data | | 32-bit integer | +---------------------------------------------------------+ ## Dependencies Table This table describes the Serialization Contexts (SC) that are required to already be loaded in order to load this one. The number of entries this table has, is supplied by the header. Each entry looks as follows. +---------------------------------------------------------+ | Index into the string heap of the SC unique ID | | 32-bit integer | +---------------------------------------------------------+ | Index into the string heap of the SC description | | 32-bit integer | +---------------------------------------------------------+ ## STables Table This table describes the 6model STables that have been serialized. Each entry contains the following items. +---------------------------------------------------------+ | Index into the string heap a string holding the name of | | the representation (REPR) that this STable points to. | | 32-bit integer | +---------------------------------------------------------+ | Offset from the start of the STable data chunk where | | the data for this STable has been serialized | | 32-bit integer | +---------------------------------------------------------+ | Offset from the start of the STable data chunk where | | the REPR data for this STable has been serialized (you | | can get there by reading everything from the previous | | offset, but it may not be efficient if you want to get | | an idea of the object size first) | | 32-bit integer | +---------------------------------------------------------+ ## STables Data The STable is serialized just by a sequence of primitives, in the following order. * HOW (object reference) * WHAT (object reference) * WHO (variant) * method_cache (VM hash) * vtable_length (native int) * \[each of the items\] (variant) * type_check_cache_length (native int) * \[each of the items in type_check_cache\] (object reference) * mode_flags (native int) * boolification_spec (native int flag for if it exists; if true, then a native int for the mode and a ref for the method slot) * container_spec (native int flag for if it exists; if true, then ref/string/int for the attribute and ref for the fetch method) * invocation_spec (native int flag for if it exists; if true, then ref for class handle, str for attr name, int for hint and ref for invocation handler) After this, the REPR data is serialized (which is specific to the REPR in question). ## Objects Table This table describes the objects that have been serialized. Each entry contains the following items. +---------------------------------------------------------+ | Base-1 index of the SC that contains the STable for the | | context, or 0 if it is in the current SC. | | 32-bit integer | +---------------------------------------------------------+ | Index in the SC where the STable can be located. | | 32-bit integer | +---------------------------------------------------------+ | Offset from the start of the object data chunk where | | the data for this object has been serialized | | 32-bit integer | +---------------------------------------------------------+ | Flags. Currently, just 1 if it's a normal object and 0 | | if it is a type object. | | 32-bit integer | +---------------------------------------------------------+ The exact data stored for an object is up to its representation. ## Closures Table This table describes the closures we have taken during compilation and that need to be re-instated during deserialization, along with references to their relevant outer contexts. +---------------------------------------------------------+ | Base-1 index of the SC that contains the static code | | reference that this closure is a clone of, or 0 if it | | is in the current SC. | | 32-bit integer | +---------------------------------------------------------+ | Index in that SC where the static code ref is located. | | 32-bit integer | +---------------------------------------------------------+ | 1-based index into the contexts table where the outer | | context for this closure can be found, or zero if there | | is none of interest | | 32-bit integer | +---------------------------------------------------------+ | Flag for if the closure has an associated code object. | | 32-bit integer | +---------------------------------------------------------+ | If it has one, this is the 1-based SC index of the code | | object, or 0 if in the current SC. | | 32-bit integer | +---------------------------------------------------------+ | If it has one, this is the index in that SC where the | | code object can be located. | | 32-bit integer | +---------------------------------------------------------+ ## Contexts Table This table describes the contexts that exist as outer scopes for closures taking during compilation. +---------------------------------------------------------+ | Base-1 index of the SC for the code ref the context is | | associated with, or 0 if it is in the current SC. | | 32-bit integer | +---------------------------------------------------------+ | Index in that SC where the code ref is located. | | 32-bit integer | +---------------------------------------------------------+ | Offset into the contexts data segment where the values | | for the various lexical entries may be found | | 32-bit integer | +---------------------------------------------------------+ | 1-based index into the contexts table where the next | | outer context in the chain can be found, or zero if | | there is none | | 32-bit integer | +---------------------------------------------------------+ ## Repossessions Table This table describes the objects serialized in this SC that were originally owned by another SC, but were taken over by this one due to being modified while it was being compiled. +---------------------------------------------------------+ | Repossessed entity type (0 = object, 1 = STable) | | 32-bit integer | +---------------------------------------------------------+ | Index in our object list where the repossessed object | | is located | | 32-bit integer | +---------------------------------------------------------+ | Base-1 index of the SC that used to own the object (0 | | is not legal here) | | 32-bit integer | +---------------------------------------------------------+ | Index in that SC where the original object is located. | | 32-bit integer | +---------------------------------------------------------+ ## Parameterization Interning Data Parameterized types are interned in a VM instance, meaning that if we deserialize two compilation units that have both serialized identical parameterizations, we will allow the first unit's parameterization to "win". The data in this section of the serialization blob identifies types whose STables and type objects are subject to this interning. This section exclusively contains entries meeting the following rules: * The parametric type must be from another SC * All the parameters must be objects from another SC Each entry starts as follows: +---------------------------------------------------------+ | Base-1 index of the SC that owns the parametric type | | (this is the type that was parameterized to get the one | | we're dealing with the interning of). A value of 0 is | | invalid, as that would imply the current SC. | | 32-bit integer | +---------------------------------------------------------+ | Index in that SC where the parametric type's type | | object can be found | | 32-bit integer | +---------------------------------------------------------+ | Index in our object list where the type object we may | | intern is located. | | 32-bit integer | +---------------------------------------------------------+ | Index in our STable list where the STable we may intern | | is located. | | 32-bit integer | +---------------------------------------------------------+ | The number of parameters in this parameterization | | 32-bit integer | +---------------------------------------------------------+ Following this, for each parameter an object reference is written. ## Primitives This section describes how the various primitive types known to the serializer are stored. ### Native Integers These are stored as 64-bit integers. ### Native Numbers These are stored as 64-bit floating point numbers (doubles). ### Strings These are stored as 32-bit indexes into the strings heap. ### Variant Reference Most times we have a pointer to serialize, we will use a variant reference to do so. The reason being that 1 = NULL 2 = Object reference 3 = VM NULL 4 = VM Boxed Integer 5 = VM Boxed Number 6 = VM Boxed String 7 = VM Array of Variant References 8 = VM Array of Strings 9 = VM Array of Integers 10 = VM Hash of Variant References with String Keys 11 = VM Static Code Reference 12 = VM Cloned Code Reference (a serialized closure) ### Object references These are stored as a 32-bit SC index (base 1 into the dependencies table, or 0 for current SC), followed by a 32-bit index into the selected SC. ### VM NULL We store no extra info for those. ### VM Array of Variants These are stored as an element count as a 32-bit integer, followed by the variants. ### VM Array of Strings These are stored as an element count, followed by the string heap indexes. ### VM Array of Integers These are stored as an element count, followed by the 64-bit integers. ### VM Hash of Variants with String Keys These are stored as an element count. This is followed by that number of string/variant pairs. ### Code References These are always serialized as the SC containing the code reference, plus the index where it can be located. ### VM Static Code Reference The simplest case of code referenced from objects is when the VM has never had to invoke the code during compilation. In this case, the thunk for the dynamic compilation will have been tagged with a STATIC_CODE_REF property and placed into the SC for the current compilation unit. When we encounter such a case, we simply serialize the SC that owns the code object and the code ref index that it is at. Deserialization relies on the deserializer being given a fixed up list of code objects, pointing to the compiled code refs. These are indexed just as the dynamic compilation stubs were, so references to them can be resolved. This also works out in the cross-context case. ### Dynamic Compilation When dynamic compilation is performed, the SC should be updated with the code ref to the now-compiled code. Additionally, this needs to be tagged as a static code reference (and will also be tagged with the SC in question). In the simplest case, we'll never end up referencing this execution of the code - but if closures are taken pointing to it, this will happen, and needs some special handling. ### Closures If we encounter a code object that is not marked as static, but already has been given an SC, then we can simply write the reference out in just the same way as we would for a static code object. This means something has already taken care of the hard work. They are deserializable in a similar way. The interesting part comes when we encounter a code ref that hasn't been tagged with an SC yet. First, we trace back from it to the correct static code ref (probably via the reference back to the static code ref that's held in the static lexical scope info). We make an entry in the closures table indicating the static code ref that needs to be cloned in order to start recreating the closure. Next, we consider the outer. There are two things involved here. One is the context, which represents the lexicals declared in that context. The second is the code object that is associated with the outer. There are some options for this. 1) The outer points to a dynamic compilation boundary (tagged DYN_COMP_BOUNDARY). This means that it is a "fake" frame that exists because the outer scopes beyond here were not done compiling at the point of invocation. We thus don't want to actually do any serialization of this context. 2) The outer points to no interesting outer whatsoever, and represents the point that we should stop doing any serialization. It will be tagged UNIT_BOUNDARY. 3) The outer points to a context with lexicals values that need to be serialized. In this case, it may be an invocation from the static code reference, or it may be an invocation from another closure. nqp-2018.03/docs/using-pod-in-nqp.md0000644000175000017500000000105113253717146015516 0ustar alexalex## Using POD in NQP Limited pod use is acceptable in nqp files. The following forms are currently allowed: ### Single-line entries =identifier ...text... # note that use of 'cut' as an identifier will cause a panic Any word (other than 'cut') is accepted as an identifier. The entry may be used inside a pod block. ### Blocks =begin ...optional identifier... ...optional text... ...any text =end ...optional identifier... If the ```=begin``` has an identifier, the ```=end``` should have the same identifier (not yet enforced). nqp-2018.03/examples/CallFromJava.java0000644000175000017500000000273313253717146016131 0ustar alexalex// nqp$ javac -cp bin/ examples/CallFromJava.java // nqp$ java -cp nqp-runtime.jar:3rdparty/asm/asm-4.1.jar:3rdparty/asm/asm-tree-4.1.jar:. examples.CallFromJava nqp.jar nqp 'say(2+2)' // 4 // rakudo$ java -cp ../nqp-jvm:../nqp-jvm/3rdparty/asm/asm-4.1.jar:../nqp-jvm/3rdparty/asm/asm-tree-4.1.jar:../nqp-jvm/nqp-runtime.jar:rakudo-runtime.jar:. examples.CallFromJava perl6.jar perl6 'say 2 + 2' // 4 // Note: curently nqp only functions properly in the application class path. package examples; import org.perl6.nqp.runtime.*; import static org.perl6.nqp.runtime.CallSiteDescriptor.*; import org.perl6.nqp.sixmodel.*; public class CallFromJava { private GlobalContext g; private ThreadContext t; private SixModelObject nqpComp; private CallFromJava(String bytecode, String hll) { g = new GlobalContext(); t = g.getCurrentThreadContext(); Ops.loadbytecode(bytecode, t); nqpComp = Ops.getcomp(hll, t); } private SixModelObject eval(String nqp) { Ops.invokeDirect(t, Ops.findmethod(t, nqpComp, "compile"), new CallSiteDescriptor(new byte[] { ARG_OBJ, ARG_STR }, null), new Object[] { nqpComp, nqp }); Ops.invokeDirect(t, Ops.result_o(t.resultFrame()), Ops.emptyCallSite, Ops.emptyArgList); return Ops.result_o(t.resultFrame()); } public static void main(String[] args) { CallFromJava nqp = new CallFromJava(args[0], args[1]); nqp.eval(args[2]); } } nqp-2018.03/examples/fib.nqp0000644000175000017500000000032613253717146014241 0ustar alexalex#! nqp sub fib($n) { $n < 2 ?? $n !! fib($n-1) + fib($n - 2); } my $N := 29; my $t0 := nqp::time_n(); my $z := fib($N); my $t1 := nqp::time_n(); say("fib($N) = " ~ fib($N)); say("time = " ~ ($t1-$t0)); nqp-2018.03/examples/hello_world.nqp0000644000175000017500000000006613253717146016014 0ustar alexalex#!nqp say("Hello, awesome Not Quite Perl 6 World!"); nqp-2018.03/examples/loops.nqp0000644000175000017500000000012513253717146014632 0ustar alexalex#! nqp # Example of a while loop my $i := 0; while $i < 10 { say("i={$i++}"); } nqp-2018.03/examples/rubyish/README.md0000644000175000017500000000735413253717146015735 0ustar alexalexnqp/examples/rubyish ==================== Ruby subset extended from the `rubyish-4` example, as introduced in the Edument [Rakudo and NQP internals course](https://github.com/edumentab/rakudo-and-nqp-internals-course). Example usage: ``` % nqp-m rubyish.nqp -e'puts "Hello World!"' % nqp-m rubyish.nqp examples-rubyish/template.rbi % nqp-m rubyish.nqp # repl mode > puts 37 + 5 ``` Implemented: - simple strings 'Hello World!' %q{...} - interpolating strings: "number #{37+5}" %Q{Hello #{planet}!} - quoted words: `%w[aa bb cc]` - basic scoping, including $globals and class @attribute variables - conditional blocks: `if ... then ... elsif ... else ... endif`, `unless..end` - nqp opcode calls: `nqp::sleep(5)` - a few built-ins: `abort`, `print`, `puts`, `sleep` - a couple of methods: `.call` and `.nil?` - infixish assignments: `+=` `-=` `*=` ... - simple classes and objects with attributes. - method inheritance (no mixins yet) - see [inheritance.t](t/inheritance.t) - `while` and `until` loops - statement modifiers `if` `unless`, `while`, `until` e.g.: `puts 42 if true` - basic arrays and hashes - for loops on arrays: `for val in [10, 20, 30] do puts val end` - for loops on hash pairs: `h = {'a'=>10, 'b'=>20}; for kv in h do puts value k end` - lambda blocks/closures: `def make_counter(n,incr) ; n-=incr; lambda { n += incr }; end` - lightweight eRuby like templating, see [template.rbi](examples-rubyish/template.rbi) - heredocs, literal `< puts 42 To run tests: ``` % prove -v -e'nqp-m rubyish.nqp' t ``` Strings and truth values are Perlish rather than Rubyish: - `+` always does addition (doesn't concatenate strings) - `~` has been introduced as the concatenation operator - `>`, `==`, `<=` ... only do arithmetic comparisons - `gt`, `eq`, `le` ... do string comparisons - 0, '0', '' are false in a boolean context. - hash dereferencing is via angle braces: `puts fruit` or curlies `puts fruit{'bananas'}` hash iteration is by pairs. The `key` and `value` built-ins are used for dereferencing ``` for item in {"apples" => 20, "bananas" => 35, "potatos" => 12} puts" #{key item} are #{value item} cents per Kg" end ``` nqp op-codes can be called like regular functions. E.g. ``` puts nqp::if(2+2 == 4, 'yup', 'nope' ) def sprintf(fmt, *args) ; nqp::sprintf(fmt, args) ; end puts sprintf("pid=%s time=%d", nqp::getpid, nqp::time_i) ``` this includes nqp control-flow functions: ``` n = 99 nqp::while n > 0, begin puts "#{n} green bottles standing on the wall" puts "#{n} green bottles standing on the wall" puts "and if one green bottle should accidently fall..." nqp::sleep 1 n -= 1 puts "there'd be #{n} green bottles standing on the wall" nqp::sleep 2 end ``` Rubyish does a limited amout of context sensitive parsing, E.g. ``` yy = 37 puts yy -5 # parsed as an expression; output is 32 def xx(n) ; 37 - n; end puts xx -5 # parsed as a function call; output is 42 ``` This only works only if the functions and methods have been previously declared. If in doubt, use parenthesis for function calls, `capitalize(name)` vs `capitalize name`; and make method calls explicit, `self.fibonacci(n - 1)` vs `fibonacci(n - 1)`. nqp-2018.03/examples/rubyish/examples-rubyish/closure.rbi0000644000175000017500000000043113253717146022116 0ustar alexalexdef gen_times(factor) lambda {|n| n*factor } end times3 = gen_times(3) # 'factor' is replaced with 3 times5 = gen_times(5) puts "3 X 12 = #{times3.call( 12 )}" puts "5 X 5 = #{times5.call( 5 )}" puts "3 X (5 X 4) = #{times3.call( times5.call(4) )}" nqp-2018.03/examples/rubyish/examples-rubyish/fractal-tree.rbi0000644000175000017500000000214313253717146023015 0ustar alexalex <%# # Fractal tree rubyish example. Usage: # % nqp-p rubyish.nqp examples-rubyish/fractal-tree.rbi > fractal.svg # %> <%#---------- def cos(a); nqp::cos_n(a); end def sin(a); nqp::sin_n(a); end @scale = 0.66 @PI = 3.1415926535 @eps = 2.0 def tree(x1, y1, len, angle = 1.5 * @PI) x2 = x1 + len * cos(angle) y2 = y1 + len * sin(angle) sw = len > 20? len / 10 : 2; g = len < @eps*2? 210: 20; puts " " if (len *= @scale) >= @eps then tree(x2, y2, len, angle + 0.2*@PI); tree(x2, y2, len, angle - 0.2*@PI); end end width = 1000 height = 850 tree(x1=width/2, y1=height, length=300) #-----------%> nqp-2018.03/examples/rubyish/examples-rubyish/pi.rbi0000644000175000017500000000077113253717146021061 0ustar alexalex# naive approximation: pi = 4 * (1 - /1/3 + 1/5 ...) LIMIT = 50000 def time() ; nqp::time_n() ; end def sprintf(fmt, *args) ; nqp::sprintf(fmt, args) ; end pi_over_4 = 0.0 print 'hang on a few moments...' start_time = time() # sum terms in reverse order to reduce accumulated error n = LIMIT; while n > 0 do m = 4.0*n - 1.0 pi_over_4 += 1/(m - 2) - 1/m n -= 1 end puts sprintf("(completed %d iterations in %.2f sec)", LIMIT, time() - start_time) pi = pi_over_4 * 4 puts pi nqp-2018.03/examples/rubyish/examples-rubyish/template.rbi0000644000175000017500000000210413253717146022254 0ustar alexalex# eRubyish templating - see http://en.wikipedia.org/wiki/ERuby # There are only three directives: # - Header; remainder of the source file is the template body # <% ... %> - rubyish statements # #{ ... } - inserted content # Any arbritrary code can appear before the template. Output to stdout # is appended to the template, both before and within the template body puts %q{}

Green Bottles...

<%n = 10 %> <%while n > 0 %>
#{n} green bottles standing on the wall, #{n} green bottles standing on the wall
<%# -- gratuitous use of puts ------------------------------ %> <%puts %q{and if one green bottle should accidently fall...} %>
<%n -= 1 %> there'd be #{if n then n else 'no' end} green bottles standing on the wall
<%end%> nqp-2018.03/examples/rubyish/rubyish.nqp0000644000175000017500000007650513253717146016667 0ustar alexalexuse NQPHLL; # Ruby subset extended from the `rubyish` example, as introduced in the # Edument Rakudo and NQP internals course. my %CLASSES; class RubyishClassHOW { has $!name; has $!isa; has %!methods; #| define a new class method new_type(:$name!, :$isa?) { nqp::die("duplicate class definition: $name") if %CLASSES{ $name }; my $obj := self.new(:$name, :$isa); %CLASSES{ $name } := [$obj]; nqp::newtype($obj, 'HashAttrStore'); } #| add a named method to a class method add_method($obj, $name, $code) { nqp::die("This class already has a method named " ~ $name) if nqp::existskey(%!methods, $name); %!methods{$name} := $code; } #| find a named method in a class or its parents #| a '^' prefix, skips the current class, starting at the parent method find_method($obj, $name) { my $method; if nqp::substr($name, 0, 1) eq '^' { $name := nqp::substr($name, 1); } else { $method := %!methods{$name}; } if !$method && $!isa { my $super := %CLASSES{ $!isa }; nqp::die("unresolved super-class: " ~ $!isa) unless $super; $method := $super[0].find_method( $obj, $name); } $method // nqp::null(); } } grammar Rubyish::Grammar is HLL::Grammar { token TOP { :my $*CUR_BLOCK := QAST::Block.new(QAST::Stmts.new()); :my $*TOP_BLOCK := $*CUR_BLOCK; # global top-level block :my $*CLASS_BLOCK := $*CUR_BLOCK; # out class block :my $*IN_TEMPLATE := 0; # true, if in a template :my $*IN_PARENS := 0; # true, if in a parentheised list (signature etc) :my %*SYM; # symbols in current scope :my %*SYM-GBL; # globals and package variables :my %*SYM-CLASS; # class-inherited methods ^ ~ $ || <.panic('Syntax error')> } rule separator { ';' | \n } token continuation { \\ \n } #| a list of statements and/or template expressions rule stmtlist { [ ? ] *%% [<.separator>|] } #| a single statement, plus optional modifier token stmtish {:s [ ]? } token modifier {if|unless|while|until} proto token stmt {*} #| function, or method definition token stmt:sym {:s :my %*inner-sym := nqp::clone(%*SYM); :my $*DEF; 'def' ~ 'end' { %*SYM{$*DEF} := %*inner-sym{$*DEF}; } } rule defbody { :my %*SYM := %*inner-sym; :my $*CUR_BLOCK := QAST::Block.new(QAST::Stmts.new()); { $*DEF := ~$; if $*IN_CLASS { # if we're in a class, we're defining a method ... %*SYM{$*DEF} := 'method'; %*SYM := 'var'; } else { # ... otherwise it's a function %*SYM{$*DEF} := 'func'; } } ['(' ~ ')' ?]? ? } rule comma { [','|'=>'] } #| a signature; for a method, function or closure rule signature { :my $*IN_PARENS := 1; [ | '*' | '&' ] +% ',' } token param {:s [ $=':' ? | '=' ]? { %*SYM{~$} := 'var' } } #| a class definition token stmt:sym { :my $*IN_CLASS := 1; :my @*METHODS; :my %*inner-sym := nqp::clone(%*SYM); [ \h+] ~ [\h* 'end'] } rule classbody { :my %*SYM := %*inner-sym; :my $*CUR_BLOCK := QAST::Block.new(QAST::Stmts.new()); :my $*CLASS_BLOCK := $*CUR_BLOCK; { $*CLASS_BLOCK.name(~$) } [ '<' { inherit-syms(~$) } ]? { %*SYM-CLASS{~$} := %*SYM; } } token stmt:sym { } token term:sym {:s '=' } token code-block {:s :my $*CUR_BLOCK := QAST::Block.new(QAST::Stmts.new()); } our %builtins; #| functions that map directly to nqp ops BEGIN { %builtins := nqp::hash( 'abort', 'die', 'exit', 'exit', 'key', 'iterkey_s', 'delete', 'deletekey', 'value', 'iterval', 'print', 'print', 'puts', 'say', 'sleep', 'sleep', ); } #| a call to a function or method token term:sym { ['(' ~ ')' ? ? |:s )}> ? ] } #| a call to the super-method (aka callsame) token term:sym { 'super' ['(' ~ ')' ? ? |:s ? ] [ || <.panic("'super' call outside of method")> ] } #| call to an nqp operation token term:sym { 'nqp::' ['(' ~ ')' ? | ? ] } #| quoted words, e.g.: %w token term:sym { \%w } token call-args {:s [ ] +% ',' } proto token arg {*} token arg:sym {:s '|':']> } token arg:sym { '&' } token arg:sym { ':' } token arg:sym {:s [ '=>' ]+ % ',' } token paren-args {:my $*IN_PARENS := 1; } token operation {[\!|\?]?} token term:sym { ['new' \h+ | '.' 'new'] ['(' ~ ')' ?]? } # process a variable name, e.g.: localvar $global @attr Pkg::Var # the first reference to a local or global variable must be an assignment token var { :my $*MAYBE_DECL := 0; \+? $=[ $=[ \$ | \@ ] | [ ? '::' ]? > || \( ]> ] [ { $*MAYBE_DECL := 1 }> || ) || ~$ eq '@' }> || ) }> <.panic("unknown variable or method: $")> ] } token term:sym { } token term:sym { \+? } proto token value {*} token value:sym {} token strings {:s ? } token string { # 'non-interpolating' | # "interpolating#{42}" | \%[ q # %q | Q # %Q ] } token value:sym {'<<'} proto token heredoc {*} #| non-interpolating heredoc: <<'MARKER' ... MARKER token heredoc:sym {[$=<.ident> | \' $=.+? \' ]\n $=.*? \n$$$ } #| interpolating heredoc: <<"MARKER" ... MARKER token heredoc:sym {\" $=.+? \" \n [ | ]*? \n$$$ } token heredoc-line {\n? [ \N]+ | \n } #| Interpolation token interp { '#{' ~ '}' [ [:s ] || ] } token quote_escape:sym<#{ }> { } token paren-list { :my $*IN_PARENS := 1; *%% } token value:sym { \d+ } token value:sym { \d* '.' \d+ } token value:sym {'[' ~ ']' } token value:sym {'{' ~ '}' } token value:sym { } token value:sym { } token value:sym { } # Reserved words. token keyword { [ BEGIN | class | ensure | nil | new | when | END | def | false | not | super | while | alias | defined | for | or | then | yield | and | do | if | redo | true | begin | else | in | rescue | undef | break | elsif | module | retry | unless | case | end | next | return | until | eq | ne | lt | gt | le | ge | cmp ] } ## Operator precedence levels # -- see http://www.tutorialspoint.com/ruby/ruby_operators.htmw my %methodop := nqp::hash('prec', 'y=', 'assoc', 'unary'); # y: ** my %exponentiation := nqp::hash('prec', 'y=', 'assoc', 'left'); # x: ! ~ + - (unary) my %unary := nqp::hash('prec', 'x=', 'assoc', 'unary'); # w: * / % my %multiplicative := nqp::hash('prec', 'w=', 'assoc', 'left'); # u: + - my %additive := nqp::hash('prec', 'u=', 'assoc', 'left'); # t: >> << my %bitshift := nqp::hash('prec', 't=', 'assoc', 'left'); # s: & my %bitand := nqp::hash('prec', 's=', 'assoc', 'left'); # r: ^ | my %bitor := nqp::hash('prec', 'r=', 'assoc', 'left'); # q: <= < > >= le lt gt ge my %comparison := nqp::hash('prec', 'q=', 'assoc', 'left'); # n: <=> == === != =~ !~ eq ne cmp my %equality := nqp::hash('prec', 'n=', 'assoc', 'left'); # l: && my %logical_and := nqp::hash('prec', 'l=', 'assoc', 'left'); # k: || my %logical_or := nqp::hash('prec', 'k=', 'assoc', 'left'); # q: ?: my %conditional := nqp::hash('prec', 'g=', 'assoc', 'right'); # f: = %= { /= -= += |= &= >>= <<= *= &&= ||= **= my %assignment := nqp::hash('prec', 'f=', 'assoc', 'right'); # e: not (unary) my %loose_not := nqp::hash('prec', 'e=', 'assoc', 'unary'); # c: or and my %loose_logical := nqp::hash('prec', 'c=', 'assoc', 'left'); # Operators - mostly stolen from NQP token infix:sym<**> { )> } token prefix:sym<-> { ]> )> } token prefix:sym { )> } token infix:sym<*> { )> } token infix:sym { )> } token infix:sym<%> { ]> )> } token infix:sym<+> { )> } token infix:sym<-> { )> } token infix:sym<~> { )> } token infix:sym«<<» { )> } token infix:sym«>>» { )> } token infix:sym<&> { )> } token infix:sym<|> { )> } token infix:sym<^> { )> } token infix:sym«<=» { ]> )> } token infix:sym«>=» { )> } token infix:sym«<» { )> } token infix:sym«>» { )> } token infix:sym«le» { )> } token infix:sym«ge» { )> } token infix:sym«lt» { )> } token infix:sym«gt» { )> } token infix:sym«==» { )> } token infix:sym«!=» { )> } token infix:sym«<=>» { )> } token infix:sym«eq» { )> } token infix:sym«ne» { )> } token infix:sym«cmp» { )> } token infix:sym<&&> { )> } token infix:sym<||> { )> } token infix:sym {:s '?' ':' , :op)> } token bind-op {'='=]>} token infix:sym<=> { <.bind-op> )> } token prefix:sym { )> } token infix:sym { )> } token infix:sym { )> } # Parenthesis token circumfix:sym<( )> { :my $*IN_PARENS := 1; '(' ~ ')' } # Method call token postfix:sym<.> { '.' [ '(' ~ ')' ? ]? } # Array and hash indices token postcircumfix:sym<[ ]> { '[' ~ ']' [ ] } token postcircumfix:sym<{ }> { '{' ~ '}' [ ] } token postcircumfix:sym { } # Statement control rule xblock {:s [ 'then'? | 'then' | >] } token stmt:sym {:s $=[if|unless] ~ 'end' [ [|]? ] } token elsif {:s 'elsif' ~ [|]? } token else {:s 'else' } token stmt:sym {:s $=[while|until] } token stmt:sym {:s 'in' { %*SYM{~$} := 'var' } } token do-block { ~ 'end' } token do {:s 'do'? | 'do' | > } token term:sym { 'begin' ~ 'end' } token closure {:s ['{' ['|' ~ '|' ?]? ] ~ '}' |:s ['do' ['|' ~ '|' ?]? ] ~ 'end' } token closure2 {:s ['(' ~ ')' ? ]? '{' ~ '}' } token term:sym {:s :my $*CUR_BLOCK := QAST::Block.new(QAST::Stmts.new()); ['lambda' | '->' ] } # Comments and whitespace proto token comment {*} token comment:sym { '#' [ \N* || [>\N]*] } token comment:sym {[^^'=begin'\n] [ .*? [^^'=end'[\n|$]] || <.panic('missing ^^=end at eof')>] } token ws { [\h | <.continuation> | <.comment> | \n]* } token hs { [\h | <.continuation> ]* } # Templates token template-chunk { [|] ~ [|$] * } proto token template-nibble {*} token template-nibble:sym { } token template-nibble:sym { [<.tmpl-unesc>|<.tmpl-hdr>] <.panic("Stray tag, e.g. '%>' or ''")> } token template-nibble:sym { [|'#{'|$]> .]+ } token tmpl-hdr {'' \h* \n? {$*IN_TEMPLATE := 1} } token tmpl-esc {\h* '<%' [ || <.panic('Template directive precedes ""')>] } token tmpl-unesc { '%>' \h* \n? [ || <.panic('Template directive precedes ""')>] } # Functions sub callable($op) { my $type := %*SYM{$op} // (%builtins{$op} && 'func'); $type && ($type eq 'func' || $type eq 'method'); } sub variable($op) { my $type := %*SYM{$op} // %*SYM-GBL{$op}; $type && ($type eq 'var'); } sub inherit-syms($class) { if my %syms := %*SYM-CLASS{$class} { %*SYM{$_} := %syms{$_} for %syms; } } } class Rubyish::Actions is HLL::Actions { method TOP($/) { $*CUR_BLOCK.push($.ast); make QAST::CompUnit.new( $*CUR_BLOCK ); } method stmtlist($/) { my $stmts := QAST::Stmts.new( :node($/) ); $stmts.push($_.ast) for @; make $stmts; } method stmtish($/) { make $ ?? QAST::Op.new( $.ast, $.ast, :op(~$), :node($/) ) !! $.ast; } method term:sym($/) { make $.ast; } method code-block($/) { make $.ast } method term:sym($/) { my $name := ~$; my $op := %Rubyish::Grammar::builtins{$name}; my $call; if $op { $call := QAST::Op.new( :op($op) ) } elsif %*SYM{$name} eq 'method' && $*DEF { $call := QAST::Op.new( :op('callmethod'), QAST::Var.new( :name('self'), :scope('lexical')), QAST::SVal.new( :value($name) ), ); } else { $call := QAST::Op.new( :op('call'), :name($name) ); } if $ { $call.push($_) for $.ast; } $call.push( $.ast ) if $; make $call; } method term:sym($/) { my $name := ~$*DEF; my $call := QAST::Op.new( :op('callmethod'), QAST::Var.new( :name('self'), :scope('lexical')), QAST::SVal.new( :value('^' ~ $name) ), ); if $ { $call.push($_) for $.ast; } $call.push( $.ast ) if $; make $call; } method term:sym($/) { my $op := ~$; my $call := QAST::Op.new( :op($op) ); if $ { $call.push($_) for $.ast; } make $call; } method call-args($/) { my @args; @args.push($_.ast) for $; make @args; } method arg:sym($/) { make $.ast } method arg:sym($/) { make $.ast } method arg:sym($/) { my $arg := $.ast; $arg.named( ~$ ); make $arg; } method arg:sym($/) { my $args := QAST::Op.new( :op ); $args.push( $_.ast ) for $; make $args; } method paren-args($/) { make $.ast; } method term:sym($/) { my $tmp-obj := '$new-obj$'; my $init-call := QAST::Op.new( :op, :name, QAST::Var.new( :name($tmp-obj), :scope ) ); if $ { $init-call.push($_) for $.ast; } my $init-block := QAST::Block.new( QAST::Stmts.new( # pseudo-code: # # def new(*call-args) # $new-obj = Class.new; # if call-args then # # always try to call initialize, when new has arguments # $new-obj.initialize(call-args) # else # $new-obj.initialize() \ # if $new-obj.can('initialize') # end # return $new-obj # end # create the new object QAST::Op.new( :op('bind'), QAST::Var.new( :name($tmp-obj), :scope, :decl), QAST::Op.new( :op('create'), QAST::Var.new( :name('::' ~ ~$), :scope('lexical') ) ), ), # call initialize method, if available ($ ?? $init-call !! QAST::Op.new( :op, QAST::Op.new( :op, QAST::Var.new( :name($tmp-obj), :scope ), QAST::SVal.new( :value )), $init-call, ) ), # return the new object QAST::Var.new( :name($tmp-obj), :scope ), )); $init-block.blocktype('immediate'); make $init-block; } method var($/) { my $sigil := ~$ // ''; my $name := ~$; if $sigil eq '@' && $*IN_CLASS && $*DEF { # instance variable, bound to self my $package-name := $*CLASS_BLOCK.name; make QAST::Var.new( :name($name), :scope('attribute'), QAST::Var.new( :name('self'), :scope('lexical')), QAST::SVal.new( :value($package-name) ) ); } else { if $ { my $ns := $ ?? ~$ !! $*CLASS_BLOCK.name; $name := $ns ~ '::' ~ $; } if $*MAYBE_DECL { my $block; my $decl := 'var'; if $sigil eq '$' || $ { $block := $*TOP_BLOCK; %*SYM-GBL{$name} := 'var'; %*SYM{~$} := 'var' if $; } elsif $sigil eq '@' { $block := $*CLASS_BLOCK; } else { $block := $*CUR_BLOCK; } my %sym := $block.symbol($name); if !%sym { %*SYM{$name} := 'var'; $block.symbol($name, :declared(1), :scope('lexical') ); my $var := QAST::Var.new( :name($name), :scope('lexical'), :decl($decl) ); $block[0].push($var); } } make QAST::Var.new( :name($name), :scope('lexical') ); } } method term:sym($/) { make $.ast } method stmt:sym($/) { my $install := $.ast; $*CUR_BLOCK[0].push(QAST::Op.new( :op('bind'), QAST::Var.new( :name($install.name), :scope('lexical'), :decl('var') ), $install )); if $*IN_CLASS { @*METHODS.push($install); } make QAST::Op.new( :op('null') ); } method defbody($/) { $*CUR_BLOCK.name(~$); $*CUR_BLOCK.push($.ast); if $*IN_CLASS { # it's a method, self will be automatically passed $*CUR_BLOCK[0].unshift(QAST::Var.new( :name('self'), :scope('lexical'), :decl('param') )); $*CUR_BLOCK.symbol('self', :declared(1)); } make $*CUR_BLOCK; } method param($/) { my $var := QAST::Var.new( :name(~$), :scope('lexical'), :decl('param') ); $var.named(~$) if $; $*CUR_BLOCK.symbol('self', :declared(1)); $var.default( $.ast ) if $; make $var; } method signature($/) { my @params; @params.push($_.ast) for @; if $ { @params.push($[0].ast); @params[-1].slurpy(1); } if $ { @params.push($[0].ast); @params[-1].default(QAST::Op.new( :op )); } for @params { $*CUR_BLOCK[0].push($_) unless $_.named; $*CUR_BLOCK.symbol($_.name, :declared(1)); } # nqp #179 named arguments need to follow positional parameters for @params { $*CUR_BLOCK[0].push($_) if $_.named; } } method stmt:sym($/) { my $body_block := $.ast; # Generate code to create the class. my $class_stmts := QAST::Stmts.new( $body_block ); my $ins_name := '::' ~ $; my $new_type := QAST::Op.new( :op('callmethod'), :name('new_type'), QAST::WVal.new( :value(RubyishClassHOW) ), QAST::SVal.new( :value(~$), :named('name') ), ); $new_type.push( QAST::SVal.new( :value(~$), :named('isa') ) ) if $; $class_stmts.push(QAST::Op.new( :op('bind'), QAST::Var.new( :name($ins_name), :scope('lexical'), :decl('var') ), $new_type, )); # Add methods. my $class_var := QAST::Var.new( :name($ins_name), :scope('lexical') ); for @*METHODS { my $name := $_.name; $class_stmts.push(QAST::Op.new( :op('callmethod'), :name('add_method'), QAST::Op.new( :op('how'), $class_var ), $class_var, QAST::SVal.new( :value($name) ), QAST::BVal.new( :value($_) )) ); } make $class_stmts; } method classbody($/) { $*CUR_BLOCK.push($.ast); $*CUR_BLOCK.blocktype('immediate'); make $*CUR_BLOCK; } method stmt:sym($/) { make $.ast; } method term:sym($/) { my $op := $.made; make QAST::Op.new( :op('bind'), $.ast, QAST::Op.new( :op($op), $.ast, $.ast )); } method value:sym($/) { make $.ast; } method strings($/) { make $ ?? QAST::Op.new( :op('concat'), $.ast, $.ast) !! $.ast; } method string($/) { make $.ast; } method value:sym($/) { make $.ast } method heredoc:sym($/) { make QAST::SVal.new( :value( ~$ ) ); } method heredoc-line($/) { make QAST::SVal.new( :value(~$/) ) } method heredoc:sym($/) { my $value := QAST::SVal.new( :value('') ); $value := QAST::Op.new( :op, $value, $_.ast) for $; make $value; } method value:sym($/) { make QAST::IVal.new( :value(+$/.Str) ) } method value:sym($/) { make QAST::NVal.new( :value(+$/.Str) ) } method paren-list($/) { my @list; if $ { @list.push($_.ast) for $ } make @list; } method value:sym($/) { my $array := QAST::Op.new( :op ); $array.push($_) for $.ast; make $array; } method term:sym($/) { make $.ast; } method value:sym($/) { my $hash := QAST::Op.new( :op ); $hash.push($_) for $.ast; make $hash; } method value:sym($/) { make QAST::Op.new( :op ); } method value:sym($/) { make QAST::IVal.new( :value<1> ); } method value:sym($/) { make QAST::IVal.new( :value<0> ); } method interp($/) { make $.ast } method quote_escape:sym<#{ }>($/) { make $.ast } method circumfix:sym<( )>($/) { make $.ast } # todo: proper type objects our %call-tab; BEGIN { %call-tab := nqp::hash( 'call', 'call', 'nil?', 'isnull' ) } method postfix:sym<.>($/) { my $op := %call-tab{ ~$ }; my $meth_call := $op ?? QAST::Op.new( :op($op) ) !! QAST::Op.new( :op('callmethod'), :name(~$) ); if $ { $meth_call.push($_) for $.ast; } make $meth_call; } method postcircumfix:sym<[ ]>($/) { make QAST::Var.new( :scope('positional'), $.ast ); } method postcircumfix:sym<{ }>($/) { make QAST::Var.new( :scope('associative'), $.ast ); } method postcircumfix:sym($/) { make QAST::Var.new( :scope('associative'), $.ast ); } method xblock($/) { make QAST::Op.new( $.ast, $.ast, :node($/) ); } method stmt:sym($/) { my $ast := $.ast; $ast.op( ~$ ); $ast.push( $.ast ) if $; make $ast; } method elsif($/) { my $ast := $.ast; $ast.op( 'if' ); $ast.push( $.ast ) if $; make $ast; } method else($/) { make $.ast } method stmt:sym($/) { make QAST::Op.new( $.ast, $.ast, :op(~$), :node($/) ); } method stmt:sym($/) { my $block := QAST::Block.new( QAST::Var.new( :name(~$), :scope('lexical'), :decl('param')), $.ast, ); make QAST::Op.new( $.ast, $block, :op('for'), :node($/) ); } method do-block($/) { make $.ast } method term:sym($/) { make $.ast; } method closure($/) { $*CUR_BLOCK.push($.ast); make QAST::Op.new(:op, $*CUR_BLOCK ); } method closure2($/) { self.closure($/) } method term:sym($/) { make $.ast } method template-chunk($/) { my $text := QAST::Stmts.new( :node($/) ); $text.push( QAST::Op.new( :op, $_.ast ) ) for $; make $text; } method template-nibble:sym($/) { make $.ast } method template-nibble:sym($/) { make QAST::SVal.new( :value(~$/) ); } } class Rubyish::Compiler is HLL::Compiler { method eval($code, *@_args, *%adverbs) { my $output := self.compile($code, :compunit_ok(1), |%adverbs); if %adverbs eq '' { my $outer_ctx := %adverbs; $output := self.backend.compunit_mainline($output); if nqp::defined($outer_ctx) { nqp::forceouterctx($output, $outer_ctx); } $output := $output(); } $output; } } sub MAIN(*@ARGS) { my $comp := Rubyish::Compiler.new(); $comp.language('rubyish'); $comp.parsegrammar(Rubyish::Grammar); $comp.parseactions(Rubyish::Actions); $comp.command_line(@ARGS, :encoding('utf8')); } nqp-2018.03/examples/rubyish/t/00hello-worldish.t0000644000175000017500000000075613253717146020201 0ustar alexalexputs "1..5" def capitalize(s) nqp::uc(nqp::substr(s, 0, 1)) ~ nqp::lc(nqp::substr(s, 1)); end class HelloWorld puts "ok 1 - class immediate code" def initialize(name) puts "ok 2 - initialize called" @name = capitalize name end def sayHi puts "ok 5 - Hello #{@name}!" end end hello = HelloWorld.new("worldish") puts "#{nqp::can(hello, 'sayHi')? 'ok' : 'nok'} 3 - can say Hi" puts "#{nqp::can(hello, 'sayBye')? 'nok' : 'ok'} 4 - can't say Bye" hello.sayHi nqp-2018.03/examples/rubyish/t/arrays.t0000644000175000017500000000113313253717146016374 0ustar alexalexputs "1..13" a=[10,18+2 , 30] a[3] = 40 puts "#{a[0] == 10? 'ok' : 'nok'} 1 - a[0]" puts "#{a[1] == 20? 'ok' : 'nok'} 2 - a[1]" puts "#{a['2'] == 30? 'ok' : 'nok'} 3 - a['2']" puts "#{a[1+2] == 40? 'ok' : 'nok'} 4 - a[1+2]" n=0 for val in a do idx = val / 10 + 4 n += 1 puts "ok #{idx} - array iteration #{n}" end puts "#{%w{nok ok nok}[1]} 9 - quote words" puts "#{['nok',['nok','nok','ok']][1][2]} 10 - 2d array" def test_array_args(x, *arg_arr) puts "ok #{x} - fixed arg" for t in arg_arr do puts "ok #{t} - array slurpy arg" end end test_array_args(11, 12, 13) nqp-2018.03/examples/rubyish/t/bitwise.t0000644000175000017500000000101313253717146016536 0ustar alexalexputs "1..8" puts "#{(not true)? 'nok' : 'ok'} 1 - loose unary 'not'" puts "#{(!true)? 'nok' : 'ok'} 2 - tight unary not '!'" puts "#{37&41 == 33? 'ok' : 'nok'} 3 - infix bit-and '&'" puts "#{138 << 2 == 552? 'ok' : 'nok'} 4 - infix bitshift left '<<'" puts "#{138 >> 2 == 34? 'ok' : 'nok'} 5 - infix bitshift right '>>'" puts "#{138 ^ 107 == 225? 'ok' : 'nok'} 6 - infix xor '^'" puts "#{138 | 107 == 235? 'ok' : 'nok'} 7 - infix or '|'" puts "#{(!2==false and not 2==1+3 and 3==2+1)? 'ok': 'nok'} 8 - precedence" nqp-2018.03/examples/rubyish/t/contextual.t0000644000175000017500000000123613253717146017265 0ustar alexalexputs"1..9" # These tests explore context sensitive parsing of variables vs methods def xx(n=-7) ; 2 - n; end def three ; 3; end def zz(m) three = 3 xx = 1 puts "ok #{xx + m +3} - nested symbols" puts "ok #{three +m +2} - nested symbols" 8 end # in particular, `xx +1` is parsed as a method call, `yy -1` as an expression puts "ok #{xx +1} - method parse" yy = 3 puts "ok #{yy -1} - arithmetic parse" puts "ok #{three} - parameterless method" puts "ok #{three + 1} - parameterless method arithmetic" puts "ok #{three() +2} - parameterless method + paren arithmetic" puts "ok #{zz 2} - method with args" puts "ok #{xx} - parameter defaulting" nqp-2018.03/examples/rubyish/t/functional.t0000644000175000017500000000524713253717146017247 0ustar alexalex# implement and test functional primitives: map, grep and sort. puts '1..9' test_counter = 0 def is(result, expected, description) test_num = (test_counter += 1) passed = result eq expected puts "#{passed ? 'ok' : 'nok'} #{test_num} - #{description}" puts "# expected:'#{expected}' got:'#{result}'" \ unless passed passed end def dump(arr, idx = 1) arr[idx-1] ~ (idx < arr ? ',' ~ dump(arr, idx+1) : '') end # ------------ def grep(array_inp, &selector) array_out = [] n = -1 selector = lambda {|elem| !elem.nil? && elem} \ if selector.nil? for elem in array_inp ; array_out[ n+=1 ] = elem \ if selector.call(elem) end array_out end arr = [0, 2, 10, 1, 13, 27, 5, 4, 18] is dump(arr), '0,2,10,1,13,27,5,4,18', 'array dump sanity' even = grep(arr) {|n| n % 2 == 0} is dump(even), '0,2,10,4,18', 'grep [even]' arr1 = nqp::clone(arr) nqp::push(arr1, nil) is dump( grep(arr1) ), '2,10,1,13,27,5,4,18', 'grep [default]' # ------------ def map(array_inp, &mapper) array_out = [] n = -1 for elem in array_inp do mapping = mapper.call(elem) if nqp::islist(mapping) then # flatten sublists for sub_elem in mapping ; array_out[ n+=1 ] = sub_elem end else array_out[ n+=1 ] = mapping end end array_out end squares = map(arr) {|n| n * n} is dump(squares), '0,4,100,1,169,729,25,16,324', 'map [squares]' # ------------ def sort(list, &func) ; list = nqp::clone list # take a copy func = lambda {|a,b| a cmp b} \ if func.nil? _quicksort(list, 0, nqp::elems(list)-1, &func); end def _quicksort(list, p, r, &func) if p < r then q = _partition(list, p, r, &func) _quicksort(list, p, q-1, &func) _quicksort(list, q+1, r, &func) end list end def _partition(list, p, r, &func) pivot = list[r] i = j = p - 1 while (j += 1) < r _swap(list, i += 1, j) \ if 0 <= func.call(pivot, list[j]) end _swap(list, i += 1, r) i end def _swap(list, i, j) tmp = list[i] list[i] = list[j] list[j] = tmp end str_sort = sort(arr) is dump(str_sort), '0,1,10,13,18,2,27,4,5', 'sort [default]' num_sort = sort(arr) {|a,b| a <=> b} is dump(num_sort), '0,1,2,4,5,10,13,18,27', 'sort [numeric]' # ------------ # put it all together odd = lambda {|n| n % 2 == 1} combined = map(sort grep(arr,&odd)) {|str| nqp::flip(str)} is dump(combined), '1,31,72,5', 'combined map sort grep' splitter = lambda {|n| nqp::split('',n)} is dump( map(combined, &splitter) ), '1,3,1,7,2,5', 'map [flattening]' is dump(arr), '0,2,10,1,13,27,5,4,18', 'array readonly' nqp-2018.03/examples/rubyish/t/hashs.t0000644000175000017500000000113713253717146016205 0ustar alexalexputs "1..11" h = {'a' =>10,'b' => 18+2 , 'c' => 30} h{'d'} = 100 puts "#{h{'a'} == 10? 'ok' : 'nok'} 1 - h{'a'}" puts "#{h == 20? 'ok' : 'nok'} 2 - h" h_idx = 'c' puts "#{h{h_idx} == 30? 'ok' : 'nok'} 3 - h{c_idx}" puts "#{h{'d'} == 100? 'ok' : 'nok'} 4 - h{'d'}" delete(h, 'd') t = 4 for kv in h do puts "ok #{t += 1} - hash key iteration" end h = ['nok','ok'] puts "#{ h[1] } 8 - HoA" def test_hash_args(x, h_args) puts "ok #{ x } - fixed arg" for kv in h_args do puts "ok #{ value kv } - hash slurpy arg (#{key kv})" end end test_hash_args 9, 'z' => 10, 'y' => 11 nqp-2018.03/examples/rubyish/t/if-then-else.t0000644000175000017500000000231313253717146017354 0ustar alexalexputs "1..17" if 1+1==3 then puts "nok 1" puts "nok 2" else puts "ok 1" puts "ok 2" end if 2+2==4 then puts "ok 3" puts "ok 4" else puts "nok 3" puts "nok 4" end if "a" eq "a" && 3+3 == 6; puts "ok 5"; else puts "nok 6"; end if "a"~"a" eq "a""a" && 3+3 == 5; puts "nok 6"; else puts "ok 6"; end if "a" eq "b" || 1+1==3; puts "nok 7"; puts "nok 8" else puts "ok 7"; puts "ok 8"; end if "a" eq "b" || 1+1==2; puts "ok 9" else puts puts "nok 9"; end if "a" eq "b" || 1+1==3; puts "nok 10" else puts "ok 10"; end if 2 >= 1 && 1 <= 2 && "b" gt "a" && "a" lt "b" && (2 != 1) ; puts "ok 11" else puts "nok 11"; end def one_liner(i) if i == 42 then 'nok' elsif i == 12 then 'ok' else 'nok' end end puts "#{one_liner(12)} 12 - single line statement" def iffy(n) if n == 10 13 elsif n == 20 14 elsif n == 30 m = 15 m else 16 end end puts "ok #{iffy 10} - [if] elsif elsif else" puts "ok #{iffy 20} - if [elsif] elsif else" puts "ok #{iffy 30} - if elsif [elsif] else" puts "ok #{iffy 42} - if elsif elsif [else]" x=10 unless x > 2 puts "nok 17 - unless" elsif x == 20 puts "nok 17 - unless (elsif)" else puts "ok 17 - unless" end nqp-2018.03/examples/rubyish/t/infix.t0000644000175000017500000000225713253717146016220 0ustar alexalexputs "1..25" x=1 puts "ok #{x} - assignment" puts "ok #{4/2} - division" puts "ok #{5-2} - subtraction" puts "ok #{2*2} - multiplication" puts "ok #{2+3} - addition" x *= 6 puts "ok #{x} - '*=' assignment" x += 1+1 y = x y -= 1 puts "ok #{y} - '-=' assignment" puts "ok #{x} - '+=' assignment" z = 27 z /= 3 puts "ok #{z} - '/=' assignment" puts "#{1+1==2? 'ok' : 'nok'} 10 - ternary (true)" puts "#{1+1==3? 'nok' : 'ok'} 11 - ternary (false)" puts "#{(false && 2)? 'nok' : 'ok'} 12 - infix &&" xx = nil yy = 42 puts "#{(false || 2) == 2? 'ok' : 'nok'} 13 - infix ||" puts "#{(xx.nil? == true)? 'ok' : 'nok'} 14 - .nil? when true" puts "#{(yy.nil? == false)? 'ok' : 'nok'} 15 - .nil? when false" puts "ok #{2 ** 4} - exponentiation **" puts "ok #{37 % 20} - modulus %" puts "#{1&&false or 1? 'ok' : 'nok'} 18 - loose 'or'" puts "#{1||false and false||1? 'ok' : 'nok'} 19 - loose 'and'" n = 22 for tst in [[20, -1, 'less than'], [22, 0, 'equal'], [24, 1, 'greater than'] ] do puts "#{ (tst[0] <=> n) == tst[1]? 'ok' :'nok'} #{tst[0]} - <=> #{tst[2]}" puts "#{ ('x'~tst[0] cmp 'x'~n) == tst[1]? 'ok' :'nok'} #{tst[0]+1} - cmp #{tst[2]}" end nqp-2018.03/examples/rubyish/t/inheritance.t0000644000175000017500000000174713253717146017377 0ustar alexalexputs "1..10" $tst = -1 class Point def initialize(x, y) puts "ok #{ $tst = $tst + 2 } - Point.initialize called" @x = x @y = y end def x; @x ; end def y; @y ; end def radius; (x * @x + y * @y) ** 0.5; end end class Point3D < Point def initialize(x, y, z) puts "ok 2 - Point3D.initialize called" super(x, y) @z = z end def z; @z ; end def radius; (x * @x + y * @y + z * @z) ** 0.5; end end obj_2d = Point.new(10, 20) obj_3d = Point3D.new(15, 25, 35) puts "#{obj_2d.x == 10 ? 'ok' : 'nok'} 4 - 2d obj x" puts "#{obj_2d.y == 20 ? 'ok' : 'nok'} 5 - 2d obj y" t2 = obj_2d.radius puts "#{t2 > 22 && t2 < 23 ? 'ok' : 'nok'} 6 - radius 2d (approx)" puts "#{obj_3d.x == 15 ? 'ok' : 'nok'} 7 - 3d obj x" puts "#{obj_3d.y == 25 ? 'ok' : 'nok'} 8 - 3d obj y" puts "#{obj_3d.z == 35 ? 'ok' : 'nok'} 9 - 3d obj z" t3 = obj_3d.radius puts "#{t3 > 45 && t3 < 46 ? 'ok' : 'nok'} 10 - radius 3d (approx)" nqp-2018.03/examples/rubyish/t/interpolation.t0000644000175000017500000000101013253717146017754 0ustar alexalexputs "1..10" a = 2 puts "ok #{1} - num" puts "ok #{a} - variable" puts "ok #{a+1} - expression" b = 'ok' puts "#{b} #{3*(a - 1) + 1} - expression" tst=5 puts %q " #{tst}" ' - adjacent' ' strings' puts %Q{ok #{a*=3} - expression - side affect} puts %Q{ok #{(a+=2)-1} - expression - side affect} puts "#{if a==8 then 'ok' else 'nok' end} #{a} - nested statement" puts "#{'#{tst}' eq '#' '{' 'tst}'? 'ok' : 'nok'} 9 - nested starters / stoppers" puts "ok #{ x = 4; x+= 1 x+5 } - multiline interpolation" nqp-2018.03/examples/rubyish/t/lambda.t0000644000175000017500000000064513253717146016322 0ustar alexalexputs "1..7" def make_iterator(n) lambda { |incr,desc| n += incr; puts "ok #{n} - #{desc}"} end it1 = make_iterator(-1) it2 = make_iterator(3) it1.call(2,"iterator 1") it1.call(1,"iterator 1") it2.call(0,"iterator 2") it2.call(1,"iterator 2") it1.call(3,"iterator 1") # Ruby 1.9+ style lambda syntax m = 5 test = ->(desc) {puts "ok #{m+=1} - #{desc}"} test.call("new lamba syntax"); test.call("new lamba syntax"); nqp-2018.03/examples/rubyish/t/line-spanning.t0000644000175000017500000000205413253717146017640 0ustar alexalexputs "1..13" a=[10, 20] puts "#{a[1]? 'ok' : 'nok'} 1 - array spanning lines" =begin This is a multi-line comment and can span as many lines as you like. But =begin and =end should appear at the start of line only. #{exit 42} =end b = [30 # some comments , 40 ] puts "#{b[1]? 'ok' : 'nok'} 2 - array spanning lines" h = {"a" => 10, "b" , 20 , "c" => 30 } puts "#{h == 30? 'ok' : 'nok'} 3 - hash spanning lines" def tricky(k, n, desc) puts "#{k} #{n} - multi-line signatures #{desc}" end tricky('ok',4,"simple call") tricky( "ok", 5, "multi-line call" ) puts \ "ok 6 - \\ line continuation" heredoc = < 5 do puts "ok #{n} - until loop" n += 1 end puts "ok #{n} - postloop" nqp-2018.03/examples/rubyish/t/modifiers.t0000644000175000017500000000060513253717146017057 0ustar alexalexputs "1..10" n=0 puts "ok #{n+=1} - if modifier" if true puts "nok #{n+=1} - if modifier" if false puts "ok #{n+=1} - unless modifier" unless false puts "nok #{n+=1} - unless modifier" unless true puts "ok #{n+=1} - until modifier" until n >= 6 puts "ok #{n+=1} - while modifier" while n < 8 begin puts "ok #{n+=1} - while modifier - begin .. end block" while n < 10 end until n >= 10 nqp-2018.03/examples/rubyish/t/nqp-ops.t0000644000175000017500000000127113253717146016473 0ustar alexalexputs "1..12" nqp::say("ok 1 - nqp::say") nqp::print("ok 2 - nqp::print with new-line\n") puts nqp::concat("ok ", '3 - nqp::concat') puts nqp::substr("nok 4 - nqp::substr", 1) puts nqp::lc(nqp::flip("KO") ~ " 5 - nQp::fLIp and nqp::lC") nqp::print nqp::chr(nqp::ord('o')); puts 'k 6 - nqp::ord and nqp::chr' def ok(i,msg) nqp::say("ok #{i} - #{msg}"); end def nok(i,msg) nqp::say("nok #{i} - #{msg}"); end nqp::if(2+2 == 4, ok(7, "nqp::if (true)"), nok(7, "nqp::if (true)")) nqp::if(2+2 == 5, nok(8, "nqp::if (true)"), ok(8, "nqp::if (false)")) n = 9 nqp::while n <= 11, begin ok(n, "nqp::while (#{n})") n += 1 end puts nqp::sprintf("%s %d - %s", ['ok', 12, 'nqp::sprintf']) nqp-2018.03/examples/rubyish/t/params.t0000644000175000017500000000045613253717146016365 0ustar alexalexputs "1..5" def foo(offset:0,desc:,test=1) puts "ok #{test+offset} - #{desc}" end foo(desc:"postional default") foo(2, desc:"positional - named last") foo(desc:"positional - named first", 3) foo(2, desc:"positional + two named params",offset:2) foo(offset:3, 2, desc:"named + positional (middle)") nqp-2018.03/examples/rubyish/t/recursion.t0000644000175000017500000000110413253717146017102 0ustar alexalexputs "1..2" def fact!(n) if n <= 1 then 1 else n * fact!(n - 1) end end result = fact!(6) expected = 6*5*4*3*2 puts "#{result == expected ? 'ok' : 'nok'} 1 - sub recursion" # ---------- class MethodRecursion def fibonacci(n) n <= 1 \ ? n \ : self.fibonacci( n - 1 ) + fibonacci(n - 2 ) ## 2 styles: ^^ explicit method call ^^ implied method call end end fib = MethodRecursion.new.fibonacci(9) expected2 = 34 puts "#{fib == expected2 ? 'ok' : 'nok'} 2 - method recursion" nqp-2018.03/examples/rubyish/t/rubyish-3-tests.t0000644000175000017500000000026013253717146020060 0ustar alexalexputs "1..4" a = "ok 1" puts a b = "nok 1" b = "ok 2" puts b def pass_a_test puts "ok 3" end pass_a_test() def pass_another(test) puts test end pass_another("ok 4") nqp-2018.03/examples/rubyish/t/rubyish-4-tests.t0000644000175000017500000000041313253717146020061 0ustar alexalexputs "1..5" a = "ok 1" puts a ifb = "nok 1" ifb = "ok 2" puts ifb def pass_a_test puts "ok 3" end pass_a_test() def pass_another(test) puts test end pass_another("ok 4") class Tester def test puts "ok 5" end end t = new Tester() t.test() nqp-2018.03/examples/rubyish/t/scoping.t0000644000175000017500000000423413253717146016542 0ustar alexalexputs "1..19" class BaseClass Y = 16; def set_inst1(v); @i1 = v; end def set_inst2(v); @i2 = v; end def get_inst1; @i1; end def get_inst2; @i2; end def tickle_f(f); @f = f ; end def const_tests puts "ok #{BaseClass::Y} - package constant qualified" puts "ok #{Y + 1} - package constant unqualified" end end class DerivedClass < BaseClass def set_inst1_child(v); @i1 = v; end def get_inst1_child; @i1; end end class DisjointClass Y = 42; def set_inst1(v); @i1 = v; end def set_inst2(v); @i2 = v; end def get_inst1; @i1; end def get_inst2; @i2; end def more_const_tests puts "ok #{BaseClass::Y + 2} - package constant cross reference" puts "ok #{Y} - package constant internal reference" end end a = "ok 1" b = "ok 2" $a = "ok 3" $c = "nok 4" base_obj = BaseClass.new; sibling_obj = BaseClass.new; disjoint_obj = DisjointClass.new; child_obj = DerivedClass.new; def some_sub b = "nok 2" $c = "ok 4" $d = "ok 5" end some_sub() puts "#{a} - local: main" puts "#{b} - local: main, function" puts "#{$a} - $global: main" puts "#{$c} - $global: main, function" puts "#{$d} - $global: function" @e=6 @f='ok' base_obj.tickle_f('nok') puts "ok #{@e} - @instance: main" puts "#{@f} 7 - @instance: main, object" t = 7 base_obj.set_inst1(t+=1) base_obj.set_inst2(t+=1) sibling_obj.set_inst1(t+=1) sibling_obj.set_inst2(t+=1) disjoint_obj.set_inst1(t+=1) disjoint_obj.set_inst2(t+=1) child_obj.set_inst1(t+=1) puts "ok #{base_obj.get_inst1} - @instance access (base)" puts "ok #{base_obj.get_inst2} - @instance access (base)" puts "ok #{sibling_obj.get_inst1} - @instance access (sibling)" puts "ok #{sibling_obj.get_inst2} - @instance access (sibling)" puts "ok #{disjoint_obj.get_inst1} - @instance access (disjoint)" puts "ok #{disjoint_obj.get_inst2} - @instance access (disjoint)" puts "ok #{child_obj.get_inst1} - @instance access (child)" puts "ok #{child_obj.get_inst1_child+1} - @instance access (child)" base_obj.const_tests DisjointClass::Y = t+5 disjoint_obj.more_const_tests nqp-2018.03/examples/rubyish/t/template.t0000644000175000017500000000117513253717146016714 0ustar alexalex# eRubyish style templates puts '1..10' puts "ok 1 - statements before template" ok 2 - initial template text <%n = 2 %> <%while (n+=1) < 5 %>ok #{n} - while loop test <%end%> ok 5 - text between statements <%if n == 5 %>ok 6 - if block (true) <%else %>nok 6 - else block (true) <%end%> <%if n == 1234 %>nok 7 - if block (false) <%elsif n == 20 %>nok 7 - elsif block (false) <%else %>ok 7 - else block (false) <% end %> <%for test in [8, 9] do %>ok #{test} - for loop test <%end%> <%n = 9 %> <%#puts "nok #{n += 1} - commented out directive" %> ok #{n += 1} - final template text nqp-2018.03/examples/webpacked/README.md0000644000175000017500000000122413253717146016163 0ustar alexalexnqp/examples/webpacked ====================== You need to install `webpack`. And then, using `npm`, install `src/vm/js/nqp-loader` and `src/vm/js/nqp-runtime` from here and install `empty-module` and `browserify-bignum` from npm. For increased awesomeness, we use `webpack-dev-server` and `webpack-reload-plugin` so you need to install those, too. Afterwards ``` $ webpack ``` Should bundle up `example.nqp` into `bundle.js`, which is loaded by `index.html`. If you run: ``` $ webpack-dev-server --progress --colors -d ``` A webserver will startup at `localhost:8080` and `example.nqp` will be automatically reloaded and recompiled when you change it. nqp-2018.03/examples/webpacked/example.nqp0000644000175000017500000000133013253717146017055 0ustar alexalexsub js($code) { nqp::getcomp("JavaScript").eval($code); } my &set-draw := js('(function(cb) {window.draw = cb})'); my &set-setup := js('(function(cb) {window.setup = cb})'); my &mouseX := js('(function() {return mouseX})'); my &mouseY := js('(function() {return mouseY})'); my &mouseIsPressed := js('(function() {return mouseIsPressed ? 1 : 0})'); sub setup() { my &fill := js('fill'); my &ellipse := js('ellipse'); my &createCanvas := js('createCanvas'); createCanvas(640, 480); set-draw(-> { if mouseIsPressed() { fill(0); } else { fill(255); } ellipse(mouseX(), mouseY(), 80, 80); }); } set-setup(&setup); say("Hello Fancy Browser World"); nqp-2018.03/examples/webpacked/index.html0000644000175000017500000000037413253717146016706 0ustar alexalex nqp-2018.03/examples/webpacked/webpack.config.js0000644000175000017500000000122513253717146020123 0ustar alexalex//var ReloadPlugin = require("webpack-reload-plugin"); module.exports = { entry: "./example.nqp", nqpRepo: "../..", // Edit this to specify where the nqp-js repo is // plugins: [new ReloadPlugin("localhost")], resolve: { alias: { "ffi": "empty-module", "fs-ext": "empty-module", "ffi": "empty-module", "ref": "empty-module", "nqp-js-io": "empty-module", "sleep": "empty-module", "bignum": "browserify-bignum" } }, output: { path: __dirname, filename: "bundle.js", sourceMapFilename: "bundle.js.map" }, module: { loaders: [ {test: /\.nqp$/, loader: "nqp-loader"} ] } } nqp-2018.03/gen/js/.gitignore0000644000175000017500000000000213253717146014307 0ustar alexalex* nqp-2018.03/gen/jvm/.gitignore0000644000175000017500000000000213253717146014467 0ustar alexalex* nqp-2018.03/gen/moar/.gitignore0000644000175000017500000000000213253717146014631 0ustar alexalex* nqp-2018.03/nqp-js-on-js/.gitignore0000644000175000017500000000000513253717146015361 0ustar alexalex*.js nqp-2018.03/nqp-js-on-js/package.json0000644000175000017500000000043313253717146015664 0ustar alexalex{ "version": "0.1.0", "name": "nqp-js-on-js", "bin": { "nqp-js-on-js": "nqp-bootstrapped.js" }, "licenses": [ { "type": "Artistic 2", "url": "http://opensource.org/licenses/Artistic-2.0" } ], "dependencies": { "nqp-runtime": "0.1.0" } } nqp-2018.03/package.json0000644000175000017500000000062313253717146013431 0ustar alexalex{ "name": "nqp-js-cross-compiling", "bugs": { "url:": "https://github.com/perl6/nqp/issues", "email": "pawelmurias@gmail.com" }, "license": "Artistic-2.0", "private": true, "repository": "http://github.com/perl6/nqp/", "devDependencies": { "eslint": "^3.19.0", "eslint-config-google": "^0.8.0" }, "dependencies": { "nqp-runtime": "file:src/vm/js/nqp-runtime" } } nqp-2018.03/ports/macports/Portfile0000644000175000017500000000307113253717146015651 0ustar alexalex# $Id$ PortSystem 1.0 name nqp version 2015.05 categories lang devel platforms darwin license Artistic-2 maintainers coleda.com:will description A lightweight Perl-6 like language for virtual machines. long_description Unlike a full-fledged implementation of Perl 6, NQP \ strives to have as small a runtime footprint as it can, \ while still providing a Perl 6 object model and regular \ expression engine for the virtual machine. homepage https://github.com/perl6/nqp master_sites http://rakudo.org/downloads/nqp/ # To find the correct checksums for the given release, use: # openssl dgst -rmd160 # openssl dgst -sha256 checksums rmd160 8fc571c56d222b0d57984199643918e54af259c2 \ sha256 790bfc3fa1af7f88f039d170875d69e6e17a9b03cd2119887e5df20834374316 # FIXME: may need ExtUtil::Command if by chance our default perl5 binary # does not match what Apple ships. depends_build port:perl5 # Unsupported by configure.pl. configure.universal_args-delete --disable-dependency-tracking universal_variant yes subport nqp { depends_lib port:MoarVM depends_build port:MoarVM depends_run port:MoarVM configure.cmd ${prefix}/bin/perl Configure.pl --backends=moar --with-moar=${prefix}/bin/moar } subport nqp-jvm { distfiles nqp-${version}.tar.gz configure.cmd ${prefix}/bin/perl Configure.pl --backends=jvm } nqp-2018.03/ports/macports/README0000644000175000017500000000250113253717146015017 0ustar alexalexWhen updating the Portfile to reflect the new release: First, update the portfile 1) update the version 2) remove the revision, if any is currently specified. 3) Recalculate the checksums (commands given in the file for this.) Keep the trailing \ on the first line. Then, test out the portfile locally. 4) Setup a local portfile directory. Edit /opt/local/etc/macports/sources.conf, adding a reference to a local directory, like: file:///Users/bob/sandbox/macports/ 5) In that folder, create a subdir lang/nqp 6) copy the updated Portfile to that folder. 7) Reindex to pick up the new portfile: `portindex` 8) From the top level of that folder, verify that we still build with our declared dependencies with, e.g. `port -t build nqp@2015.04` 9) Install the port, e.g. `sudo port install nqp@2015.04` Finally, open a ticket to update the portfile 10) Create a unified diff from https://trac.macports.org/browser/trunk/dports/lang/nqp/Portfile?format=txt to our most recent copy. 11) create an "update" ticket in http://trac.macports.org/ - you'll need a trac account to open the ticket. Be sure to attach the diff, and list "nqp" as the port. Additionally, set keywords to "haspatch", and if you're listed as a maintainer, "maintainer" 12) git push the updated Portfile 13) hang out in #macports on freenode and mention the ticket. nqp-2018.03/src/HLL/Actions.nqp0000644000175000017500000001751113253717146014475 0ustar alexalexclass HLL::Actions { method perl() { self.HOW.name(self) ~ '.new() #`[' ~ nqp::where(self) ~ ']' } method string_to_int($src, $base) { my $res := nqp::radix($base, $src, 0, 2); $src.panic("'$src' is not a valid number") unless nqp::atpos($res, 2) == nqp::chars($src); nqp::atpos($res, 0); } method ints_to_string($ints) { if nqp::islist($ints) { my $result := ''; for $ints { $result := $result ~ nqp::chr($_.made); } $result; } else { nqp::chr($ints.made); } } method CTXSAVE() { QAST::Stmts.new( QAST::Op.new( :op('bind'), QAST::Var.new( :name('ctxsave'), :scope('local'), :decl('var') ), QAST::Var.new( :name('$*CTXSAVE'), :scope('contextual') ) ), QAST::Op.new( :op('unless'), QAST::Op.new( :op('isnull'), QAST::Var.new( :name('ctxsave'), :scope('local') ) ), QAST::Op.new( :op('if'), QAST::Op.new( :op('can'), QAST::Var.new( :name('ctxsave'), :scope('local') ), QAST::SVal.new( :value('ctxsave') ) ), QAST::Op.new( :op('callmethod'), :name('ctxsave'), QAST::Var.new( :name('ctxsave'), :scope('local') ))))) } method SET_BLOCK_OUTER_CTX($block) { my $outer_ctx := %*COMPILING<%?OPTIONS>; if nqp::defined($outer_ctx) { until nqp::isnull($outer_ctx) { my $pad := nqp::ctxlexpad($outer_ctx); unless nqp::isnull($pad) { for $pad { my str $key := ~$_; unless $block.symbol($key) { my $lextype := nqp::lexprimspec($pad, $key); if $lextype == 0 { $block.symbol($key, :scope, :lazy_value_from($pad)); } elsif $lextype == 1 { $block.symbol($key, :scope, :value(nqp::atkey_i($pad, $key)), :type(int)); } elsif $lextype == 2 { $block.symbol($key, :scope, :value(nqp::atkey_n($pad, $key)), :type(num)); } elsif $lextype == 3 { $block.symbol($key, :scope, :value(nqp::atkey_s($pad, $key)), :type(str)); } } } } $outer_ctx := nqp::ctxouter($outer_ctx); } } } method O($/) { make %*SPEC; } method EXPR($/, $key?) { unless $key { return 0; } my $ast := $/.ast // $.ast; unless $ast { $ast := QAST::Op.new( :node($/) ); if $.made { $ast.op( ~$.made ); } if $key eq 'LIST' { $key := 'infix'; } my $name := nqp::lc($key) ~ ':' ~ ($ ~~ /<[ < > ]>/ ?? '«' ~ $ ~ '»' !! '<' ~ $ ~ '>'); $ast.name('&' ~ $name); unless $ast.op { $ast.op('call'); } } if $key eq 'POSTFIX' { $ast.unshift($/[0].ast); } else { for $/.list { if nqp::defined($_.ast) { $ast.push($_.ast); } } } make $ast; } method term:sym($/) { make $.made } method termish($/) { make $.made; } method nullterm($/) { make NQPMu; } method nullterm_alt($/) { make $.made; } method integer($/) { make $.made; } method dec_number($/) { make +$/; } method decint($/) { make self.string_to_int( $/, 10); } method hexint($/) { make self.string_to_int( $/, 16); } method octint($/) { make self.string_to_int( $/, 8 ); } method binint($/) { make self.string_to_int( $/, 2 ); } method quote_EXPR($/) { my $ast := $.ast; if %*QUOTEMOD { if nqp::istype($ast, QAST::SVal) { my @words := HLL::Grammar::split_words($/, $ast.value); if +@words != 1 { $ast := QAST::Op.new( :op('list'), :node($/) ); for @words { $ast.push(QAST::SVal.new( :value($_) )); } } else { $ast := QAST::SVal.new( :value(~@words[0]) ); } } else { $/.panic("Can't form :w list from non-constant strings (yet)"); } } make $ast; } method quote_delimited($/) { my @parts; my $lastlit := ''; for $ { my $ast := $_.ast; if !nqp::istype($ast, QAST::Node) { $lastlit := $lastlit ~ $ast; } elsif nqp::istype($ast, QAST::SVal) { $lastlit := $lastlit ~ $ast.value; } else { if $lastlit gt '' { @parts.push(QAST::SVal.new( :value($lastlit) )); } @parts.push(nqp::istype($ast, QAST::Node) ?? $ast !! QAST::SVal.new( :value($ast) )); $lastlit := ''; } } if $lastlit gt '' { @parts.push(QAST::SVal.new( :value($lastlit) )); } my $ast := @parts ?? @parts.shift !! QAST::SVal.new( :value('') ); while @parts { $ast := QAST::Op.new( $ast, @parts.shift, :op('concat') ); } make $ast; } method quote_atom($/) { make $ ?? $.made !! ~$/; } method quote_escape:sym($/) { make "\\"; } method quote_escape:sym($/) { make ~$ } method quote_escape:sym($/) { make "\b"; } method quote_escape:sym($/) { make "\n"; } method quote_escape:sym($/) { make "\r"; } method quote_escape:sym($/) { make "\t"; } method quote_escape:sym($/) { make "\c[12]"; } method quote_escape:sym($/) { make "\c[27]"; } method quote_escape:sym($/) { make self.ints_to_string( $ ?? $ !! $ ); } method quote_escape:sym($/) { make self.ints_to_string( $ ?? $ !! $ ); } method quote_escape:sym($/) { make $.made; } method quote_escape:sym<0>($/) { make "\c[0]"; } method quote_escape:sym($/) { make $ ?? '\\' ~ $.Str !! $.Str; } method charname($/) { my $codepoint := $ ?? nqp::chr($.made) !! nqp::getstrfromname(~$/); $/.panic("Unrecognized character name '$/'") if $codepoint eq ''; make $codepoint; } method charnames($/) { my $str := ''; for $ { $str := $str ~ $_.made; } make $str; } method charspec($/) { make $ ?? $.made !! nqp::chr($ ?? nqp::ord($) +^ 64 !! self.string_to_int( $/, 10 ) ); } method comment:sym($/) { my $orig_line := HLL::Compiler.lineof($/.orig(), $/.from(), :cache(1), :directives(0)); $*W.add_comp_line_directive([$orig_line, nqp::radix(10, $, 0, 0)[0], $]); } } nqp-2018.03/src/HLL/CommandLine.nqp0000644000175000017500000002104513253717146015260 0ustar alexalex=begin =head1 NAME HLL::CommandLine - command line parsing tools =head1 SYNOPSIS my $parser := HLL::CommandLine::Parser.new([ 'verbose', 'target=s', 'e=s' ]); # treat the first non-option argument as the program name, # and everything after that as arguments to the program $parser.stop-after-first-arg; # -e "program" also treats everything after it as arguments # to the program: $parser.add-stopper('-e'); my $results := $parser.parse(@*ARGS); my %options := $parser.options; my @args := $parser.arguments; # remaining arguments from @*ARGS =head1 DESCRIPTION HLL::CommandLine::Parser stores a specification of command line options and other behavior, and uses that to parse an array of command line directives. It classifies the directives as I (usually of the form C<-f> or C<--foo>) and I (ie. non-options). The result of a C<.parse(RPA)> call is an C object (or an exception thrown), which makes the options and arguments available via the methods C and C. =head1 HLL::CommandLine::Parser =head2 new(Array) The C<.new> method and constructor expects an array with option specifications. Such a specification is the name of an option, optionally followed by the C<=> equals sign and a single character describing the kind of value it expects. Missing value specification or C stand for C, ie the option does not expect a value. C stands for a string value. Optional values are only supported for string values so far. For the value specified with C the value will default to ''. =head2 add-stopper(String) Adds a stopper. A stopper is a special value that, when encountered in the command line arguments, terminates the processing, and classifies the rest of the strings as arguments, independently of their form. C<--> is a pre-defined stopper. If an option is used a stopper, that option itself is still processed. Examples: yourprogram -a --bar b -- c --foo # options: a = 1, bar = 1 # arguments: b, c, --foo # with stopper -e, and -e expecting a value: yourprogram -a -e foo --bar baz # options: -a = 1, -e = foo # arguments: --bar, baz =head2 parse(Array) Parses the array as command line strings, and returns a C object (or thrown an exception on error). =head1 HLL::CommandLine::Result An object of this type holds the options and arguments from a successful command line parse. =head2 options Returns a hash of options =head2 arguments Return an array of arguments. =end class HLL::CommandLine::Result { has @!arguments; has %!options; method init() { @!arguments := []; %!options := nqp::hash(); } method arguments() { @!arguments } method options() { %!options } method add-argument($x) { nqp::push(@!arguments, $x); } method add-option($name, $value) { # how I miss p6's Hash.push if nqp::existskey(%!options, $name) { if nqp::islist(%!options{$name}) { nqp::push(%!options{$name}, $value); } else { %!options{$name} := [ %!options{$name}, $value ]; } } else { %!options{$name} := $value; } } } class HLL::CommandLine::Parser { has @!specs; has %!options; has %!stopper; has $!stop-after-first-arg; method new(@specs) { my $obj := self.CREATE; $obj.BUILD(specs => @specs); $obj; } method stop-after-first-arg() { $!stop-after-first-arg := 1; } method BUILD(:@specs) { @!specs := nqp::list(); %!options := nqp::hash(); %!stopper := nqp::hash(); %!stopper{'--'} := 1; $!stop-after-first-arg := 0; for @specs { self.add-spec($_); } } method add-stopper($x) { %!stopper{$x} := 1; } method split-option-aliases($s) { nqp::split('|', $s); } method add-spec($s) { my $i := nqp::index($s, '='); my $type; my @options; if $i < 0 { $type := 'b'; @options := self.split-option-aliases($s); } else { $type := nqp::substr($s, $i + 1); @options := self.split-option-aliases(nqp::substr($s, 0, $i)); } for @options { %!options{$_} := $type; } } method is-option($x) { return 0 if $x eq '-' || $x eq '--'; nqp::eqat($x, '-', 0); } method wants-value($x) { my $spec := %!options{$x}; nqp::eqat($spec, 's', 0); } method optional-value($x) { my $spec := %!options{$x}; $spec eq 's?'; } method parse(@args) { my int $i := 0; my int $arg-count := nqp::elems(@args); my $result := HLL::CommandLine::Result.new(); $result.init(); # called when an option expects a value after it sub get-value($opt) { if $i == $arg-count - 1 { nqp::die("Option $opt needs a value"); } else { $i++; @args[$i]; } } # called after a terminator that declares the rest # as not containing any options sub slurp-rest() { $i++; while $i < $arg-count { $result.add-argument(@args[$i]); $i++; } } while $i < $arg-count { my $cur := @args[$i]; if self.is-option($cur) { if nqp::eqat($cur, '--', 0) { # long option my $opt := nqp::substr(@args[$i], 2); my $idx := nqp::index($opt, '='); my $value := 1; my $has-value := 0; if $idx >= 0 { $value := nqp::substr($opt, $idx + 1); $opt := nqp::substr($opt, 0, $idx); $has-value := 1; } elsif self.optional-value($opt) { $value := ''; $has-value := 1; } nqp::die("Illegal option --$opt") unless nqp::existskey(%!options, $opt); nqp::die("Option --$opt does not allow a value") if !self.wants-value($opt) && $has-value; if !$has-value && self.wants-value($opt) { $value := get-value("--$opt"); } $result.add-option($opt, $value); slurp-rest if %!stopper{"--$opt"}; } else { my $opt := nqp::substr($cur, 1); my $len := nqp::chars($opt); if $len == 1 { # not grouped, so it might have a value nqp::die("No such option -$opt") unless %!options{$opt}; if self.wants-value($opt) { $result.add-option($opt, get-value("-$opt")); } else { $result.add-option($opt, 1); } slurp-rest() if %!stopper{"-$opt"}; } else { my $i := 0; while $i < $len { my $o := nqp::substr($opt, $i, 1); if %!options{$o} { if self.wants-value($o) { if $i + 1 == $len { $result.add-option($o, get-value("-$o")); } else { $result.add-option($o, nqp::substr($opt, $i + 1)); } last; } else { $result.add-option($o, 1); } } else { nqp::die("Grouped options '-$opt' contain '$o', which is not a valid option") } $i++; } } } } elsif %!stopper{$cur} { slurp-rest(); } else { $result.add-argument($cur); slurp-rest() if $!stop-after-first-arg; } $i++; } return $result; } } nqp-2018.03/src/HLL/Compiler.nqp0000644000175000017500000005761413253717146014657 0ustar alexalexmy $more-code-sentinel := nqp::hash(); # Provides base functionality for a compiler object. class HLL::Compiler does HLL::Backend::Default { has @!stages; has $!parsegrammar; has $!parseactions; has @!cmdoptions; has $!compiler_progname; has $!language; has %!config; has $!user_progname; has @!cli-arguments; has %!cli-options; has $!backend; has $!save_ctx; method BUILD() { # Backend is set to the default one, by default. $!backend := self.default_backend(); # Default stages. @!stages := nqp::split(' ', 'start parse ast ' ~ $!backend.stages()); # Command options and usage. @!cmdoptions := nqp::split(' ', 'e=s help|h target=s trace|t=s encoding=s output|o=s source-name=s combine version|v show-config verbose-config|V stagestats=s? ll-exception rxtrace nqpevent=s profile=s? profile-compile=s? profile-filename=s profile-stage=s repl-mode=s' #?if js ~ ' substagestats beautify nqp-runtime=s perl6-runtime=s libpath=s shebang execname=s source-map' #?endif ); %!config := nqp::hash(); } method backend(*@value) { if @value { $!backend := @value[0]; @!stages := nqp::split(' ', 'start parse ast ' ~ $!backend.stages()); } $!backend } method language($name?) { if $name { $!language := $name; nqp::bindcomp($name, self); } $!language; } method compiler($name) { nqp::getcomp($name); } method config() { %!config }; method autoprint($value) { self.interactive_result($value) unless nqp::tellfh(nqp::getstdout()) > $*AUTOPRINTPOS; } method readline($stdin, $stdout, $prompt) { $stdout.print($prompt); return $stdin.get; } method context() { $!save_ctx # XXX starting value? } method interactive(*%adverbs) { stderr().print(self.interactive_banner); my $stdin := stdin(); my $stdout := stdout(); my $encoding := ~%adverbs; if $encoding && $encoding ne 'fixed_8' { $stdin.set-encoding($encoding); } my $target := nqp::lc(%adverbs); my $prompt := self.interactive_prompt // '> '; my $code; while 1 { last if $stdin.eof; my str $newcode := self.readline($stdin, $stdout, ~$prompt); if nqp::isnull_s($newcode) || !nqp::defined($newcode) { nqp::print("\n"); last; } if $newcode { $code := $code ~ $newcode; } # Set the current position of stdout for autoprinting control my $*AUTOPRINTPOS := nqp::tellfh(nqp::getstdout()); my $*CTXSAVE := self; my $*MAIN_CTX; if $code { $code := $code ~ "\n"; my $output; { $output := self.eval($code, :outer_ctx($!save_ctx), |%adverbs); CATCH { self.interactive_exception($!); } }; if self.input-incomplete($output) { # Need to get more code before we execute # Strip the trailing \, but reinstate the newline if nqp::eqat($code, "\\\n", 0) { $code := nqp::substr($code, 0, nqp::chars($code) - 2) ~ "\n"; } if $code { $prompt := '* '; } next; } if nqp::defined($*MAIN_CTX) { $!save_ctx := $*MAIN_CTX; } $code := ""; $prompt := self.interactive_prompt // '> '; next unless nqp::defined($output); next if nqp::isnull($output); if !$target { self.autoprint($output); } elsif $!backend.is_textual_stage($target) { nqp::say($output); } else { self.dumper($output, $target, |%adverbs); } } } } method interactive_result($value) { nqp::say(~$value) } method interactive_exception($ex) { nqp::print(~$ex ~ "\n") } method input-incomplete($value) { nqp::where($value) == nqp::where($more-code-sentinel); } method needs-more-input() { $more-code-sentinel } method eval($code, *@args, *%adverbs) { my $output; if $code && nqp::eqat($code, "\\\n", nqp::chars($code) - 2) { return self.needs-more-input(); } if nqp::existskey(%adverbs, 'profile-compile') { $output := $!backend.run_profiled({ self.compile($code, :compunit_ok(1), |%adverbs); }, %adverbs, %adverbs); } else { $output := self.compile($code, :compunit_ok(1), |%adverbs); } if $!backend.is_compunit($output) && %adverbs eq '' { my $outer_ctx := %adverbs; $output := $!backend.compunit_mainline($output); if nqp::defined($outer_ctx) { nqp::forceouterctx($output, $outer_ctx); } if nqp::existskey(%adverbs, 'profile') { $output := $!backend.run_profiled({ $output(|@args) }, %adverbs, %adverbs); } elsif %adverbs { $output := $!backend.run_traced(%adverbs, { $output(|@args) }); } else { $output := $output(|@args); } } $output; } method ctxsave() { $*MAIN_CTX := nqp::ctxcaller(nqp::ctx()); $*CTXSAVE := 0; } method panic(*@args) { nqp::die(join('', @args)) } method stages(@value?) { if +@value { @!stages := @value; } @!stages; } method parsegrammar(*@value) { if +@value { $!parsegrammar := @value[0]; } $!parsegrammar; } method parseactions(*@value) { if +@value { $!parseactions := @value[0]; } $!parseactions; } method interactive_banner() { '' } method interactive_prompt() { '> ' } method compiler_progname($value?) { if nqp::defined($value) { $!compiler_progname := $value; } $!compiler_progname; } method commandline_options(@value?) { if +@value { @!cmdoptions := @value; } @!cmdoptions; } method command_line(@args, *%adverbs) { my $program-name := @args[0]; my $res := self.process_args(@args); my %opts := $res.options; my @a := $res.arguments; for %opts { %adverbs{$_.key} := $_.value; } self.usage($program-name) if %adverbs || %adverbs; if $!backend.is_precomp_stage(%adverbs) { %adverbs := 1; } my $*PERL6_RUNTIME; if %adverbs { $*PERL6_RUNTIME := %adverbs; } my $*LIBPATH; if %adverbs { $*LIBPATH := nqp::split('|||', %adverbs); nqp::getcomp('JavaScript').eval('(function(paths) {nqp.libpath(paths.array)})')($*LIBPATH); } my $*EXECNAME; if %adverbs { $*EXECNAME := %adverbs; } self.command_eval(|@a, |%adverbs); } method command_eval(*@a, *%adverbs) { self.version if %adverbs || %adverbs; self.verbose-config if %adverbs || %adverbs || %adverbs; self.nqpevent(%adverbs) if %adverbs; my $result; my $error; my $has_error := 0; my $target := nqp::lc(%adverbs); try { { if nqp::defined(%adverbs) { $!user_progname := '-e'; my $?FILES := '-e'; $result := self.eval(%adverbs, '-e', |@a, |%adverbs); unless $target eq '' || $!backend.is_textual_stage($target) || %adverbs { self.dumper($result, $target, |%adverbs); } } elsif !@a { # Is STDIN a TTY display? If so, start the REPL, otherwise, simply # assume the program to eval is given on STDIN. my $force := %adverbs//''; my $wants-interactive := $force ?? $force eq 'interactive' ?? 1 !! $force eq 'non-interactive' ?? 0 !! self.panic( "Unknown REPL mode '$force'. Valid values" ~ " are 'non-interactive' and 'interactive'" ) !! stdin().t(); $result := $wants-interactive ?? self.interactive(|%adverbs) !! self.evalfiles('-', |%adverbs); } elsif %adverbs { $result := self.evalfiles(@a, |%adverbs) } else { $result := self.evalfiles(@a[0], |@a, |%adverbs) } if !nqp::isnull($result) && ($!backend.is_textual_stage($target) || %adverbs) { my $output := %adverbs; my $fh := ($output eq '' || $output eq '-') ?? stdout() !! open($output, :w); self.panic("Cannot write to $output") unless $fh; $fh.print($result); $fh.flush(); close($fh) unless ($output eq '' || $output eq '-'); } CONTROL { if nqp::can(self, 'handle-control') { self.handle-control($_); } else { nqp::rethrow($_); } } } CATCH { $has_error := 1; $error := $_; } } if ($has_error) { if %adverbs || !nqp::can(self, 'handle-exception') { my $err := stderr(); my $message := nqp::getmessage($error); my $payload := nqp::getpayload($error); if nqp::isnull_s($message) && nqp::can($payload, 'message') { $message := $payload.message; } $err.say($message); $err.say(nqp::join("\n", nqp::backtracestrings($error))); nqp::exit(1); } else { self.handle-exception($error); } } $result; } method process_args(@args) { # First argument is the program name. self.compiler_progname(@args.shift); my $p := HLL::CommandLine::Parser.new(@!cmdoptions); $p.add-stopper('-e'); $p.stop-after-first-arg; my $res; try { $res := $p.parse(@args); CATCH { note($_); self.usage(:use-stderr); nqp::exit(1); } } if $res { %!cli-options := $res.options(); @!cli-arguments := $res.arguments(); } else { %!cli-options := nqp::hash(); @!cli-arguments := []; } $res; } method evalfiles($files, *@args, *%adverbs) { my $target := nqp::lc(%adverbs); my $encoding := %adverbs; my @files := nqp::islist($files) ?? $files !! [$files]; $!user_progname := join(',', @files); my @codes; for @files -> $filename { my $err := 0; my $in-handle; try { if $filename eq '-' { $in-handle := stdin(); } elsif nqp::stat($filename, nqp::const::STAT_ISDIR) { note("Can not run directory $filename."); $err := 1; } else { $in-handle := open($filename, :r, :enc($encoding)); } CATCH { note("Could not open $filename. $_"); $err := 1; } } nqp::exit(1) if $err; try { nqp::push(@codes, $in-handle.slurp()); $in-handle.close; CATCH { note("Error while reading from file: $_"); $err := 1; } } nqp::exit(1) if $err; } my $code := join('', @codes); my $?FILES := %adverbs || join(' ', @files); my $r := self.eval($code, |@args, |%adverbs); if $target eq '' || $!backend.is_textual_stage($target) || %adverbs { return $r; } else { return self.dumper($r, $target, |%adverbs); } } method exists_stage($stage) { my $found := 0; for self.stages() { if $_ eq $stage { return 1; } } return 0; } method execute_stage($stage, $result, %adverbs) { if nqp::can($!backend, $stage) { $!backend."$stage"($result, |%adverbs); } elsif nqp::can(self, $stage) { self."$stage"($result, |%adverbs); } else { nqp::die("Unknown compilation stage '$stage'"); } } method compile($source, :$from, :$lineposcache, *%adverbs) { my %*COMPILING<%?OPTIONS> := %adverbs; my $*LINEPOSCACHE := $lineposcache; my $target := nqp::lc(%adverbs); my $result := $source; my $stderr := stderr(); my $stdin := stdin(); my $stagestats := %adverbs; unless $from eq '' || self.exists_stage($from) { nqp::die("Unknown compilation input '$from'"); } unless $target eq '' || self.exists_stage($target) { nqp::die("Unknown compilation target '$target'"); } for self.stages() { if $from ne '' { if $_ eq $from { $from := ''; } next; } $stderr.print(nqp::sprintf("Stage %-11s: ", [$_])) if nqp::defined($stagestats); my $timestamp := nqp::time_n(); my sub run() { self.execute_stage($_, $result, %adverbs) } $result := %adverbs eq $_ ?? $!backend.run_profiled(&run, '', %adverbs) !! run(); my $diff := nqp::time_n() - $timestamp; if nqp::defined($stagestats) { $stderr.print(nqp::sprintf("%7.3f", [$diff])); $!backend.force_gc() if nqp::bitand_i($stagestats, 0x4); $stderr.print($!backend.vmstat()) if nqp::bitand_i($stagestats, 0x2); $stderr.print("\n"); if nqp::bitand_i($stagestats, 0x8) { $stderr.print("continue> "); $stdin.get; } } last if $_ eq $target; } if %adverbs { return $result } else { return $!backend.compunit_mainline($result); } } method start($source, *%adverbs) { $source; } method parse($source, *%adverbs) { my $s := $source; my $grammar; my $actions; if %adverbs { $s := $!backend.apply_transcodings($s, %adverbs); } my $outer_ctx := %adverbs; if nqp::existskey(%adverbs, 'grammar') { $grammar := %adverbs; $actions := %adverbs; } else { $grammar := self.parsegrammar; $actions := self.parseactions; } $grammar.HOW.trace-on($grammar) if %adverbs; my $match := $grammar.parse($s, p => 0, actions => $actions); $grammar.HOW.trace-off($grammar) if %adverbs; self.panic('Unable to parse source') unless $match; return $match; } method ast($source, *%adverbs) { my $ast := $source.ast(); self.panic("Unable to obtain AST from " ~ $source.HOW.name($source)) unless nqp::istype($ast, QAST::Node); $ast; } method dumper($obj, $name, *%options) { if nqp::can($obj, 'dump') { my $out := stdout(); $out.print($obj.dump()); $out.flush(); } else { nqp::die("Cannot dump this object; no dump method"); } } method usage($name?, :$use-stderr = False) { my $print-func := $use-stderr ?? ¬e !! &say; # RT #130760 if $name { $print-func($name); } my $usage := "This compiler is based on HLL::Compiler.\n\nOptions:\n"; for @!cmdoptions { $usage := $usage ~ " $_\n"; } $print-func($usage); nqp::exit(0); } method version() { my $version := %!config; my $backver := $!backend.version_string(); my $implementation := self.implementation(); my $language_name := self.language_name(); if nqp::can(self, 'language_version') { nqp::say("This is $implementation version $version built on $backver\n" ~ "implementing $language_name " ~ self.language_version() ~ "."); } else { nqp::say("This is $implementation version $version built on $backver"); } nqp::exit(0); } method implementation() { $!language } method language_name() { $!language } method show-config() { self.verbose-config(); } method verbose-config() { my $bname := $!backend.name; for $!backend.config { nqp::say($bname ~ '::' ~ $_.key ~ '=' ~ $_.value); } for %!config { nqp::say($!language ~ '::' ~ $_.key ~ '=' ~ $_.value); } nqp::exit(0); } method nqpevent(*@pos) { $!backend.nqpevent(|@pos) } method removestage($stagename) { my @new_stages := nqp::list(); for @!stages { if $_ ne $stagename { nqp::push(@new_stages, $_); } } @!stages := @new_stages; } method addstage($stagename, *%adverbs) { my $position; my $where; if %adverbs { $where := %adverbs; $position := 'before'; } elsif %adverbs { $where := %adverbs; $position := 'after'; } else { my @new-stages := nqp::clone(self.stages); nqp::push(@new-stages, $stagename); self.stages(@new-stages); return 1; } my @new-stages := nqp::list(); for self.stages { if $_ eq $where { if $position eq 'before' { nqp::push(@new-stages, $stagename); nqp::push(@new-stages, $_); } else { nqp::push(@new-stages, $_); nqp::push(@new-stages, $stagename); } } else { nqp::push(@new-stages, $_) } } self.stages(@new-stages); } method parse_name($name) { my @ns := nqp::split('::', $name); my $sigil := nqp::substr(@ns[0], 0, 1); # move any leading sigil to the last item my $idx := nqp::index('$@%&', $sigil); if $idx >= 0 { @ns[0] := nqp::substr(@ns[0], 1); @ns[-1] := $sigil ~ @ns[-1]; } # remove any empty items from the list # maybe replace with a grep() once we have the setting for sure my @actual_ns; for @ns { nqp::push(@actual_ns, $_) unless $_ eq ''; } @actual_ns; } method line_and_column_of($target, int $pos, int :$cache = 0) { my $linepos; if $cache { # if we've previously cached c for target, we use it. $linepos := $*LINEPOSCACHE; } unless nqp::defined($linepos) { # calculate a new linepos array. $linepos := nqp::list_i(); if $cache { $*LINEPOSCACHE := $linepos; } my str $s := ~$target; my int $eos := nqp::chars($s); my int $jpos := 0; my int $ord; # search for all of the newline markers in c. when we # find one, mark the ending offset of the line in c. while nqp::islt_i($jpos := nqp::findcclass(nqp::const::CCLASS_NEWLINE, $s, $jpos, $eos), $eos) { $ord := nqp::ord($s, $jpos); $jpos := nqp::add_i($jpos, 1); nqp::push_i($linepos, $jpos); # Treat \r\n as a single logical newline. Note that NFG # implementations, we should check it really is a lone \r, # not the first bit of a \r\n grapheme. if nqp::iseq_i($ord, 13) && nqp::eqat($s, "\r", $jpos - 1) && $jpos < $eos && nqp::iseq_i(nqp::ord($s, $jpos), 10) { $jpos := nqp::add_i($jpos, 1); } } } # We have c, so now we (binary) search the array # for the largest element that is not greater than c. my int $lo := 0; my int $hi := nqp::elems($linepos); my int $line; while nqp::islt_i($lo, $hi) { $line := nqp::div_i(nqp::add_i($lo, $hi), 2); if nqp::isgt_i(nqp::atpos_i($linepos, $line), $pos) { $hi := $line; } else { $lo := nqp::add_i($line, 1); } } my $column := nqp::iseq_i($lo, 0) ?? $pos !! nqp::sub_i($pos, nqp::atpos_i($linepos, nqp::sub_i($lo, 1))); nqp::list_i(nqp::add_i($lo, 1), nqp::add_i($column, 1)); } method lineof($target, int $pos, int :$cache = 0, int :$directives = 0) { self.linefileof($target, $pos, :$cache, :$directives)[0] } method linefileof($target, int $pos, int :$cache = 0, int :$directives = 0) { my int $line := nqp::atpos_i(self.line_and_column_of($target, $pos, :$cache), 0); my str $file := ''; if $directives && (my @clds := @*comp_line_directives) { my int $i := nqp::elems(@clds); while $i > 0 { $i := $i - 1; last if $line > @clds[$i][0]; } if $line > @clds[$i][0] { my @directive := @clds[$i]; $line := $line - @directive[0] + @directive[1] - 1; $file := @directive[2]; } } [$line, $file]; } # the name of the file(s) that are executed, or -e or 'interactive' method user-progname() { $!user_progname // 'interactive' } # command line options and arguments as provided by the user method cli-options() { %!cli-options } method cli-arguments() { @!cli-arguments } # set a recursion limit, if the backend supports it method recursion_limit($limit) { if nqp::can($!backend, 'recursion_limit') { $!backend.recursion_limit($limit); 1; } else { 0; } } } my $compiler := HLL::Compiler.new(); $compiler.language($compiler.backend.name); nqp-2018.03/src/HLL/Grammar.nqp0000644000175000017500000005643113253717146014467 0ustar alexalexuse QRegex; grammar HLL::Grammar { my $brackets := '<>[](){}' ~ "\x[0028]\x[0029]\x[003C]\x[003E]\x[005B]\x[005D]" ~ "\x[007B]\x[007D]\x[00AB]\x[00BB]\x[0F3A]\x[0F3B]\x[0F3C]\x[0F3D]\x[169B]\x[169C]" ~ "\x[2018]\x[2019]\x[201A]\x[2019]\x[201B]\x[2019]\x[201C]\x[201D]\x[201E]\x[201D]" ~ "\x[201F]\x[201D]\x[2039]\x[203A]\x[2045]\x[2046]\x[207D]\x[207E]\x[208D]\x[208E]" ~ "\x[2208]\x[220B]\x[2209]\x[220C]\x[220A]\x[220D]\x[2215]\x[29F5]\x[223C]\x[223D]" ~ "\x[2243]\x[22CD]\x[2252]\x[2253]\x[2254]\x[2255]\x[2264]\x[2265]\x[2266]\x[2267]" ~ "\x[2268]\x[2269]\x[226A]\x[226B]\x[226E]\x[226F]\x[2270]\x[2271]\x[2272]\x[2273]" ~ "\x[2274]\x[2275]\x[2276]\x[2277]\x[2278]\x[2279]\x[227A]\x[227B]\x[227C]\x[227D]" ~ "\x[227E]\x[227F]\x[2280]\x[2281]\x[2282]\x[2283]\x[2284]\x[2285]\x[2286]\x[2287]" ~ "\x[2288]\x[2289]\x[228A]\x[228B]\x[228F]\x[2290]\x[2291]\x[2292]\x[2298]\x[29B8]" ~ "\x[22A2]\x[22A3]\x[22A6]\x[2ADE]\x[22A8]\x[2AE4]\x[22A9]\x[2AE3]\x[22AB]\x[2AE5]" ~ "\x[22B0]\x[22B1]\x[22B2]\x[22B3]\x[22B4]\x[22B5]\x[22B6]\x[22B7]\x[22C9]\x[22CA]" ~ "\x[22CB]\x[22CC]\x[22D0]\x[22D1]\x[22D6]\x[22D7]\x[22D8]\x[22D9]\x[22DA]\x[22DB]" ~ "\x[22DC]\x[22DD]\x[22DE]\x[22DF]\x[22E0]\x[22E1]\x[22E2]\x[22E3]\x[22E4]\x[22E5]" ~ "\x[22E6]\x[22E7]\x[22E8]\x[22E9]\x[22EA]\x[22EB]\x[22EC]\x[22ED]\x[22F0]\x[22F1]" ~ "\x[22F2]\x[22FA]\x[22F3]\x[22FB]\x[22F4]\x[22FC]\x[22F6]\x[22FD]\x[22F7]\x[22FE]" ~ "\x[2308]\x[2309]\x[230A]\x[230B]\x[2329]\x[232A]\x[23B4]\x[23B5]\x[2768]\x[2769]" ~ "\x[276A]\x[276B]\x[276C]\x[276D]\x[276E]\x[276F]\x[2770]\x[2771]\x[2772]\x[2773]" ~ "\x[2774]\x[2775]\x[27C3]\x[27C4]\x[27C5]\x[27C6]\x[27D5]\x[27D6]\x[27DD]\x[27DE]" ~ "\x[27E2]\x[27E3]\x[27E4]\x[27E5]\x[27E6]\x[27E7]\x[27E8]\x[27E9]\x[27EA]\x[27EB]" ~ "\x[2983]\x[2984]\x[2985]\x[2986]\x[2987]\x[2988]\x[2989]\x[298A]\x[298B]\x[298C]" ~ "\x[298D]\x[2990]\x[298F]\x[298E]\x[2991]\x[2992]\x[2993]\x[2994]\x[2995]\x[2996]" ~ "\x[2997]\x[2998]\x[29C0]\x[29C1]\x[29C4]\x[29C5]\x[29CF]\x[29D0]\x[29D1]\x[29D2]" ~ "\x[29D4]\x[29D5]\x[29D8]\x[29D9]\x[29DA]\x[29DB]\x[29F8]\x[29F9]\x[29FC]\x[29FD]" ~ "\x[2A2B]\x[2A2C]\x[2A2D]\x[2A2E]\x[2A34]\x[2A35]\x[2A3C]\x[2A3D]\x[2A64]\x[2A65]" ~ "\x[2A79]\x[2A7A]\x[2A7D]\x[2A7E]\x[2A7F]\x[2A80]\x[2A81]\x[2A82]\x[2A83]\x[2A84]" ~ "\x[2A8B]\x[2A8C]\x[2A91]\x[2A92]\x[2A93]\x[2A94]\x[2A95]\x[2A96]\x[2A97]\x[2A98]" ~ "\x[2A99]\x[2A9A]\x[2A9B]\x[2A9C]\x[2AA1]\x[2AA2]\x[2AA6]\x[2AA7]\x[2AA8]\x[2AA9]" ~ "\x[2AAA]\x[2AAB]\x[2AAC]\x[2AAD]\x[2AAF]\x[2AB0]\x[2AB3]\x[2AB4]\x[2ABB]\x[2ABC]" ~ "\x[2ABD]\x[2ABE]\x[2ABF]\x[2AC0]\x[2AC1]\x[2AC2]\x[2AC3]\x[2AC4]\x[2AC5]\x[2AC6]" ~ "\x[2ACD]\x[2ACE]\x[2ACF]\x[2AD0]\x[2AD1]\x[2AD2]\x[2AD3]\x[2AD4]\x[2AD5]\x[2AD6]" ~ "\x[2AEC]\x[2AED]\x[2AF7]\x[2AF8]\x[2AF9]\x[2AFA]\x[2E02]\x[2E03]\x[2E04]\x[2E05]" ~ "\x[2E09]\x[2E0A]\x[2E0C]\x[2E0D]\x[2E1C]\x[2E1D]\x[2E20]\x[2E21]\x[2E28]\x[2E29]" ~ "\x[3008]\x[3009]\x[300A]\x[300B]\x[300C]\x[300D]\x[300E]\x[300F]\x[3010]\x[3011]" ~ "\x[3014]\x[3015]\x[3016]\x[3017]\x[3018]\x[3019]\x[301A]\x[301B]\x[301D]\x[301E]" ~ "\x[FE17]\x[FE18]\x[FE35]\x[FE36]\x[FE37]\x[FE38]\x[FE39]\x[FE3A]\x[FE3B]\x[FE3C]" ~ "\x[FE3D]\x[FE3E]\x[FE3F]\x[FE40]\x[FE41]\x[FE42]\x[FE43]\x[FE44]\x[FE47]\x[FE48]" ~ "\x[FE59]\x[FE5A]\x[FE5B]\x[FE5C]\x[FE5D]\x[FE5E]\x[FF08]\x[FF09]\x[FF1C]\x[FF1E]" ~ "\x[FF3B]\x[FF3D]\x[FF5B]\x[FF5D]\x[FF5F]\x[FF60]\x[FF62]\x[FF63]\x[27EE]\x[27EF]" ~ "\x[2E24]\x[2E25]\x[27EC]\x[27ED]\x[2E22]\x[2E23]\x[2E26]\x[2E27]" #?if js ~ nqp::chr(0x2329) ~ nqp::chr(0x232A) #?endif ; method perl() { self.HOW.name(self) ~ '.new() #`[' ~ nqp::where(self) ~ ']' } method throw_unrecog_backslash_seq ($sequence) { self.panic("Unrecognized backslash sequence: '\\" ~ $sequence ~ "'"); } token termish { * * } proto token term { <...> } proto token infix { <...> } proto token prefix { <...> } proto token postfix { <...> } proto token circumfix { <...> } proto token postcircumfix { <...> } token term:sym { } token infixish { } token prefixish { <.ws> } token postfixish { | | } token nullterm { } token nullterm_alt { } # Return if it matches, otherwise. method nulltermish() { !self.terminator && self.termish || self.nullterm_alt } token quote_delimited { * } token quote_atom { [ | | [ <-quote_escape - stopper - starter> ]+ | * ] } token decint { [\d+]+ % '_' } token decints { [<.ws><.ws>]+ % ',' } token hexint { [[\d|<[ a..f A..F a..f A..F ]>]+]+ % '_' } token hexints { [<.ws><.ws>]+ % ',' } token octint { [\d+]+ % '_' } token octints { [<.ws><.ws>]+ % ',' } token binint { [\d+]+ % '_' } token binints { [<.ws><.ws>]+ % ',' } token integer { [ | 0 [ b | o | x | d ] | ] } token dec_number { | $=[ '.' \d+ ] ? | $=[ \d+ '.' \d+ ] ? | $=[ \d+ ] } token escale { <[Ee]> <[+\-]>? \d+ } proto token quote_escape { <...> } token quote_escape:sym { \\ \\ } token quote_escape:sym { \\ } token quote_escape:sym { \\ b } token quote_escape:sym { \\ n } token quote_escape:sym { \\ r } token quote_escape:sym { \\ t } token quote_escape:sym { \\ f } token quote_escape:sym { \\ e } token quote_escape:sym { \\ x [ | '[' ']' ] } token quote_escape:sym { \\ o [ | '[' ']' ] } token quote_escape:sym { \\ c } token quote_escape:sym<0> { \\ } token quote_escape:sym { \\ {} [ || [ | $=(\W) | (\w) { self.throw_unrecog_backslash_seq: $/[0].Str } ] || $=[.] ] } token charname { || || <.alpha> .*? > } token charnames { [<.ws><.ws>]+ % ',' } token charspec { [ | '[' ']' | \d+ [ _ \d+]* | | <.panic: 'Unrecognized \\c character'> ] } regex comment:sym { ^^ '#' \s* 'line' \s+ $=(\d+) [ \s+ $=(\S+) ]? $$ } =begin =item O(*%spec) This subrule attaches operator precedence information to a match object (such as an operator token). A typical invocation for the subrule might be: token infix:sym<+> { )> } This says to add all of the attributes of the C<%additive> hash (described below) and a C entry into the match object returned by the C<< infix:sym<+> >> token (as the C named capture). Note that this is a alphabetic "O", not a digit zero. The %additive hash is simply a hash containing information that is shared between all additive operators. Generally, this will simply be a normal lexically scoped hash belonging to the grammar. For example, the NQP grammar has: grammar NQP::Grammar is HLL::Grammar { my %additive := nqp::hash('prec', 't=', 'assoc', 'left'); token infix:sym<+> { )> } } =end token O(*%spec) { :my %*SPEC := %spec; } =begin =item panic([args :slurpy]) Throw an exception at the current cursor location. If the message doesn't end with a newline, also output the line number and offset of the match. =end method panic(*@args) { my $pos := self.pos(); my $target := self.target(); @args.push(' at line '); @args.push(HLL::Compiler.lineof($target, $pos) + 1); @args.push(', near "'); @args.push(nqp::escape(nqp::substr($target, $pos, 10))); @args.push('"'); nqp::die(join('', @args)) } method FAILGOAL($goal, $dba?) { unless $dba { $dba := nqp::getcodename(nqp::callercode()); } self.panic("Unable to parse expression in $dba; couldn't find final $goal"); } =begin =item peek_delimiters(target, pos) Return the start/stop delimiter pair based on peeking at C position C. =end method peek_delimiters(str $target, int $pos) { # peek at the next character my str $start := nqp::substr($target, $pos, 1); # Only if it's not a punctuation symbol should we have to check for word or whitespace props unless nqp::iscclass(nqp::const::CCLASS_PUNCTUATION, $start, 0) { if nqp::iscclass(nqp::const::CCLASS_WORD, $start, 0) { self.panic('Alphanumeric character is not allowed as a delimiter'); } if nqp::iscclass(nqp::const::CCLASS_WHITESPACE, $start, 0) { my str $code := nqp::sprintf('%X', [nqp::ord($start)]); # If it's a synthetic grapheme then we must have combiners. # Notify the user to avoid confusion. my int $combining-chars := nqp::codes($start) - 1; my str $character_s := 1 < $combining-chars ?? 'characters' !! 'character'; my str $description := (nqp::chr(nqp::ordbaseat($start, 0)) ne $start) ?? "with $combining-chars combining $character_s" !! ''; self.panic('Whitespace character ‘' ~ nqp::getuniname(nqp::ord($start)) ~ '’ (0x' ~ $code ~') ' ~ $description ~ ' is not allowed as a delimiter'); } } # assume stop delim is same as start, for the moment my str $stop := $start; my int $brac := nqp::index($brackets, $start); if $brac >= 0 { # if it's a closing bracket, that's an error also if $brac % 2 { self.panic('Use of a closing delimiter for an opener is reserved'); } # it's an opener, so get the closing bracket $stop := nqp::substr($brackets, $brac + 1, 1); # see if the opening bracket is repeated my int $len := 1; while nqp::eqat($target, $start, ++$pos) { $len++; } if $len > 1 { $start := nqp::x($start, $len); $stop := nqp::x($stop, $len); } } else { # If we ended up here it's not a bracket, so make sure it's not a colon just to be sure # Colon, word and whitespace characters aren't valid delimiters if $start eq ':' { self.panic('Colons may not be used to delimit quoting constructs'); } } [$start, $stop] } my $TRUE := 1; token quote_EXPR(*@args) { :my %*QUOTEMOD; :my $*QUOTE_START; :my $*QUOTE_STOP; { for @args -> $mod { $mod := nqp::substr($mod, 1); %*QUOTEMOD{$mod} := $TRUE; if $mod eq 'qq' { %*QUOTEMOD{'s'} := $TRUE; %*QUOTEMOD{'a'} := $TRUE; %*QUOTEMOD{'h'} := $TRUE; %*QUOTEMOD{'f'} := $TRUE; %*QUOTEMOD{'c'} := $TRUE; %*QUOTEMOD{'b'} := $TRUE; } elsif $mod eq 'b' { %*QUOTEMOD{'q'} := $TRUE; } } my @delims := self.peek_delimiters(self.target, self.pos); $*QUOTE_START := @delims[0]; $*QUOTE_STOP := @delims[1]; } } token quotemod_check(str $mod) { } method starter() { my $start := $*QUOTE_START; nqp::isconcrete($start) ?? self.'!LITERAL'($start) !! self.'!cursor_start_fail'() } method stopper() { my $stop := $*QUOTE_STOP; nqp::isconcrete($stop) ?? self.'!LITERAL'($stop) !! self.'!cursor_start_fail'() } my %nbsp := nqp::hash( "\x00A0", 1, "\x2007", 1, "\x202F", 1, "\xFEFF", 1, ); our method split_words(str $words) { my @result; my int $pos := 0; my int $eos := nqp::chars($words); my int $ws; while ($pos := nqp::findnotcclass(nqp::const::CCLASS_WHITESPACE, $words, $pos, $eos)) < $eos { # Search for another white space character as long as we hit non-breakable spaces. $ws := $pos; while %nbsp{nqp::substr($words, $ws := nqp::findcclass(nqp::const::CCLASS_WHITESPACE, $words, $ws, $eos), 1)} { $ws := $ws + 1 } nqp::push(@result, nqp::substr($words, $pos, $ws - $pos)); $pos := $ws; } @result } =begin =item EXPR(...) An operator precedence parser. =end method EXPR(str $preclim = '', int :$noinfix = 0) { my $here := self.'!cursor_start_cur'(); my int $pos := nqp::getattr_i($here, NQPMatch, '$!from'); my str $termishrx := 'termish'; my @opstack; my @termstack; my $termcur; my $termish; my %termOPER; my @prefixish; my @postfixish; my $wscur; my $infixcur; my $infix; my %inO; my str $inprec; my str $opprec; my str $inassoc; my int $more_infix; my int $term_done; while 1 { nqp::bindattr_i($here, NQPMatch, '$!pos', $pos); $termcur := $here."$termishrx"(); $pos := nqp::getattr_i($termcur, NQPMatch, '$!pos'); nqp::bindattr_i($here, NQPMatch, '$!pos', $pos); if $pos < 0 { $here.panic('Missing required term after infix') if @opstack; return $here; } $termish := $termcur.MATCH(); # Interleave any prefix/postfix we might have found. %termOPER := $termish; %termOPER := nqp::atkey(%termOPER, 'OPER') while nqp::existskey(%termOPER, 'OPER'); @prefixish := nqp::atkey(%termOPER, 'prefixish'); @postfixish := nqp::atkey(%termOPER, 'postfixish'); unless nqp::isnull(@prefixish) || nqp::isnull(@postfixish) { while @prefixish && @postfixish { my %preO := @prefixish[0].made; my %postO := @postfixish[nqp::elems(@postfixish)-1].made; my $preprec := nqp::ifnull(nqp::atkey(%preO, 'sub'), nqp::ifnull(nqp::atkey(%preO, 'prec'), '')); my $postprec := nqp::ifnull(nqp::atkey(%postO, 'sub'), nqp::ifnull(nqp::atkey(%postO, 'prec'), '')); if $postprec gt $preprec { nqp::push(@opstack, nqp::shift(@prefixish)); } elsif $postprec lt $preprec { nqp::push(@opstack, nqp::pop(@postfixish)); } elsif %postO eq 'right' { nqp::push(@opstack, nqp::shift(@prefixish)); } elsif %postO eq 'left' { nqp::push(@opstack, nqp::pop(@postfixish)); } else { self.EXPR_nonassoc($here, ~@prefixish[0], ~@postfixish[0]); } } nqp::push(@opstack, nqp::shift(@prefixish)) while @prefixish; nqp::push(@opstack, nqp::pop(@postfixish)) while @postfixish; } nqp::deletekey($termish, 'prefixish'); nqp::deletekey($termish, 'postfixish'); nqp::push(@termstack, nqp::atkey($termish, 'term')); last if $noinfix; $more_infix := 1; $term_done := 0; while $more_infix { # Now see if we can fetch an infix operator # First, we need ws to match. nqp::bindattr_i($here, NQPMatch, '$!pos', $pos); $wscur := $here.ws(); $pos := nqp::getattr_i($wscur, NQPMatch, '$!pos'); if $pos < 0 { $term_done := 1; last; } # Next, try the infix itself. nqp::bindattr_i($here, NQPMatch, '$!pos', $pos); $infixcur := $here.infixish(); $pos := nqp::getattr_i($infixcur, NQPMatch, '$!pos'); if $pos < 0 { $term_done := 1; last; } $infix := $infixcur.MATCH(); # We got an infix. %inO := $infix.made; $termishrx := nqp::ifnull(nqp::atkey(%inO, 'nextterm'), 'termish'); $inprec := ~%inO; $infixcur.panic('Missing infixish operator precedence') unless $inprec; if $inprec le $preclim { $term_done := 1; last; } while @opstack { my %opO := @opstack[+@opstack-1].made; $opprec := nqp::ifnull(nqp::atkey(%opO, 'sub'), nqp::atkey(%opO, 'prec')); last unless $opprec gt $inprec; self.EXPR_reduce(@termstack, @opstack); } if nqp::isnull(nqp::atkey(%inO, 'fake')) { $more_infix := 0; } else { nqp::push(@opstack, $infix); self.EXPR_reduce(@termstack, @opstack); } } last if $term_done; # if equal precedence, use associativity to decide if $opprec eq $inprec { $inassoc := nqp::atkey(%inO, 'assoc'); if $inassoc eq 'non' { self.EXPR_nonassoc($infixcur, @opstack[nqp::elems(@opstack)-1].Str, $infix.Str()); } elsif $inassoc eq 'left' { # left associative, reduce immediately self.EXPR_reduce(@termstack, @opstack); } elsif $inassoc eq 'list' { my $op1 := @opstack[nqp::elems(@opstack)-1].Str; my $op2 := $infix.Str(); self.EXPR_nonlistassoc($infixcur, $op1, $op2) if $op1 ne $op2 && $op1 ne ':'; } } nqp::push(@opstack, $infix); # The Shift nqp::bindattr_i($here, NQPMatch, '$!pos', $pos); $wscur := $here.ws(); $pos := nqp::getattr_i($wscur, NQPMatch, '$!pos'); nqp::bindattr_i($here, NQPMatch, '$!pos', $pos); return $here if $pos < 0; } self.EXPR_reduce(@termstack, @opstack) while @opstack; self.'!clone_match_at'( nqp::pop(@termstack), nqp::getattr_i($here, NQPMatch, '$!pos') ).'!reduce'('EXPR') } method EXPR_reduce(@termstack, @opstack) { my $op := nqp::pop(@opstack); # Give it a fresh capture list, since we'll have assumed it has # no positional captures and not taken them. nqp::bindattr($op, NQPCapture, '@!array', nqp::list()); my %opOPER := nqp::atkey($op, 'OPER'); my %opO := nqp::atkey(%opOPER, 'O').made; my str $opassoc := ~nqp::atkey(%opO, 'assoc'); my str $key; my str $sym; my $reducecheck; my $arg; if $opassoc eq 'unary' { $arg := nqp::pop(@termstack); $op[0] := $arg; $key := $arg.from() < $op.from() ?? 'POSTFIX' !! 'PREFIX'; } elsif $opassoc eq 'list' { $sym := nqp::ifnull(nqp::atkey(%opOPER, 'sym'), ''); nqp::unshift($op, nqp::pop(@termstack)); while @opstack { last if $sym ne nqp::ifnull( nqp::atkey(nqp::atkey(nqp::atpos(@opstack, nqp::elems(@opstack) - 1), 'OPER'), 'sym'), ''); nqp::unshift($op, nqp::pop(@termstack)); nqp::pop(@opstack); } nqp::unshift($op, nqp::pop(@termstack)); $key := 'LIST'; } else { # infix op assoc: left|right|ternary|... $op[1] := nqp::pop(@termstack); # right $op[0] := nqp::pop(@termstack); # left $reducecheck := nqp::atkey(%opO, 'reducecheck'); self."$reducecheck"($op) unless nqp::isnull($reducecheck); $key := 'INFIX'; } self.'!reduce_with_match'('EXPR', $key, $op); nqp::push(@termstack, $op); } method EXPR_nonassoc($cur, $op1, $op2) { $cur.panic('"' ~ $op1 ~ '" and "' ~ $op2 ~ '" are non-associative and require parens'); } method EXPR_nonlistassoc($cur, $op1, $op2) { $cur.panic('"' ~ $op1 ~ '" and "' ~ $op2 ~ '" are non-identical list-associatives and require parens'); } method ternary($match) { $match[2] := $match[1]; $match[1] := $match{'infix'}{'EXPR'}; } method MARKER(str $markname) { my %markhash := nqp::getattr( nqp::getattr(self, NQPMatch, '$!shared'), ParseShared, '%!marks'); my $cur := nqp::atkey(%markhash, $markname); if nqp::isnull($cur) { $cur := self."!cursor_start_cur"(); $cur."!cursor_pass"(self.pos()); nqp::bindkey(%markhash, $markname, $cur); } else { nqp::bindattr_i($cur, NQPMatch, '$!from', self.from); $cur."!cursor_pos"(self.pos()); $cur } } method MARKED(str $markname) { my %markhash := nqp::getattr( nqp::getattr(self, NQPMatch, '$!shared'), ParseShared, '%!marks'); my $cur := nqp::atkey(%markhash, $markname); unless nqp::istype($cur, NQPMatch) && $cur.pos() == self.pos() { $cur := self.'!cursor_start_fail'(); } $cur } method LANG($lang, $regex, *@args) { self.check_PACKAGE_oopsies('LANG1'); my $actions := self.slang_actions($lang); my $lang_cursor := self.slang_grammar($lang).'!cursor_init'(self.orig(), :p(self.pos()), :shared(self.'!shared'())); $lang_cursor.clone_braid_from(self); $lang_cursor.set_actions($actions); if self.HOW.traced(self) { $lang_cursor.HOW.trace-on($lang_cursor, self.HOW.trace_depth(self)); } $lang_cursor.check_PACKAGE_oopsies('LANG2'); my $result := $lang_cursor."$regex"(|@args); $result.set_braid_from(self) } } nqp-2018.03/src/HLL/World.nqp0000644000175000017500000001324013253717146014157 0ustar alexalex# While the grammar represents the syntactic elements of our language and # the actions take care of building up an AST to represent the semantics # of it, the world is about the declarative aspects of a language. This # includes: # # * Symbol table management # * Creating meta-object instances # * Parts of library loading (most likely it delegates to an actual loader) # * Resolving references to objects, within or between compilation units # # Just as there is one AST produced per compilation unit, there is also a # world produced per compilation unit. # # A world includes a serialization context. This contains a bunch of # objects - often meta-objects - that we want to persist across the # compile time / run time boundary. If we're doing pre-compilation # rather than "immediate run" then we serialize the contents of the # serialization context. class HLL::World { class CompilationContext { # The serialization context that we're building. has $!sc; # The number of code refs we've added to the code refs root so far. has int $!num_code_refs; # List of QAST blocks that map to the code refs table, for use in # building deserialization code. has $!code_ref_blocks; # List of QAST nodes specifying fixup tasks, either after deserialization # or between compile time and run time. has @!fixup_tasks; method BUILD(:$handle, :$description) { $!sc := nqp::createsc($handle); $!num_code_refs := 0; $!code_ref_blocks := []; @!fixup_tasks := nqp::list(); nqp::scsetdesc($!sc, $description); # Add to currently compiling SC stack. nqp::pushcompsc($!sc); } # Gets the built serialization context. method sc() { $!sc } method next_code_ref_num() { my int $code_ref_idx := $!num_code_refs; $!num_code_refs := $!num_code_refs + 1; $code_ref_idx } method code_ref_blocks() { $!code_ref_blocks } # Gets the list of tasks to do at fixup time. method fixup_tasks() { @!fixup_tasks } } has $!context; # The handle for the context. has $!handle; # Whether we're in pre-compilation mode. has $!precomp_mode; # List of QAST nodes specifying dependency loading related tasks. These # are done before the deserialization of the current context, or if in # immediate run mode before any of the other fixup tasks. # DON'T SHARE because we'd run the outer worlds tasks which include loading a setting! has @!load_dependency_tasks; has $!is_nested; method BUILD(:$handle!, :$description = '', :$context) { if $context { $!context := $context; $!is_nested := 1; } else { $!context := self.context_class().new(:$handle, :$description); $!is_nested := 0; } # Initialize attributes. $!handle := $handle; @!load_dependency_tasks := nqp::list(); $!precomp_mode := %*COMPILING<%?OPTIONS>; } method context_class() { CompilationContext } # Adds an object to the root set, along with a mapping. method add_object($obj) { my $sc := $!context.sc; nqp::setobjsc($obj, $sc); my int $idx := nqp::scobjcount($sc); nqp::scsetobj($sc, $idx, $obj); $idx } # Adds a code reference to the root set of code refs. method add_root_code_ref($code_ref, $ast_block) { my int $code_ref_idx := $!context.next_code_ref_num; $!context.code_ref_blocks.push($ast_block); nqp::scsetcode($!context.sc, $code_ref_idx, $code_ref); $code_ref_idx } # Updates a code reference in the root set. method update_root_code_ref($idx, $new_code_ref) { nqp::scsetcode($!context.sc, $idx, $new_code_ref); } # Checks if we are in pre-compilation mode. method is_precompilation_mode() { $!precomp_mode } method is_nested() { $!is_nested } method context() { $!context } # Add an event that we want to run before deserialization or before any # other fixup. method add_load_dependency_task(:$deserialize_ast, :$fixup_ast) { if $!precomp_mode { @!load_dependency_tasks.push($deserialize_ast) if $deserialize_ast; } else { @!load_dependency_tasks.push($fixup_ast) if $fixup_ast; } } # Add an event that we need to run at fixup time (after deserialization of # between compilation and runtime). method add_fixup_task(:$deserialize_ast, :$fixup_ast) { if $!precomp_mode { $!context.fixup_tasks.push($deserialize_ast) if $deserialize_ast; } else { $!context.fixup_tasks.push($fixup_ast) if $fixup_ast; } } # Gets the built serialization context. method sc() { $!context.sc } # Gets the SC handle. method handle() { $!handle } method code_ref_blocks() { $!context.code_ref_blocks } # Gets the list of load dependency tasks to do. method load_dependency_tasks() { @!load_dependency_tasks } # Gets the list of tasks to do at fixup time. method fixup_tasks() { $!context.fixup_tasks } method add_comp_line_directive(@directive) { my @clds := @*comp_line_directives; my int $elems := nqp::elems(@clds); if $elems == 0 || !(@clds[$elems - 1][0] eq @directive[0]) { nqp::push(@clds, @directive); } } } nqp-2018.03/src/HLL/sprintf.nqp0000644000175000017500000005124613253717146014565 0ustar alexalexmy module sprintf { my @handlers; my $assert_used_args; grammar Syntax { token TOP { :my $*ARGS_USED := 0; ^ * $ } method panic($message, $payload) { my $ex := nqp::newexception(); nqp::setmessage($ex, $message); nqp::setpayload($ex, $payload); nqp::throw($ex); } token statement { [ | [ [ | ] || <.panic("'" ~ nqp::substr(self.orig,1) ~ "' is not valid in sprintf format sequence '" ~ self.orig ~ "'", nqp::hash('BAD_DIRECTIVE', nqp::hash('DIRECTIVE', nqp::substr(self.orig, 1), 'SEQUENCE', self.orig)))> ] | ] } proto token directive { <...> } token directive:sym { '%' ? * ? [ '.' ]? $=<[bB]> } token directive:sym { '%' ? * ? } token directive:sym { '%' ? * ? [ '.' ]? $=<[di]> } token directive:sym { '%' ? * ? [ '.' ]? $=<[eE]> } token directive:sym { '%' ? * ? [ '.' ]? $=<[fF]> } token directive:sym { '%' ? * ? [ '.' ]? $=<[gG]> } token directive:sym { '%' ? * ? [ '.' ]? } token directive:sym { '%' ? * ? [ '.' ]? } token directive:sym { '%' ? * ? } token directive:sym { '%' ? * ? [ '.' ]? $=<[xX]> } proto token escape { <...> } token escape:sym<%> { '%' * ? } token literal { <-[%]>+ } token idx { $=[\d+] '$' } token flags { | $ = ' ' | $ = '+' | $ = '-' | $ = '0' | $ = '#' } token size { \d* | $='*' } } class Actions { my $knowhow := nqp::knowhow().new_type(:repr("P6bigint")); my $zero := nqp::box_i(0, $knowhow); method TOP($/) { my @statements; @statements.push( $_.made ) for $; if ($assert_used_args && $*ARGS_USED < +@*ARGS_HAVE) || ($*ARGS_USED > +@*ARGS_HAVE) { panic("Your printf-style directives specify " ~ ($*ARGS_USED == 1 ?? "1 argument, but " !! "$*ARGS_USED arguments, but ") ~ (+@*ARGS_HAVE < 1 ?? "no argument was" !! +@*ARGS_HAVE == 1 ?? "1 argument was" !! +@*ARGS_HAVE ~ " arguments were") ~ " supplied", nqp::hash('DIRECTIVES_COUNT', nqp::hash('ARGS_HAVE', +@*ARGS_HAVE, 'ARGS_USED', $*ARGS_USED))) } make nqp::join('', @statements); } sub panic($message, $payload) { my $ex := nqp::newexception(); nqp::setmessage($ex, $message); nqp::setpayload($ex, $payload); nqp::throw($ex); } sub bad-type-for-directive($type, $directive) { my $message := "Directive $directive not applicable for type " ~ $type.HOW.name($type); my $payload := nqp::hash('BAD_TYPE_FOR_DIRECTIVE', nqp::hash('TYPE', $type.HOW.name($type), 'DIRECTIVE', $directive)); panic($message, $payload); } sub infix_x($s, $n) { my @strings; my int $i := 0; @strings.push($s) while $i++ < $n; nqp::join('', @strings); } sub next_argument($/) { if $ { $assert_used_args := 0; @*ARGS_HAVE[$.made] } else { @*ARGS_HAVE[$*ARGS_USED++] } } sub floatify($n) { unless $n =:= NQPMu { for @handlers { return $_.float: $n if $_.mine: $n; } } $n } sub intify($number_representation) { if $number_representation =:= NQPMu { return $zero; ## missing argument is handled in method TOP } for @handlers -> $handler { if $handler.mine($number_representation) { return $handler.int($number_representation); } } if nqp::isint($number_representation) { nqp::box_i($number_representation, $knowhow); } else { if nqp::isnum($number_representation) || nqp::isstr($number_representation) { if $number_representation > 0 { nqp::fromnum_I(nqp::floor_n($number_representation), $knowhow); } else { nqp::fromnum_I(nqp::ceil_n($number_representation), $knowhow); } } else { $number_representation; } } } sub padding_char($st) { my $padding_char := ' '; if (!$st && !has_flag($st, 'minus')) || $st ~~ /<[eEfFgG]>/ { $padding_char := '0' if $_ for $st; } $padding_char } sub has_flag($st, $key) { my $ok := 0; if $st { $ok := 1 if $_{$key} for $st } $ok } method statement($/){ my $st; if $ { $st := $ } elsif $ { $st := $ } else { $st := $ } my @pieces; # Adds padding chars on in certain cases @pieces.push: infix_x(padding_char($st), $st.made - nqp::chars($st.made)) if $st; has_flag($st, 'minus') ?? @pieces.unshift: $st.made !! @pieces.push: $st.made; make join('', @pieces) } method directive:sym($/) { my $next := next_argument($/); CATCH { bad-type-for-directive($next, 'b'); } my $int := intify($next); my $pad := padding_char($/); my $sign := nqp::islt_I($int, $zero) ?? '-' !! has_flag($/, 'plus') ?? '+' !! has_flag($/, 'space') ?? ' ' !! ''; $int := nqp::base_I(nqp::abs_I($int, $knowhow), 2); my $size := $ ?? $.made !! 0; my $pre := ''; $pre := ($ eq 'b' ?? '0b' !! '0B') if $int ne '0' && has_flag($/, 'hash'); if nqp::chars($) { $int := ($.made == 0 && $int == 0) ?? '' !! $sign ~ $pre ~ infix_x('0', $.made - nqp::chars($int)) ~ $int; } else { if $pad ne ' ' && $size { my $chars_sign_pre := $sign ?? nqp::chars($pre) + 1 !! nqp::chars($pre); $int := $sign ~ $pre ~ infix_x($pad, $size - $chars_sign_pre - nqp::chars($int)) ~ $int; } else { $int := $sign ~ $pre ~ $int; } } make $int; } method directive:sym($/) { my $next := next_argument($/); CATCH { bad-type-for-directive($next, 'c'); } make nqp::chr(intify($next)) } method directive:sym($/) { my $next := next_argument($/); CATCH { bad-type-for-directive($next, 'd'); } my $int := intify($next); my $pad := padding_char($/); my $sign := nqp::islt_I($int, $zero) ?? '-' !! has_flag($/, 'plus') ?? '+' !! has_flag($/, 'space') ?? ' ' !! ''; $int := nqp::tostr_I(nqp::abs_I($int, $knowhow)); # For `d`, precision is how many digits long the number should be, # prefixing it with zeros, as needed. If precision is zero and # our number is zero, then the result is an empty string. if nqp::chars($) { if my $prec := +$.made { if (my $rep := $prec - nqp::chars($int)) > 0 { $int := infix_x('0', $rep) ~ $int; } } else { $int := '' if $int == 0; } } if $pad ne ' ' && $ { $int := $sign ~ infix_x($pad, $.made - nqp::chars($int) - 1) ~ $int; } else { $int := $sign ~ $int; } make $int } sub pad-with-sign($sign, $num, $size, $pad) { if $pad ne ' ' && $size { $sign ~ infix_x($pad, $size - nqp::chars($num) - 1) ~ $num; } else { $sign ~ $num; } } sub normalize($float) { my @parts := nqp::split('e', nqp::lc($float)); my $sign := ''; if nqp::eqat(@parts[0], '-', 0) { $sign := '-'; @parts[0] := nqp::substr(@parts[0], 1); } my @num := nqp::split('.', @parts[0]); my $radix-point := nqp::chars(@num[0]); my $new-radix-point := $radix-point + @parts[1]; my $num := @num[0] ~ @num[1]; my $n := nqp::chars($num); if $new-radix-point > $n { $num := $num ~ infix_x('0', $new-radix-point - $n); } elsif $new-radix-point < 0 { $num := "0." ~ infix_x('0', nqp::abs_n($new-radix-point)) ~ $num; } else { $num := nqp::substr($num,0,$new-radix-point) ~ '.' ~ nqp::substr($num,$new-radix-point); } $sign ~ $num; } sub stringify-to-precision($float, $precision) { $float := normalize($float) if nqp::index($float, "e") || nqp::index($float, "E"); my @number := nqp::split('.', $float); my $lhs_s := @number[0]; my $rhs_s := @number[1]; my $d := nqp::chars($rhs_s); # digits after decimal my $zeroes := infix_x("0", 1 + ($precision > $d ?? $precision - $d !! 0)); $lhs_s := nqp::substr($lhs_s, 1) if nqp::eqat($lhs_s, '-', 0); my $lhs_I := nqp::fromstr_I($lhs_s, $knowhow); my $rhs_I := nqp::fromstr_I("1" ~ $rhs_s ~ $zeroes, $knowhow); # The leading 1 is to preserve leading zeroes my $cc := nqp::chars(nqp::tostr_I($rhs_I)); my $e := nqp::fromnum_I($d > $precision ?? $d - $precision !! 0, $knowhow); my $pot := nqp::pow_I(nqp::fromnum_I(10, $knowhow), $e, $knowhow, $knowhow); # power of ten my $rounder := nqp::mul_I(nqp::fromnum_I(5, $knowhow), $pot, $knowhow); $rhs_I := nqp::add_I($rhs_I, $rounder, $knowhow); $rhs_s := nqp::tostr_I($rhs_I); $lhs_I := nqp::add_I($lhs_I, nqp::fromnum_I(1,$knowhow), $knowhow) if nqp::substr($rhs_s,0,1) ne '1'; # we had a carry $lhs_s := nqp::tostr_I($lhs_I); $rhs_s := nqp::substr($rhs_s,1,$precision); # skip the leading char we added. my $return := $lhs_s; $return := $return ~ "." ~ $rhs_s if $rhs_s ne ""; $return; } sub stringify-to-precision2($float, $precision) { my $exp := $float == 0.0 ?? 0 !! nqp::floor_n(nqp::log_n($float) / nqp::log_n(10)); $float := nqp::abs_n($float) * nqp::pow_n(10, $precision - ($exp + 1)) + 0.5; $float := nqp::floor_n($float); $float := $float / nqp::pow_n(10, $precision - ($exp + 1)); #?if jvm if $exp == -4 { $float := stringify-to-precision($float, $precision + 3); $float := nqp::substr($float, 0, nqp::chars($float) - 1) if nqp::chars($float) > 1 && $float ~~ /\.\d**4 0+$/; $float := nqp::substr($float, 0, nqp::chars($float) - 1) if nqp::chars($float) > 1 && $float ~~ /\.\d**4 0+$/; } $float #?endif } sub fixed-point($float, $precision, $size, $pad, $/) { # if we have zero; handle its sign: 1/0e0 == +Inf, 1/-0e0 == -Inf my $sign := $float < 0 || ( $float == 0 && nqp::islt_n(nqp::div_n(1e0,$float), 0e0) ) ?? '-' !! has_flag($/, 'plus') ?? '+' !! has_flag($/, 'space') ?? ' ' !! ''; $float := nqp::abs_n($float); $float := stringify-to-precision($float, $precision) unless nqp::isnanorinf($float); pad-with-sign($sign, $float, $size, $pad); } sub scientific($float, $e, $precision, $size, $pad, $/) { # if we have zero; handle its sign: 1/0e0 == +Inf, 1/-0e0 == -Inf my $sign := $float < 0 || ( $float == 0 && nqp::islt_n(nqp::div_n(1e0,$float), 0e0) ) ?? '-' !! has_flag($/, 'plus') ?? '+' !! has_flag($/, 'space') ?? ' ' !! ''; $float := nqp::abs_n($float); unless nqp::isnanorinf($float) { my $exp := $float == 0.0 ?? 0 !! nqp::floor_n(nqp::log_n($float) / nqp::log_n(10)); $float := $float / nqp::pow_n(10, $exp); $float := stringify-to-precision($float, $precision); if $exp < 0 { $exp := -$exp; $float := $float ~ $e ~ '-' ~ ($exp < 10 ?? '0' !! '') ~ $exp; } else { $float := $float ~ $e ~ '+' ~ ($exp < 10 ?? '0' !! '') ~ $exp; } } pad-with-sign($sign, $float, $size, $pad); } sub shortest($float, $e, $precision, $size, $pad, $/) { # if we have zero; handle its sign: 1/0e0 == +Inf, 1/-0e0 == -Inf my $sign := $float < 0 || ( $float == 0 && nqp::islt_n(nqp::div_n(1e0,$float), 0e0) ) ?? '-' !! has_flag($/, 'plus') ?? '+' !! has_flag($/, 'space') ?? ' ' !! ''; $float := nqp::abs_n($float); return pad-with-sign($sign, $float, $size, $pad) if nqp::isnanorinf($float); my $exp := $float == 0.0 ?? 0 !! nqp::floor_n(nqp::log_n($float) / nqp::log_n(10)); if -2 - $precision < $exp && $exp < $precision { my $fixed-precision := $precision - ($exp + 1); my $fixed := stringify-to-precision2($float, $precision); pad-with-sign($sign, $fixed, $size, $pad); } else { $float := $float / nqp::pow_n(10, $exp); $float := stringify-to-precision2($float, $precision); my $sci; if $exp < 0 { $exp := -$exp; $sci := $float ~ $e ~ '-' ~ ($exp < 10 ?? '0' !! '') ~ $exp; } else { $sci := $float ~ $e ~ '+' ~ ($exp < 10 ?? '0' !! '') ~ $exp; } pad-with-sign($sign, $sci, $size, $pad); } } method directive:sym($/) { my $next := next_argument($/); CATCH { bad-type-for-directive($next, 'e'); } my $float := floatify($next); my $precision := $ ?? $.made !! 6; my $pad := padding_char($/); my $size := $ ?? $.made !! 0; make scientific($float, $, $precision, $size, $pad, $/); } method directive:sym($/) { my $next := next_argument($/); CATCH { bad-type-for-directive($next, 'f'); } my $int := floatify($next); my $precision := $ ?? $.made !! 6; my $pad := padding_char($/); my $size := $ ?? $.made !! 0; make fixed-point($int, $precision, $size, $pad, $/); } method directive:sym($/) { my $next := next_argument($/); CATCH { bad-type-for-directive($next, 'g'); } my $float := floatify($next); my $precision := $ ?? $.made !! 6; my $pad := padding_char($/); my $size := $ ?? $.made !! 0; make shortest($float, $ eq 'G' ?? 'E' !! 'e', $precision, $size, $pad, $/); } method directive:sym($/) { my $next := next_argument($/); CATCH { bad-type-for-directive($next, 'o'); } my $int := intify($next); $int := nqp::base_I($int, 8); my $pre := '0' if $int ne '0' && has_flag($/, 'hash'); if nqp::chars($) { $int := '' if $.made == 0 && $int == 0; $pre := '' if $.made > nqp::chars($int); $int := $pre ~ infix_x('0', intify($.made) - nqp::chars($int)) ~ $int; } else { $int := $pre ~ $int } make $int } method directive:sym($/) { my $next := next_argument($/); CATCH { bad-type-for-directive($next, 's'); } my $string := $next; if nqp::chars($) && nqp::chars($string) > $.made { $string := nqp::substr($string, 0, $.made); } make $string } # XXX: Should we emulate an upper limit, like 2**64? # XXX: Should we emulate p5 behaviour for negative values passed to %u ? method directive:sym($/) { my $next := next_argument($/); CATCH { bad-type-for-directive($next, 'u'); } my $int := intify($next); if nqp::islt_I($int, $zero) { note("negative value '$int' for %u in sprintf"); $int := 0; } # Go through tostr_I to avoid scientific notation. make nqp::tostr_I($int) } method directive:sym($/) { my $next := next_argument($/); CATCH { bad-type-for-directive($next, 'x'); } my $int := intify($next); $int := nqp::base_I($int, 16); my $pre := '0X' if $int ne '0' && has_flag($/, 'hash'); if nqp::chars($) { $int := '' if $.made == 0 && $int == 0; $int := $pre ~ infix_x('0', $.made - nqp::chars($int)) ~ $int; } else { $int := $pre ~ $int } make $ eq 'x' ?? nqp::lc($int) !! $int } method escape:sym<%>($/) { make '%' } method literal($/) { make ~$/ } method idx($/) { my $index := nqp::radix(10, $, 0, 0)[0] - 1; nqp::die("Parameter index starts to count at 1 but 0 was passed") if $index < 0; make $index } method size($/) { make $ ?? next_argument({}) !! ~nqp::radix(10, $/, 0, 0)[0] } } my $actions := Actions.new(); sub sprintf($format, @arguments) { my @*ARGS_HAVE := @arguments; $assert_used_args := 1; return Syntax.parse( $format, :actions($actions) ).made; } nqp::bindcurhllsym('sprintf', &sprintf); class Directives { method TOP($/) { my $count := 0; $count := nqp::add_i($count, $_.made) for $; make $count } method statement($/) { make $ && !$ ?? 1 !! 0 } } my $directives := Directives.new(); sub sprintfdirectives($format) { return Syntax.parse( $format, :actions($directives) ).made; } nqp::bindcurhllsym('sprintfdirectives', &sprintfdirectives); sub sprintfaddargumenthandler($interface) { @handlers.push($interface); "Added!"; # return meaningless string } nqp::bindcurhllsym('sprintfaddargumenthandler', &sprintfaddargumenthandler); } nqp-2018.03/src/NQP/Actions.nqp0000644000175000017500000020544613253717146014522 0ustar alexalexclass NQP::Actions is HLL::Actions { sub xblock_immediate($xblock) { $xblock[1] := block_immediate($xblock[1]); $xblock; } sub block_immediate($block) { $block.blocktype('immediate'); unless $block.symtable() { my $stmts := QAST::Stmts.new( :node($block.node) ); for $block.list { $stmts.push($_); } $block := $stmts; } $block; } sub default_for($sigil) { if $sigil eq '@' { QAST::Op.new( :op('list') ) } elsif $sigil eq '%' { QAST::Op.new( :op('hash') ) } else { my $default; try { $default := QAST::WVal.new( :value($*W.find_sym(['NQPMu'])) ); CATCH { $default := QAST::Op.new( :op('null') ) } } $default } } sub default_value_for_prim($prim) { $prim == 1 ?? QAST::IVal.new( :value(0) ) !! $prim == 2 ?? QAST::NVal.new( :value(0.0) ) !! QAST::SVal.new( :value('') ) } method TOP($/) { make $.made; } method deflongname($/) { make $ ?? ~$ ~ ':' ~ $.made.named ~ colonpair_str($.made) !! ~$/; } sub colonpair_str($ast) { my $s; if nqp::istype($ast, QAST::Op) { my @parts; for $ast.list { @parts.push($_.value) } $s := join(' ', @parts) } else { $s := $ast.value } $s ~~ /<[ < > ]>/ ?? '«' ~ $s ~ '»' !! '<' ~ $s ~ '>'; } method comp_unit($/) { my $mainline := $.ast; my $unit := $*W.pop_lexpad(); $unit.name(''); # If our caller wants to know the mainline ctx, provide it here. # (CTXSAVE is inherited from HLL::Actions.) Don't do this when # there was an explicit {YOU_ARE_HERE}. unless $*HAS_YOU_ARE_HERE { $unit.push( self.CTXSAVE() ); } # Detect if we're the main unit by if we were given any args. If so, # register the mainline as a module (so trying to use ourself in the # program will not explode). If we have a MAIN sub, call it at end of # mainline. $unit.unshift(QAST::Var.new( :scope('lexical'), :name('@ARGS'), :decl('param'), :slurpy(1) )); if $*MAIN_SUB { $mainline.push(QAST::Op.new( :op('if'), QAST::Var.new( :scope('lexical'), :name('@ARGS') ), QAST::Op.new( :op('call'), :name('&' ~ $*MAIN_SUB.name), QAST::Var.new( :scope('lexical'), :name('@ARGS'), :flat(1) ) ) )); } # Push mainline statements into UNIT. $unit.push($mainline); # Load the needed libraries. $unit.push($*W.libs()); # Wrap everything in a QAST::CompUnit. my $compunit := QAST::CompUnit.new( :hll('nqp'), # Serialization related bits. :sc($*W.sc()), :code_ref_blocks($*W.code_ref_blocks()), :compilation_mode($*W.is_precompilation_mode()), :pre_deserialize($*W.load_dependency_tasks()), :post_deserialize($*W.fixup_tasks()), # If this unit is loaded as a module, we want it to automatically # execute the mainline code above after all other initializations # have occurred. :load(QAST::Op.new( :op('call'), QAST::BVal.new( :value($unit) ) )), # If we're executed as the mainline, get the command line args # and pass them along. :main(QAST::Stmts.new( QAST::Op.new( :op('call'), QAST::BVal.new( :value($unit) ), QAST::Var.new( :name('ARGS'), :scope('local'), :decl('param'), :slurpy(1), :flat(1) ) ) )), # Finally, UNIT, which in turn contains all of the other program # elements. $unit ); $*W.cleanup(); make $compunit; } method statementlist($/) { my $ast_list := QAST::Stmts.new( :node($/) ); if $ { for $ { my $ast := $_.ast; my $sunk := $ast.ann('sink'); $ast := $sunk if nqp::defined($sunk); if $ast.ann('bareblock') { $ast := block_immediate($ast[0]); } $ast := QAST::Stmts.new($ast) if nqp::istype($ast, QAST::Node); $ast_list.push( $ast ); } } else { $ast_list.push(default_for('$')); } make $ast_list; } method statement($/, $key?) { my $ast; if $ { my $mc := $; my $ml := $; $ast := $.ast; if $mc { $ast := QAST::Op.new($mc.ast, $ast, :op(~$mc), :node($/) ); } if $ml { if ~$ml eq 'for' { $ast := QAST::Block.new( :blocktype('immediate'), QAST::Var.new( :name('$_'), :scope('lexical'), :decl('param') ), $ast); $ast.symbol('$_', :scope('lexical') ); $ast.arity(1); $ast := QAST::Op.new($ml.ast, $ast, :op(~$ml), :node($/) ); } else { $ast := QAST::Op.new($ml.ast, $ast, :op(~$ml), :node($/) ); } } if $ast.ann('var_initialized') { # Variable declared and unconditionally initialized; can strip # the added just-to-be-safe initialization of the lexical and # just have the var decl. my $decls := $*W.cur_lexpad()[0]; $decls.push($decls.pop()[0]); # First child of bind node is var decl } } elsif $ { $ast := $.ast; } elsif $ { $ast := $.ast; } else { $ast := 0; } make $ast; } method xblock($/) { make QAST::Op.new( $.ast, $.ast, :op('if'), :node($/) ); } method pblock($/) { make $.ast; } method block($/) { make $.ast; } method blockoid($/) { my $BLOCK := $*W.pop_lexpad(); if $ { my $ast := $.ast; if %*HANDLERS { $ast := QAST::Op.new( :op('handle'), $ast ); for %*HANDLERS { $ast.push($_.key); $ast.push($_.value); } } $BLOCK.push($ast); $BLOCK.node($/); $BLOCK.annotate('handlers', %*HANDLERS) if %*HANDLERS; make $BLOCK; } else { if $*HAS_YOU_ARE_HERE { $/.panic('{YOU_ARE_HERE} may only appear once in a setting'); } $*HAS_YOU_ARE_HERE := 1; make $.ast; } } method newpad($/) { $*W.push_lexpad($/) } method outerctx($/) { unless nqp::defined(%*COMPILING<%?OPTIONS>) { # We haven't got a specified outer context already, so load a # setting. my $SETTING := $*W.load_setting(%*COMPILING<%?OPTIONS> // 'NQPCORE'); # If it exports HOWs, grab them. Also, if we're loading the # setting, also by default load Regex library (we can't load # this in the setting as Regex depends on the setting). unless %*COMPILING<%?OPTIONS> eq 'NULL' { import_HOW_exports($SETTING); unless %*COMPILING<%?OPTIONS> { if %*COMPILING<%?OPTIONS> -> $lib { $*W.load_module($lib, $*GLOBALish); } else { $*W.load_module('NQPP6QRegex', $*GLOBALish); } } } } self.SET_BLOCK_OUTER_CTX($*W.cur_lexpad()); } sub import_HOW_exports($UNIT) { # See if we've exported any HOWs. if nqp::existskey($UNIT, 'EXPORTHOW') { for $UNIT.WHO { $*LANG.set_how($_.key, $_.value); } } } method you_are_here($/) { make self.CTXSAVE(); } ## Statement control method statement_control:sym($/) { my $module := $*W.load_module(~$, $*GLOBALish); if nqp::defined($module) { $*W.import($module.WHO.WHO) if nqp::existskey($module, 'EXPORT') && nqp::existskey($module.WHO, 'DEFAULT'); import_HOW_exports($module); } make QAST::Stmts.new(); } method statement_control:sym($/) { my $count := +$ - 1; my $ast := xblock_immediate( $[$count].ast ); if $ { $ast.push( block_immediate( $.ast ) ); } # build if/then/elsif structure while $count > 0 { $count--; my $else := $ast; $ast := xblock_immediate( $[$count].ast ); $ast.push($else); } make $ast; } method statement_control:sym($/) { my $ast := xblock_immediate( $.ast ); $ast.op('unless'); $/.prune(); make $ast; } method statement_control:sym($/) { my $ast := xblock_immediate( $.ast ); $ast.op(~$); if $*LABEL { $ast.push(QAST::WVal.new( :value($*W.find_sym([$*LABEL])), :named('label') )); } elsif !$*CONTROL_USED { $ast.push(QAST::IVal.new( :value(1), :named('nohandler') )); } $/.prune(); make $ast; } method statement_control:sym($/) { my $op := 'repeat_' ~ ~$; my $ast; if $ { $ast := xblock_immediate( $.ast ); $ast.op($op); } else { $ast := QAST::Op.new( $.ast, block_immediate( $.ast ), :op($op), :node($/) ); } if $*LABEL { $ast.push(QAST::WVal.new( :value($*W.find_sym([$*LABEL])), :named('label') )); } elsif !$*CONTROL_USED { $ast.push(QAST::IVal.new( :value(1), :named('nohandler') )); } $/.prune(); make $ast; } method statement_control:sym($/) { my $ast := $.ast; $ast.op('for'); my $block := $ast[1]; unless $block.arity { $block[0].push( QAST::Var.new( :name('$_'), :scope('lexical'), :decl('param') ) ); $block.symbol('$_', :scope('lexical') ); $block.arity(1); } $block.blocktype('immediate'); if $*LABEL { $ast.push(QAST::WVal.new( :value($*W.find_sym([$*LABEL])), :named('label') )); } elsif !$*CONTROL_USED { $ast.push(QAST::IVal.new( :value(1), :named('nohandler') )); } $/.prune(); make $ast; } method statement_control:sym($/) { my $block := $.ast; set_block_handler($/, $block, 'CATCH'); $/.prune(); make default_for('$'); } method statement_control:sym($/) { my $block := $.ast; set_block_handler($/, $block, 'CONTROL'); $/.prune(); make default_for('$'); } sub set_block_handler($/, $block, $type) { if nqp::existskey(%*HANDLERS, $type) { nqp::die("Duplicate $type handler in block"); } unless $block.arity { $block.unshift( QAST::Op.new( :op('bind'), QAST::Var.new( :scope('lexical'), :name('$!'), :decl('var') ), QAST::Var.new( :scope('lexical'), :name('$_')), ), ); $block.unshift(QAST::Var.new( :name('$_'), :scope('lexical'), :decl('param') )); $block.symbol('$_', :scope('lexical') ); $block.symbol('$!', :scope('lexical') ); $block.arity(1); } $block.blocktype('declaration'); %*HANDLERS{$type} := QAST::Stmts.new( QAST::Op.new( :op('call'), $block, QAST::Op.new( :op('exception') ), ), default_for('$')); } method statement_prefix:sym($/) { make $*W.run_begin_block($.ast); $/.prune(); } method statement_prefix:sym($/) { $*W.cur_lexpad().push($.ast); make QAST::Stmts.new(); $/.prune(); } method statement_prefix:sym($/) { my $ast := $.ast; if nqp::istype($ast, QAST::Block) { my $handlers := $ast.ann('handlers'); if $handlers && nqp::existskey($handlers, 'CATCH') { make $ast; return 1; } else { $ast.blocktype('immediate'); } } make QAST::Op.new( :op('handle'), $ast, 'CATCH', QAST::Stmts.new( default_for('$') )); $/.prune(); } method blorst($/) { make $ ?? block_immediate($.ast) !! $.ast; $/.prune(); } # Statement modifiers method statement_mod_cond:sym($/) { make $.ast; } method statement_mod_cond:sym($/) { make $.ast; } method statement_mod_loop:sym($/) { make $.ast; } method statement_mod_loop:sym($/) { make $.ast; } ## Terms method term:sym($/) { make $.ast; } method term:sym($/) { make $.ast; } method term:sym($/) { make $.ast; } method term:sym($/) { make $.ast; } method term:sym($/) { make $.ast; } method term:sym($/) { make $.ast; } method term:sym($/) { make $.ast; } method term:sym($/) { make $.ast; } method term:sym($/) { make QAST::Op.new( :op('takeclosure'), $.ast ); } method fatarrow($/) { my $ast := $.ast; $ast.named( $.Str ); make $ast; $/.prune; } method colonpair($/) { if $ { $.ast.named(~$); make $.ast; } else { my $ast := $ ?? $.ast !! QAST::IVal.new( :value( !$ ) ); $ast.named( ~$ ); make $ast; } $/.prune; } method variable($/) { my $ast; if $ { $ast := $.ast; $ast.unshift(QAST::VarWithFallback.new( :name('$/'), :scope('lexical'), :fallback(default_for('$')) )); } else { my @name := NQP::Compiler.parse_name(~$/); if +@name > 1 { if $ { $/.panic("Twigil not allowed on multi-part name"); } $ast := lexical_package_lookup(@name, $/); } elsif $ eq '*' { my $global_fallback := QAST::Op.new( :op('ifnull'), lexical_package_lookup(['GLOBAL', ~$ ~ $], $/), QAST::Op.new( :op('die_s'), QAST::SVal.new( :value('Contextual ' ~ ~$/ ~ ' not found') ) )); $ast := QAST::VarWithFallback.new( :name(~@name.pop), :scope('contextual'), :fallback($global_fallback) ); } elsif $ eq '!' { my $name := ~@name.pop; my $ch; my $package := $/.package; if $*PKGDECL eq 'role' { $ch := QAST::Var.new( :name('$?CLASS'), :scope('typevar') ); $ch.set_compile_time_value($package); } else { $ch := QAST::WVal.new( :value($package) ); } $ast := QAST::Var.new( :name($name), :scope('attribute'), QAST::Op.new( :op('decont'), QAST::Var.new( :name('self'), :scope('lexical') ) ), $ch ); # Make sure the attribute exists and add type info. unless $*IN_DECL { my $attr; for $package.HOW.attributes($package, :local(1)) { if $_.name eq $name { $attr := $_; last; } } if nqp::defined($attr) { if nqp::can($attr, 'type') { $ast.returns($attr.type); } } else { $/.panic("Attribute '$name' not declared"); } } } elsif $ { my $name := ~$ eq '@' ?? 'list' !! ~$ eq '%' ?? 'hash' !! 'item'; $ast := QAST::Op.new( :op('callmethod'), :name($name), $.ast ); } elsif $*W.is_package(~@name[0]) { $ast := lexical_package_lookup(@name, $/); $ast.fallback( default_for( $ ) ); } else { my str $name := ~@name.pop; my int $is_lex := 0; if $*IN_DECL eq 'variable' || $name eq '$_' || $name eq '$/' || $name eq '$¢' || $name eq '$!' || $ eq '?' || ($is_lex := $*W.is_lexical($name)) { $ast := QAST::Var.new( :name($name), :scope($name eq '$?CLASS' ?? 'typevar' !! 'lexical') ); $ast.returns($*W.lexical_type($name)) if $is_lex; } else { $/.panic("Use of undeclared variable '$name'"); } } } make $ast; } method package_declarator:sym($/) { make $.ast } method package_declarator:sym($/) { make $.ast } method package_declarator:sym($/) { make $.ast } method package_declarator:sym($/) { make $.ast } method package_declarator:sym($/) { make $.ast } method package_declarator:sym($/) { make $.ast } method package_declarator:sym($/) { # Construct meta-object with specified metaclass, adding it to the # serialization context for this compilation unit. my $HOW := $*W.find_sym($); my $PACKAGE := $*W.pkg_create_mo($HOW, :name(~$)); # Install it in the current package or current lexpad as needed. if $*SCOPE eq 'our' || $*SCOPE eq '' { $*W.install_package_symbol($*OUTERPACKAGE, $, $PACKAGE); if +$ == 1 { $*W.install_lexical_symbol($*W.cur_lexpad(), ~$[0], $PACKAGE); } } elsif $*SCOPE eq 'my' { if +$ != 1 { $.panic("A my scoped package cannot have a multi-part name yet"); } $*W.install_lexical_symbol($*W.cur_lexpad(), ~$[0], $PACKAGE); } else { $/.panic("$*SCOPE scoped packages are not supported"); } make QAST::Stmts.new(); $/.prune; } method package_def($/) { # Get name and meta-object. my @ns := nqp::clone($); my $name := ~@ns.pop; my $how := $/.how($*PKGDECL); my $package := $/.package; # Get the body code. my $ast; if $ { $ast := $.ast; } else { $ast := $*W.pop_lexpad(); $ast.push($.ast); } # Evaluate everything in the package in-line unless this is a generic # type in which case it needs delayed evaluation. Normally, $?CLASS is # a fixed lexical, but for generic types it becomes a parameter. Also # for parametric types, pass along the role body block. if nqp::can($how, 'parametric') && $how.parametric($how) { $ast.blocktype('declaration_static'); my $params := QAST::Stmts.new( QAST::Var.new( :name('$?CLASS'), :scope('lexical'), :decl('param') ) ); if $ { for $ { $params.push($_.ast); } } $ast.unshift($params); $ast.push(QAST::Op.new( :op('curlexpad') )); $ast.symbol('$?CLASS', :scope('lexical')); $*W.pkg_set_body_block($package, $ast); } else { $ast.blocktype('immediate'); } # Add parent, if we have one; otherwise set default. if $ { my $parent; my $parent_found; try { $parent := $*W.find_sym(nqp::clone($)); $parent_found := 1; } if $parent_found { $*W.pkg_add_parent_or_role($package, "add_parent", $parent); } else { $/.panic("Could not find parent class '" ~ ~$ ~ "'"); } } elsif nqp::can($how, 'set_default_parent') { my $default := $*PKGDECL eq 'grammar' ?? ['NQPMatch'] !! ['NQPMu']; $*W.pkg_add_parent_or_role($package, "set_default_parent", $*W.find_sym($default)); } # Add any done roles. if $ { for $ { my $role; my $role_found; try { $role := $*W.find_sym(nqp::clone($_)); $role_found := 1; } if $role_found { $*W.pkg_add_parent_or_role($package, "add_role", $role); } else { $/.panic("Could not find role '" ~ ~$_ ~ "'"); } } } # Extra traits, if present. if $ { $package.HOW.set_nativesize($package, nqp::add_i($, 0)); } if $ { $package.HOW.set_unsigned($package, 1); } # Finally, compose. $*W.pkg_compose($package); # If it's a grammar, pre-compute the NFAs. if $*PKGDECL eq 'grammar' && nqp::can($package, '!precompute_nfas') { $package.'!precompute_nfas'(); } # Export if needed. if $ { $*EXPORT.WHO.WHO{$name} := $package; } make $ast; $/.prune; } method role_params($/) { for $ { my $var := $_.ast; $var.scope('lexical'); $var.decl('param'); $*W.cur_lexpad().symbol($var.name, :scope('lexical')); } } method scope_declarator:sym($/) { make $.ast; $/.prune } method scope_declarator:sym($/) { make $.ast; $/.prune } method scope_declarator:sym($/) { make $.ast; $/.prune } method scoped($/) { make $ ?? $.ast !! $ ?? $.ast !! $ ?? $.ast !! $.ast; $/.prune; } method declarator($/) { make $ ?? $.ast !! $.ast; } method multi_declarator:sym($/) { make $ ?? $.ast !! $.ast; $/.prune } method multi_declarator:sym($/) { make $ ?? $.ast !! $.ast; $/.prune } method multi_declarator:sym($/) { make $.ast; $/.prune } method variable_declarator($/) { my $ast := $.ast; my $sigil := $; my $name := $ast.name; my $BLOCK := $*W.cur_lexpad(); my $*DECLARAND_ATTR; if $name && $BLOCK.symbol($name) { $/.panic("Redeclaration of symbol ", $name); } if $*SCOPE eq 'has' { # Initializer not allowed. if $ { $/.panic('Initiailizers not supported on has-scoped variables'); } # Locate the type of meta-attribute we need. unless $/.know_how($*PKGDECL ~ '-attr') { $/.panic("$*PKGDECL packages do not support attributes"); } # Set up arguments for meta-attribute instantiation. my %lit_args; my %obj_args; %lit_args := $name; if $ { %obj_args := $*W.find_sym([~$]); } if $sigil eq '$' || $sigil eq '&' { if $ { %obj_args := %obj_args; } else { try %obj_args := $*W.find_sym(['NQPMu']); } } # Add it. $*DECLARAND_ATTR := $*W.pkg_add_attribute($/.package, $/.how($*PKGDECL ~ '-attr'), %lit_args, %obj_args); $ast := QAST::Stmts.new(); } elsif $*SCOPE eq 'our' { # Depending on if this was already considered our scoped, # we may or may not have got a node in $var that's set up # right already. We build it here just to be sure. if $ { $/.panic("Cannot put types on our-scoped variables"); } $name := ~$; $ast := lexical_package_lookup([$name], $/); $BLOCK.symbol($name, :scope('package') ); if $ { $ast := QAST::Op.new( :op('bind'), $ast, $.ast ); } } else { my $type; my $default; if $ { unless $sigil eq '$' { $/.panic("Only typed scalars are currently supported in NQP"); } $type := $*W.find_sym([~$]); if nqp::objprimspec($type) -> $prim_spec { $default := default_value_for_prim($prim_spec); } else { $/.panic("Only native types are currently supported/checked"); } } else { $default := default_for($sigil); } $BLOCK[0].push(QAST::Op.new( :op('bind'), :node($/), QAST::Var.new( :name($name), :scope('lexical'), :decl('var'), :returns($type) ), $default )); if $ { $ast := QAST::Op.new( :op('bind'), :node($/), $ast, $.ast ); $ast.annotate('var_initialized', 1); } $BLOCK.symbol($name, :scope('lexical'), :type($type) ); } # Apply traits. if $ { for $ { $_.ast()($/); } } make $ast; $/.prune; $/.prune; } method initializer($/) { make $.ast; $/.prune; } method constant_declarator($/) { my $sym := ~$; my $type := $.ast.value; if $*SCOPE eq 'my' || $*SCOPE eq 'our' { $*W.install_lexical_symbol($*W.cur_lexpad(), $sym, $type); if $*SCOPE eq 'our' { $*W.install_package_symbol($*PACKAGE, [$sym], $type); } } else { $/.panic("Cannot have a $*SCOPE scoped constant"); } make QAST::WVal.new( :value($type) ); } method routine_declarator:sym($/) { make $.ast; $/.prune } method routine_declarator:sym($/) { make $.ast; $/.prune } method routine_def($/) { # If it's just got * as a body, make a multi-dispatch enterer. # Otherwise, need to build a sub. my $ast; my int $onlystar; if $ { $ast := only_star_block(); $onlystar := 1; } else { $ast := $.ast; if $*RETURN_USED { $ast[1] := wrap_return_handler($ast[1]); } } $ast.blocktype('declaration'); my $block := $ast; if $ { my $name := ~$ ~ $.ast; $ast.name($name); if $*SCOPE eq '' || $*SCOPE eq 'my' || $*SCOPE eq 'our' { if $*MULTINESS eq 'multi' { # Does the current block have a proto? if $*SCOPE eq 'our' { nqp::die('a multi can not be our-scoped') } my $proto; my %sym := $*W.cur_lexpad().symbol('&' ~ $name); if %sym { $proto := %sym; } # Otherwise, no candidate holder, so add one. else { # Check we have a proto in scope. my $found_proto; for $*W.get_legacy_block_list() { my %sym := $_.symbol('&' ~ $name); if %sym { $proto := %sym; $found_proto := 1; } elsif %sym { $/.panic("Cannot declare a multi when an only is already in scope."); } } # If we didn't find a proto, error for now. unless $found_proto { $/.panic("Sorry, no proto sub in scope, and auto-generation of protos is not yet implemented."); } # Set up dispatch routine in this scope. nqp::die("Dispatcher derivation NYI"); } # Create a code object and attach the signature. my $code := $*W.create_code($ast, $name, 0); attach_multi_signature($code, $ast); # Add this candidate to the proto. $proto.add_dispatchee($code); # Ensure we emit the code block. # XXX We'll mark it static so the code object inside the # proto is captured correctly. Technically this is wrong, # as the multi may be nested in another sub. $ast.blocktype('declaration_static'); my $BLOCK := $*W.cur_lexpad(); $BLOCK[0].push($ast); } elsif $*MULTINESS eq 'proto' { # Create a candidate list holder for the dispatchees # this proto will work over, and install them along # with the proto. if $*SCOPE eq 'our' { nqp::die('our-scoped protos not yet implemented') } my $code := $*W.create_code($ast, $name, 1, :$onlystar); my $BLOCK := $*W.cur_lexpad(); $BLOCK[0].push(QAST::Op.new( :op('bind'), QAST::Var.new( :name('&' ~ $name), :scope('lexical'), :decl('var') ), $ast )); $BLOCK.symbol('&' ~ $name, :scope('lexical'), :proto(1), :value($code), :declared(1) ); # Also stash the current lexical dispatcher and capture, for the {*} # to resolve. $block[0].push(QAST::Op.new( :op('bind'), QAST::Var.new( :name('CURRENT_DISPATCH_CAPTURE'), :scope('lexical'), :decl('var') ), QAST::Op.new( :op('savecapture') ) )); $block[0].push(QAST::Op.new( :op('bind'), QAST::Var.new( :name('&*CURRENT_DISPATCHER'), :scope('lexical'), :decl('var') ), QAST::Op.new( :op('getcodeobj'), QAST::Op.new( :op('curcode') ) ) )); } else { my $BLOCK := $*W.cur_lexpad(); $BLOCK[0].push(QAST::Op.new( :op('bind'), QAST::Var.new( :name('&' ~ $name), :scope('lexical'), :decl('var') ), $ast )); $BLOCK.symbol('&' ~ $name, :scope('lexical'), :declared(1)); if $*SCOPE eq 'our' { # Need to install it at loadinit time but also re-bind # it per invocation. $*W.install_package_routine($/.package, $name, $ast); $BLOCK[0].push(QAST::Op.new( :op('bind'), lexical_package_lookup([$name], $/), QAST::Var.new( :name('&' ~ $name), :scope('lexical') ) )); # Static code object needs re-capturing also, as it's # our-scoped. $ast.blocktype('declaration_static'); # Also need to make sure it gets a code object so it's # in the SC. $*W.create_code($ast, $name, 0); } } $ast := QAST::Var.new( :name('&' ~ $name), :scope('lexical') ); } else { $/.panic("$*SCOPE scoped routines are not supported yet"); } # Is it the MAIN sub? if $name eq 'MAIN' && $*MULTINESS ne 'multi' { $*MAIN_SUB := $block; } } else { if $*W.is_precompilation_mode() { $*W.create_code($ast, '', 0) } } make QAST::Op.new( :op('takeclosure'), $ast ).annotate_self('sink', $ast).annotate_self('block_ast', $block); # Apply traits. if $ { for $ { $_.ast()($/); } } $/.prune; } method method_def($/) { # If it's just got * as a body, make a multi-dispatch enterer. # Otherwise, build method block QAST. my $ast; my int $onlystar; my $package := $/.package; if $ { $ast := only_star_block(); $onlystar := 1; } else { $ast := $.ast; if $*RETURN_USED { $ast[1] := wrap_return_handler($ast[1]); } } $ast.blocktype('declaration_static'); # Always need an invocant. unless $ast.ann('signature_has_invocant') { $ast[0].unshift(QAST::Var.new( :name('self'), :scope('lexical'), :decl('param'), :returns($package) )); } $ast.symbol('self', :scope('lexical') ); # Install it where it should go (methods table / namespace). my $name := ""; if $ { $name := ~$ ~ ~$.ast; } elsif $ { if $*PKGDECL ne 'role' { $/.panic("Late-bound method name only valid in role"); } $name := "!!LATENAME!!" ~ ~$; } if $name ne "" { # Set name. $ast.name($name); # Insert it into the method table. my $meta_meth := $*MULTINESS eq 'multi' ?? 'add_multi_method' !! 'add_method'; my $is_dispatcher := $*MULTINESS eq 'proto'; my $code := $*W.create_code($ast, $name, $is_dispatcher, :$onlystar); if $*MULTINESS eq 'multi' { attach_multi_signature($code, $ast); } $*W.pkg_add_method($package, $meta_meth, $name, $code); $ast.annotate('code_obj', $code); # Install it in the package also if needed. if $*SCOPE eq 'our' { $*W.install_package_routine($package, $name, $ast); } # If it's a proto, also stash the current lexical dispatcher, for the {*} # to resolve. if $is_dispatcher { $ast[0].push(QAST::Op.new( :op('bind'), QAST::Var.new( :name('CURRENT_DISPATCH_CAPTURE'), :scope('lexical'), :decl('var') ), QAST::Op.new( :op('savecapture') ) )); $ast[0].push(QAST::Op.new( :op('bind'), QAST::Var.new( :name('&*CURRENT_DISPATCHER'), :scope('lexical'), :decl('var') ), QAST::Op.new( :op('getcodeobj'), QAST::Op.new( :op('curcode') ) ) )); } } # Install AST node in match object, then apply traits. make QAST::Op.new( :op('takeclosure'), $ast ).annotate_self( 'sink', $ast ).annotate_self( 'block_ast', $ast ).annotate_self('code_obj', $ast.ann('code_obj')); if $ { for $ { $_.ast()($/); } } $/.prune; } sub only_star_block() { my $ast := $*W.pop_lexpad(); $ast.push(QAST::Op.new( :op('invokewithcapture'), QAST::Op.new( :op('ifnull'), QAST::Op.new( :op('multicachefind'), QAST::Var.new( :name('$!dispatch_cache'), :scope('attribute'), QAST::Op.new( :op('getcodeobj'), QAST::Op.new( :op('curcode') ) ), QAST::WVal.new( :value($*W.find_sym(['NQPRoutine'])) ), ), QAST::Op.new( :op('usecapture') ) ), QAST::Op.new( :op('callmethod'), :name('dispatch'), QAST::Op.new( :op('getcodeobj'), QAST::Op.new( :op('curcode') ) ), QAST::Op.new( :op('savecapture') ) ) ), QAST::Op.new( :op('usecapture') ) )); $ast } sub attach_multi_signature($code_obj, $routine) { my $types := nqp::list(); my $definednesses := nqp::list(); for @($routine[0]) { if nqp::istype($_, QAST::Var) && $_.decl eq 'param' && !$_.named { $types.push($_.returns =:= NQPMu ?? nqp::null() !! $_.returns); my $defann := $_.ann('definedness'); $definednesses.push($defann eq 'D' ?? 1 !! $defann eq 'U' ?? 2 !! 0); } } $*W.set_routine_signature($code_obj, $types, $definednesses); } sub wrap_return_handler($ast) { QAST::Op.new( :op, $ast, 'RETURN', QAST::Op.new( :op ) ) } method signature($/) { my $BLOCK := $*W.cur_lexpad(); my $BLOCKINIT := $BLOCK[0]; if $ { my $inv := $.ast; $BLOCKINIT.push($inv); $BLOCKINIT.push(QAST::Op.new( :op('bind'), QAST::Var.new( :name('self'), :scope('lexical'), :decl('var') ), QAST::Var.new( :scope('lexical'), :name($inv.name) ) )); $BLOCK.annotate('signature_has_invocant', 1); } if $ { for $ { $BLOCKINIT.push($_.ast); } } $/.prune; } method parameter($/) { my $quant := $; my $ast; if $ { $ast := $.ast; if $quant ne '!' { $ast.default( default_for($) ); } } else { $ast := $.ast; if $quant eq '*' { $ast.slurpy(1); $ast.named( $ eq '%' ); } elsif $quant eq '?' { $ast.default( default_for($) ); } } if $ { if $quant eq '*' { $/.panic("Can't put default on slurpy parameter"); } if $quant eq '!' { $/.panic("Can't put default on required parameter"); } $ast.default( $[0].ast ); } unless $ast.default { $*W.cur_lexpad().arity( +$*W.cur_lexpad().arity + 1 ); } # Set the type of the parameter. if $ { my $type := $[0].ast.value; $ast.returns($type); if nqp::objprimspec($type) -> $prim { $*W.cur_lexpad().symbol($ast.name, :type($type)); if $ast.default && !$ { $ast.default(default_value_for_prim($prim)); } } } # Set definedness flag (XXX want a better way to do this). if $ { $ast.annotate('definedness', ~$[0]); } make $ast; $/.prune(); } method param_var($/) { my $name := ~$/; my $ast := QAST::Var.new( :name($name), :scope('lexical'), :decl('param'), :node($/) ); $*W.cur_lexpad().symbol($name, :scope('lexical') ); make $ast; } method named_param($/) { my $ast := $.ast; $ast.named( ~$ ); make $ast; } method typename($/) { # Try to locate the symbol. We'll emit a lookup via the SC so # the scope we emit code to do the symbol lookup in won't matter, # and so we can complain about non-existent type names. my @name := HLL::Compiler.parse_name(~$/); my $found := 0; try { my $sym := $*W.find_sym(@name); make QAST::WVal.new( :value($sym) ); $found := 1; } unless $found { $/.panic("Use of undeclared type '" ~ ~$/ ~ "'"); } $/.prune; } method trait($/) { make $.ast; $/.prune; } method trait_mod:sym($/) { if $ eq 'positional_delegate' { make -> $m { $*DECLARAND_ATTR.set_positional_delegate(1) }; } elsif $ eq 'associative_delegate' { make -> $m { $*DECLARAND_ATTR.set_associative_delegate(1) }; } elsif $ eq 'export' { make -> $match { my $ast := $match.ast; my $name := $ast.ann('block_ast').name; $*EXPORT.WHO.WHO{'&' ~ $name} := $ast.ann('code_obj') // $*W.create_code($ast.ann('block_ast'), $name, 0); }; } else { $/.panic("Trait '$' not implemented"); } $/.prune; } method regex_declarator($/, $key?) { my $name; my $package := $/.package; if $ { $name := ~$.ast; } else { if $*PKGDECL ne 'role' { $/.panic("Late-bound method name only valid in role"); } $name := "!!LATENAME!!" ~ ~$; } my $ast; if $ { $ast := QAST::Block.new( :name($name), QAST::Op.new( QAST::Var.new( :name('self'), :scope('local'), :decl('param') ), QAST::SVal.new( :value($name) ), :name('!protoregex'), :op('callmethod') ), :blocktype('declaration_static'), :node($/) ); $*W.pkg_add_method($package, 'add_method', $name, $*W.create_code($ast, $name, 0, :code_type_name)); } else { my $block := $*W.pop_lexpad(); $block[0].unshift(QAST::Var.new(:name, :scope, :decl)); $block[0].push(QAST::Op.new( :op('bind'), QAST::Var.new(:name, :scope, :decl ), QAST::Var.new( :name, :scope('lexical') ))); $block[0].push(QAST::Var.new(:name<$¢>, :scope, :decl)); $block[0].push(QAST::Var.new(:name<$/>, :scope, :decl)); $block.symbol('$¢', :scope); $block.symbol('$/', :scope); my $code := %*RX; my $regex := $/.slang_actions('Regex').qbuildsub($.ast, $block, code_obj => $code); $regex.name($name); if $*PKGDECL && nqp::can($package.HOW, 'add_method') { # Add the actual method, marking it as a static declaration # since it's reachable through the method table. $block.blocktype('declaration_static'); $*W.pkg_add_method($package, 'add_method', $name, $code); } # If this appears in a role, its NFA may depend on generic args. # If it does, we store the generic version of it. if $*PKGDECL eq 'role' { my $gen_nfa := QRegex::NFA.new(); $gen_nfa.addnode($.ast, :vars_as_generic); if $gen_nfa.generic { $code.SET_GENERIC_NFA($gen_nfa); } } # In sink context, we don't need the Regex::Regex object. $ast := QAST::Op.new( :op, :name, lexical_package_lookup(['NQPRegexMethod'], $/), $regex); $ast.annotate('sink', $regex); } make $ast; } method dotty($/) { my $ast := $ ?? $[0].ast !! QAST::Op.new( :node($/) ); if $ { $ast.unshift($.ast); $ast.op('callmethod'); } elsif $ eq 'HOW' { $ast.op('how'); } elsif $ eq 'WHAT' { $ast.op('what'); } elsif $ eq 'WHO' { $ast.op('who'); } elsif $ eq 'REPR' { $ast.op('reprname'); } else { $ast.name(~$); $ast.op('callmethod'); } make $ast; $/.prune; } ## Terms method term:sym($/) { make QAST::Op.new( :op('decont'), QAST::Var.new( :name('self'), :scope('lexical') ) ); } method term:sym($/) { my $ast := $.ast; $ast.name('&' ~ ~$); $ast.node($/); make $ast; $/.prune; } method term:sym($/) { # See if it's a lexical symbol (known in any outer scope). my $var; if $*W.is_lexical(~$) { unless $ { try { my $sym := $*W.find_sym([~$]); unless nqp::isnull(nqp::getobjsc($sym)) { $var := QAST::WVal.new( :value($sym) ); } } } unless $var { $var := QAST::Var.new( :name(~$), :scope('lexical') ); } } else { my @ns := nqp::clone($); unless $ { try { my $sym := $*W.find_sym(@ns); unless nqp::isnull(nqp::getobjsc($sym)) { $var := QAST::WVal.new( :value($sym) ); } } } unless $var { $var := lexical_package_lookup(@ns, $/); } } # If it's a call, add the arguments. my $ast := $var; if $ { $ast := $[0].ast; $ast.unshift($var); } make $ast; $/.prune; } method term:sym($/) { my $op := ~$; my @args := $ ?? $[0].ast.list !! []; if $op eq 'handle' || $op eq 'handlepayload' { my int $i := 1; my int $n := nqp::elems(@args); while $i < $n { @args[$i] := @args[$i].value; $i := $i + 2; } } my $ast := QAST::Op.new( :op($op), |@args, :node($/) ); make $ast; $/.prune; } method term:sym($/) { make QAST::Op.new( :op('const'), :name(~$) ); $/.prune; } method term:sym($/) { my $dc_name := QAST::Node.unique('dispatch_cap'); my $stmts := QAST::Stmts.new( QAST::Op.new( :op('bind'), QAST::Var.new( :name($dc_name), :scope('local'), :decl('var') ), QAST::Var.new( :name('CURRENT_DISPATCH_CAPTURE'), :scope('lexical') ) ), QAST::Op.new( :op('invokewithcapture'), QAST::Op.new( :op('ifnull'), QAST::Op.new( :op('multicachefind'), QAST::Var.new( :name('$!dispatch_cache'), :scope('attribute'), QAST::Var.new( :name('&*CURRENT_DISPATCHER'), :scope('lexical') ), QAST::WVal.new( :value($*W.find_sym(['NQPRoutine'])) ), ), QAST::Var.new( :name($dc_name), :scope('local') ) ), QAST::Op.new( :op('callmethod'), :name('dispatch'), QAST::Var.new( :name('&*CURRENT_DISPATCHER'), :scope('lexical') ), QAST::Var.new( :name($dc_name), :scope('local') ) ) ), QAST::Var.new( :name($dc_name), :scope('local') ) )); make QAST::Op.new( :op('locallifetime'), $stmts, $dc_name ); $/.prune; } method args($/) { make $.ast; } method arglist($/) { my $ast := QAST::Op.new( :op('call'), :node($/) ); if $ { my $expr := $.ast; if nqp::istype($expr, QAST::Op) && $expr.name eq '&infix:<,>' && !$expr.named { for $expr.list { $ast.push($_); } } else { $ast.push($expr); } } my $i := 0; my $n := +$ast.list; while $i < $n { if nqp::istype($ast[$i], QAST::Op) && $ast[$i].name eq '&prefix:<|>' { $ast[$i] := $ast[$i][0]; $ast[$i].flat(1); $ast[$i].named(1) if nqp::istype($ast[$i], QAST::Var) && nqp::eqat($ast[$i].name, '%', 0); } $i++; } make $ast; } method term:sym($/) { make $.ast; $/.prune; } method term:sym($/) { make $.ast; $/.prune; } method circumfix:sym<( )>($/) { make $ ?? $[0].ast !! QAST::Op.new( :op('list'), :node($/) ); $/.prune; } method circumfix:sym<[ ]>($/) { my $ast; if $ { $ast := $[0].ast; unless nqp::istype($ast, QAST::Op) && $ast.name eq '&infix:<,>' { $ast := QAST::Op.new( $ast, :op('list') ); } } else { $ast := QAST::Op.new( :op('list') ); } $ast.name('&circumfix:<[ ]>'); make $ast; $/.prune; } method circumfix:sym($/) { make $.ast; $/.prune } method circumfix:sym<« »>($/) { make $.ast; $/.prune } method circumfix:sym<{ }>($/) { if +$ > 0 { make QAST::Op.new( :op('takeclosure'), $.ast ).annotate_self('bareblock', 1); } elsif $ { make $.ast; } else { make default_for('%'); } $/.prune; } method coloncircumfix($/) { make $.ast } method semilist($/) { make $.ast; $/.prune; } method postcircumfix:sym<[ ]>($/) { make QAST::VarWithFallback.new( :scope('positional'), $.ast, :fallback(default_for('$')) ); } method postcircumfix:sym<{ }>($/) { make QAST::VarWithFallback.new( :scope('associative'), $.ast, :fallback(default_for('$')) ); } method postcircumfix:sym($/) { make QAST::VarWithFallback.new( :scope('associative'), $.ast, :fallback(default_for('$')) ); } method postcircumfix:sym<( )>($/) { make $.ast; } method value($/) { make $ ?? $.ast !! $.ast; $/.prune; } method number($/) { my $value := $ ?? $.ast !! $.ast; if ~$ eq '-' { $value := -$value; } make $ ?? QAST::NVal.new( :value($value) ) !! QAST::IVal.new( :value($value) ); $/.prune; } method quote:sym($/) { make $.ast; } method quote:sym($/) { make $.ast; } method quote:sym($/) { make $.ast; } method quote:sym($/) { make $.ast; } method quote:sym($/) { make $.ast; } method quote:sym($/) { my $block := $*W.pop_lexpad(); $block[0].push(QAST::Var.new(:name, :scope, :decl)); $block[0].push(QAST::Op.new( :op('bind'), QAST::Var.new(:name, :scope, :decl('var') ), QAST::Var.new( :name, :scope('lexical') ))); $block[0].push(QAST::Var.new(:name<$¢>, :scope, :decl('var'))); $block[0].push(QAST::Var.new(:name<$/>, :scope, :decl('var'))); $block.symbol('$¢', :scope); $block.symbol('$/', :scope); my $regex := $/.slang_actions('Regex').qbuildsub($.ast, $block); my $ast := QAST::Op.new( :op, :name, lexical_package_lookup(['NQPRegex'], $/), $regex); # In sink context, we don't need the Regex::Regex object. $ast.annotate('sink', $regex); make $ast; } method quote_escape:sym<$>($/) { make $.ast; } method quote_escape:sym<{ }>($/) { make QAST::Op.new( :op('stringify'), block_immediate($.ast), :node($/) ); } method quote_escape:sym($/) { make "\c[27]"; } ## Operators method postfix:sym<.>($/) { make $.ast; } method term:sym($/) { # This can go away in favor of nqp::const::CONTROL_RETURN after rebootstrap # of Moar and JVM backends. my $CONTROL_RETURN := 32; make QAST::Op.new( :op('throwpayloadlex'), QAST::IVal.new( :value($CONTROL_RETURN) ), $ ?? $.ast !! QAST::WVal.new( :value($*W.find_sym(['NQPMu']))) ); } method prefix:sym($/) { make QAST::Op.new( QAST::Var.new( :name('$/'), :scope('lexical') ), :op('callmethod'), :name('make'), :node($/) ); } method term:sym($/) { my $ast := QAST::Op.new( :op('control'), :name('next') ); if $ { $ast.push(QAST::WVal.new( :value($*W.find_sym([$])), :named('label') )); } make $ast } method term:sym($/) { my $ast := QAST::Op.new( :op('control'), :name('last') ); if $ { $ast.push(QAST::WVal.new( :value($*W.find_sym([$])), :named('label') )); } make $ast } method term:sym($/) { my $ast := QAST::Op.new( :op('control'), :name('redo') ); if $ { $ast.push(QAST::WVal.new( :value($*W.find_sym([$])), :named('label') )); } make $ast } method infix:sym<~~>($/) { make QAST::Op.new( :op, :name, :node($/) ); } # Takes a multi-part name that we know is in a package and generates # QAST to look it up using NQP package semantics. sub lexical_package_lookup(@name, $/) { # Catch empty names and die helpfully. if +@name == 0 { $/.panic("Cannot compile empty name"); } # The final lookup will always be just a keyed access to a # symbol table. my $final_name := @name.pop(); my $lookup := QAST::VarWithFallback.new( :scope('associative'), QAST::SVal.new( :value(~$final_name) ) ); # If there's no explicit qualification, then look it up in the # current package, and fall back to looking in GLOBAL. if +@name == 0 { $lookup.unshift(QAST::Op.new( :op('who'), QAST::Var.new( :name('$?PACKAGE'), :scope('lexical') ) )); $lookup.fallback(QAST::Op.new( :op('ifnull'), QAST::Op.new( :op('atkey'), QAST::Op.new( :op('who'), QAST::WVal.new( :value($*GLOBALish) ) ), QAST::SVal.new( :value(~$final_name) ) ), default_for(nqp::substr(~$final_name, 0, 1)))); } # Otherwise, see if the first part of the name is lexically # known. If not, it's in GLOBAL. Also, if first part is GLOBAL # then strip it off. else { my $path; if $*W.is_lexical(~@name[0]) { try { my $first := @name.shift(); $path := QAST::WVal.new( :value($*W.find_sym([$first])) ); CATCH { $path := QAST::Var.new( :name($first), :scope('lexical') ); } } } else { $path := QAST::WVal.new( :value($*GLOBALish) ); } if @name[0] eq 'GLOBAL' { @name.shift(); } for @name { my $path_temp := QAST::Node.unique('pkg_lookup_tmp'); $path := QAST::Stmts.new( QAST::Op.new( :op('bind'), QAST::Var.new( :name($path_temp), :scope('local'), :decl('var') ), $path ), QAST::Op.new( :op('if'), QAST::Op.new( :op('existskey'), QAST::Op.new( :op('who'), QAST::Var.new( :name($path_temp), :scope('local') ) ), QAST::SVal.new( :value(~$_) ) ), QAST::Op.new( :op('atkey'), QAST::Op.new( :op('who'), QAST::Var.new( :name($path_temp), :scope('local') ) ), QAST::SVal.new( :value(~$_) ) ), default_for('$') )); $path := QAST::Op.new( :op('locallifetime'), $path, $path_temp ); } $lookup.unshift(QAST::Op.new(:op('who'), $path)); my $sigil := nqp::substr(~$final_name, 0, 1); if $sigil eq '@' || $sigil eq '%' { my $viv_temp := QAST::Node.unique('pkg_viv_tmp'); $lookup[0] := QAST::Op.new( :op('bind'), QAST::Var.new( :name($viv_temp), :scope('local'), :decl('var') ), $lookup[0]); $lookup.fallback(QAST::Op.new( :op('bindkey'), QAST::Var.new( :name($viv_temp), :scope('local') ), $lookup[1], default_for($sigil) )); } else { $lookup.fallback(default_for($sigil)); } } return $lookup; } } class NQP::RegexActions is QRegex::P6Regex::Actions { method metachar:sym<:my>($/) { my $ast := $.ast; make QAST::Regex.new( $ast, :rxtype('qastnode'), :subtype('declarative'), :node($/) ); } method metachar:sym<{ }>($/) { make QAST::Regex.new( $.ast, :rxtype, :node($/) ); } method metachar:sym($/) { make QAST::Regex.new( QAST::NodeList.new( QAST::SVal.new( :value('!INTERPOLATE') ), $.ast, QAST::IVal.new( :value($*SEQ ?? 1 !! 0) ) ), :rxtype, :subtype, :node($/)); } method assertion:sym<{ }>($/) { make QAST::Regex.new( QAST::NodeList.new( QAST::SVal.new( :value('!INTERPOLATE_REGEX') ), $.ast), :rxtype, :subtype, :node($/)); } method assertion:sym($/) { make QAST::Regex.new( $.ast, :subtype, :negate( $ eq '!' ), :rxtype, :node($/) ); } method assertion:sym($/) { make QAST::Regex.new( QAST::NodeList.new( QAST::SVal.new( :value('!INTERPOLATE_REGEX') ), $.ast), :rxtype, :subtype, :node($/)); } method codeblock($/) { my $block := $.ast; $block.blocktype('immediate'); my $ast := QAST::Stmts.new( QAST::Op.new( :op('bind'), QAST::Var.new( :name('$/'), :scope('lexical') ), QAST::Op.new( QAST::Var.new( :name('$¢'), :scope('lexical') ), :name('MATCH'), :op('callmethod') ) ), $block ); make $ast; } method assertion:sym($/) { my $name := ~$; my $qast; if $ { $qast := $.ast; if $qast.rxtype eq 'subrule' { self.subrule_alias($qast, $name); } else { $qast := QAST::Regex.new( $qast, :name($name), :rxtype, :node($/) ); } } elsif $name eq 'sym' { my str $fullrxname := %*RX; my str $rxname := ""; my int $loc := nqp::index($fullrxname, ':sym'); if $loc >= 0 { $rxname := nqp::substr($fullrxname, $loc + 5 ); $rxname := nqp::substr( $rxname, 0, nqp::chars($rxname) - 1); } else { $loc := nqp::index($fullrxname, ':'); my $angleloc := nqp::index($fullrxname, '<', $loc); $angleloc := nqp::index($fullrxname, '«', $loc) if $angleloc < 0; $rxname := nqp::substr($fullrxname, $loc + 1, $angleloc - $loc - 1) unless $loc < 0; } if $loc >= 0 { $qast := QAST::Regex.new(:name('sym'), :rxtype, :node($/), QAST::Regex.new(:rxtype, $rxname, :node($/))); } else { self.panic(" only valid in multiregexes"); } } else { $qast := QAST::Regex.new(:rxtype, :subtype, :node($/), :name($name), QAST::NodeList.new( QAST::SVal.new( :value($name) ) ) ); if $ { for $.ast.list { $qast[0].push( $_ ) } } elsif $ { if $name eq 'after' { my int $litlen := self.offset_ast($.ast); if $litlen >= 0 { $qast[0][0].value('before'); $qast[0].push(self.qbuildsub($.ast, :anon(1), :addself(1))); $qast[0].push(QAST::IVal.new( :value($litlen) )); # optional offset to before } else { $qast[0].push(self.qbuildsub(self.flip_ast($.ast), :anon(1), :addself(1))); } } else { $qast[0].push(self.qbuildsub($.ast, :anon(1), :addself(1))); } } } make $qast; } method arglist($/) { make $.ast } method arg($/) { make $.ast; } method create_regex_code_object($block) { my $code := $*W.create_code($block, '', 0, :code_type_name); if nqp::existskey(%*RX, 'code') { %*RX.ADD_NESTED_CODE($code); } $code } method store_regex_nfa($code_obj, $block, $nfa) { $code_obj.SET_NFA($nfa.save); } method store_regex_caps($code_obj, $block, %caps) { $code_obj.SET_CAPS(%caps); } method store_regex_alt_nfa($code_obj, $block, $key, @alternatives) { my @saved; for @alternatives { @saved.push($_.save(:non_empty)); } $code_obj.SET_ALT_NFA($key, @saved); } method set_cursor_type($qast) { my $cursor_type := nqp::null(); try { $cursor_type := $*W.find_sym(['NQPMatch']); }; $qast.cursor_type($cursor_type) unless nqp::isnull($cursor_type); } } nqp-2018.03/src/NQP/Compiler.nqp0000644000175000017500000000265413253717146014670 0ustar alexalexuse QRegex; class NQP::Compiler is HLL::Compiler { method optimize($ast, *%adverbs) { %adverbs eq 'off' ?? $ast !! NQP::Optimizer.new.optimize($ast, |%adverbs) } } # Create and configure compiler object. my $nqpcomp := NQP::Compiler.new(); $nqpcomp.language('nqp'); $nqpcomp.parsegrammar(NQP::Grammar); $nqpcomp.parseactions(NQP::Actions); hll-config($nqpcomp.config); $nqpcomp.addstage('optimize', :after); # Add extra command line options. my @clo := $nqpcomp.commandline_options(); @clo.push('parsetrace'); @clo.push('setting=s'); @clo.push('setting-path=s'); @clo.push('custom-regex-lib=s'); @clo.push('module-path=s'); @clo.push('no-regex-lib'); @clo.push('stable-sc'); @clo.push('optimize=s'); #?if jvm @clo.push('javaclass=s'); @clo.push('bootstrap'); $nqpcomp.addstage('classname', :after); #?endif #?if moar @clo.push('vmlibs=s'); @clo.push('bootstrap'); #?endif #?if js @clo.push('nyi=s'); #?endif #?if moar # XXX FIX ME sub MAIN(@ARGS) { #?endif #?if jvm sub MAIN(*@ARGS) { #?endif #?if js sub MAIN(*@ARGS) { #?endif # Enter the compiler. $nqpcomp.command_line(@ARGS, :encoding('utf8'), :transcode('ascii iso-8859-1')); # Uncomment below to dump cursor usage logging (also need to uncomment two lines # in src/QRegex/Cursor.nqp, in !cursor_start_cur and !cursor_start_all). #ParseShared.log_dump(); # Close event logging $nqpcomp.nqpevent(); } nqp-2018.03/src/NQP/Grammar.nqp0000644000175000017500000006552413253717146014511 0ustar alexalexgrammar NQP::Grammar is HLL::Grammar { method TOP() { # Language braid. my $*LANG := self; self.define_slang('MAIN', self, self.actions); self.define_slang('Regex', NQP::Regex, NQP::RegexActions); # Old language braids, going away. my %*LANG; %*LANG := NQP::Regex; %*LANG := NQP::RegexActions; %*LANG
:= NQP::Grammar; %*LANG := NQP::Actions; # Package declarator to meta-package mapping. Note that there is # one universal KnowHOW from the 6model core, and an attribute # meta-object to go with it. self.set_how('knowhow', nqp::knowhow()); self.set_how('knowhow-attr', nqp::knowhowattr()); # Serialization context builder - keeps track of objects that # cross the compile-time/run-time boundary that are associated # with this compilation unit. my $file := nqp::getlexdyn('$?FILES'); my $source_id := nqp::sha1(self.target()) ~ (%*COMPILING<%?OPTIONS> ?? '' !! '-' ~ ~nqp::time_n()); my $*W := nqp::isnull($file) ?? NQP::World.new(:handle($source_id)) !! NQP::World.new(:handle($source_id), :description($file)); my $*SCOPE := ''; my $*MULTINESS := ''; my $*PKGDECL := ''; my $*INVOCANT_OK := 0; my $*RETURN_USED := 0; my $*CONTROL_USED := 0; my $*IN_REGEX_ASSERTION := 0; my %*HANDLERS; self.comp_unit; } ## Lexer stuff token identifier { <.ident> [ <[\-']> <.ident> ]* } token name { ['::']* } token deflongname { ? } token ENDSTMT { [ <.unv>? $$ <.ws> ]? } token ws { || || [ \v+ | '#' \N* | ^^ <.pod_comment> | \h+ ]* } token unv { # :dba('horizontal whitespace') [ | ^^ <.pod_comment> | \h* '#' \N* | \h+ ] } token pod_comment { ^^ \h* '=' [ | 'begin' \h+ 'END' >> [ .*? \n \h* '=' 'end' \h+ 'END' » \N* || .* ] | 'begin' \h+ [ || .*? \n \h* '=' 'end' \h+ $ » \N* || <.panic: '=begin without matching =end'> ] | 'begin' » \h* [ $$ || '#' || <.panic: 'Unrecognized token after =begin'> ] [ || .*? \n \h* '=' 'end' » \N* || <.panic: '=begin without matching =end'> ] | {} .*? \n }/=cut), please use =begin/=end instead"> ]? | \n ]> | {} [ \s || <.panic: 'Illegal pod directive'> ] \N* ] } ## Top-level rules token comp_unit { :my $*IN_DECL := ''; :my $*HAS_YOU_ARE_HERE := 0; :my $*MAIN_SUB; :my $*UNIT := $*W.push_lexpad($/); # Create GLOBALish - the current GLOBAL view, created fresh # for each compilation unit so we get separate compilation. :my $*GLOBALish := $*W.pkg_create_mo(self.how('knowhow'), :name('GLOBALish')); { $*GLOBALish.HOW.compose($*GLOBALish); $*W.install_lexical_symbol($*UNIT, 'GLOBALish', $*GLOBALish); } # This is also the starting package. :my $*PACKAGE := $*GLOBALish; { $/.set_package($*PACKAGE); $*W.install_lexical_symbol($*UNIT, '$?PACKAGE', $*PACKAGE); } # Create EXPORT::DEFAULT. :my $*EXPORT; { unless %*COMPILING<%?OPTIONS> eq 'NULL' { $*EXPORT := $*W.pkg_create_mo(self.how('knowhow'), :name('EXPORT')); $*EXPORT.HOW.compose($*EXPORT); $*W.install_lexical_symbol($*UNIT, 'EXPORT', $*EXPORT); my $DEFAULT := $*W.pkg_create_mo(self.how('knowhow'), :name('DEFAULT')); $DEFAULT.HOW.compose($DEFAULT); ($*EXPORT.WHO) := $DEFAULT; } } { $*W.add_initializations(); } <.outerctx> <.set_braid_from(self)> <.check_PACKAGE_oopsies('comp_unit')> [ $ || <.panic: 'Confused'> ] } rule statementlist { :my $*LANG := self; '' [ | $ | > | [ <.eat_terminator> ]* ] } token label { ':' <.ws> { $*LABEL := ~$; my $label := $*W.find_sym(['NQPLabel']).new(); $*W.add_object($label); $*W.install_lexical_symbol($*W.cur_lexpad(), $*LABEL, $label); } } token statement($*LABEL = '') { | $ > [ |