├── .envrc ├── CODEOWNERS ├── internal ├── deps │ ├── pull.go │ ├── deps.go │ ├── glj.go │ ├── get.go │ └── embed.go ├── persistent │ └── vector │ │ ├── testdata │ │ └── fuzz │ │ │ └── FuzzTransient │ │ │ └── e94aecaacbdb36f657e0f2350dd640e7b7f05ca739adbfa945fdbb61ace36369 │ │ └── doc.go ├── goid │ ├── goid.go │ └── goid_wasm.go ├── seq │ └── seq.go └── murmur3 │ └── murmur3.go ├── pkg ├── runtime │ ├── testdata │ │ ├── eval │ │ │ ├── stdlib02.glj │ │ │ ├── stdlib02.out │ │ │ ├── testtab.out │ │ │ ├── functional00.out │ │ │ ├── prog00.out │ │ │ ├── seq00.out │ │ │ ├── sort00.out │ │ │ ├── def01.out │ │ │ ├── interop04.out │ │ │ ├── let00.out │ │ │ ├── macros00.out │ │ │ ├── def00.out │ │ │ ├── interop02.out │ │ │ ├── reduceslice00.out │ │ │ ├── char00.out │ │ │ ├── fnarity00.out │ │ │ ├── def01.glj │ │ │ ├── interop03.out │ │ │ ├── print.out │ │ │ ├── functions00.out │ │ │ ├── require00.out │ │ │ ├── print.glj │ │ │ ├── quasiquote00.glj │ │ │ ├── interop04.glj │ │ │ ├── math00.out │ │ │ ├── prog02.out │ │ │ ├── quasiquote00.out │ │ │ ├── case00.out │ │ │ ├── char00.glj │ │ │ ├── quote00.out │ │ │ ├── interop02.glj │ │ │ ├── reduceslice00.glj │ │ │ ├── functions01.out │ │ │ ├── interop01.out │ │ │ ├── stdlib01.out │ │ │ ├── functions01.glj │ │ │ ├── prog00.glj │ │ │ ├── vector00.out │ │ │ ├── testtab.glj │ │ │ ├── def00.glj │ │ │ ├── math00.glj │ │ │ ├── fnarity00.glj │ │ │ ├── functional00.glj │ │ │ ├── quote00.glj │ │ │ ├── interop00.out │ │ │ ├── interop03.glj │ │ │ ├── stdlib01.glj │ │ │ ├── let00.glj │ │ │ ├── stdlib00.out │ │ │ ├── interop01.glj │ │ │ ├── require00.glj │ │ │ ├── vector00.glj │ │ │ ├── macros00.glj │ │ │ ├── prog02.glj │ │ │ ├── sort00.glj │ │ │ ├── case00.glj │ │ │ ├── functions00.glj │ │ │ ├── seq00.glj │ │ │ ├── bool00.out │ │ │ ├── stdlib00.glj │ │ │ ├── bool00.glj │ │ │ └── interop00.glj │ │ ├── codegen │ │ │ └── test │ │ │ │ ├── ns_skip.glj │ │ │ │ ├── const_keyword.glj │ │ │ │ ├── const_string.glj │ │ │ │ ├── const_number.glj │ │ │ │ ├── quote_simple.glj │ │ │ │ ├── the_var.glj │ │ │ │ ├── with_meta.glj │ │ │ │ ├── throw_simple.glj │ │ │ │ ├── maybe_class.glj │ │ │ │ ├── ref.glj │ │ │ │ ├── values.glj │ │ │ │ ├── loop_simple.glj │ │ │ │ ├── letfn.glj │ │ │ │ ├── def_simple.glj │ │ │ │ ├── fn_closure.glj │ │ │ │ ├── fn_recur.glj │ │ │ │ ├── case_switch.glj │ │ │ │ ├── set_bang.glj │ │ │ │ ├── multifn.glj │ │ │ │ ├── goroutine.glj │ │ │ │ ├── regex_literal.glj │ │ │ │ ├── ns_skip │ │ │ │ └── load.go.out │ │ │ │ ├── try_advanced.glj │ │ │ │ ├── try_basic.glj │ │ │ │ ├── const_keyword │ │ │ │ └── load.go.out │ │ │ │ ├── const_string │ │ │ │ └── load.go.out │ │ │ │ ├── const_number │ │ │ │ └── load.go.out │ │ │ │ ├── throw_simple │ │ │ │ └── load.go.out │ │ │ │ └── quote_simple │ │ │ │ └── load.go.out │ │ └── eval_error │ │ │ ├── let00.in │ │ │ ├── let01.in │ │ │ ├── stack00.in │ │ │ └── invalid_set.in │ ├── scope.go │ ├── nsloaders.go │ └── readeval.go ├── reader │ ├── testdata │ │ ├── reader │ │ │ ├── bigint00.glj │ │ │ ├── bigint00.out │ │ │ ├── set00.glj │ │ │ ├── set00.out │ │ │ ├── shebang.out │ │ │ ├── bigfloat00.glj │ │ │ ├── hex00.glj │ │ │ ├── hex00.out │ │ │ ├── strings0.glj │ │ │ ├── strings0.out │ │ │ ├── bigfloat00.out │ │ │ ├── nil00.glj │ │ │ ├── nil00.out │ │ │ ├── core004.glj │ │ │ ├── ignoreform00.out │ │ │ ├── multiline.out │ │ │ ├── numbers00.out │ │ │ ├── symbolic00.out │ │ │ ├── map00.out │ │ │ ├── numbers00.glj │ │ │ ├── comment00.out │ │ │ ├── float00.glj │ │ │ ├── shebang.glj │ │ │ ├── core003.glj │ │ │ ├── map01.out │ │ │ ├── core000.out │ │ │ ├── core003.out │ │ │ ├── char.out │ │ │ ├── multiline.glj │ │ │ ├── symbolic00.glj │ │ │ ├── core002.glj │ │ │ ├── float00.out │ │ │ ├── map00.glj │ │ │ ├── readcond.out │ │ │ ├── map01.glj │ │ │ ├── readcondunusual.out │ │ │ ├── basic00.glj │ │ │ ├── readcondsplicing.out │ │ │ ├── basic00.out │ │ │ ├── char.glj │ │ │ ├── comment00.glj │ │ │ ├── core004.out │ │ │ ├── core001.out │ │ │ ├── quasiquote00.glj │ │ │ ├── core000.glj │ │ │ ├── ignoreform00.glj │ │ │ ├── readcondunusual.glj │ │ │ ├── basic01.out │ │ │ ├── readcond.glj │ │ │ ├── readcondsplicing.glj │ │ │ ├── basic01.glj │ │ │ ├── quasiquote00.out │ │ │ └── core001.glj │ │ ├── fuzz │ │ │ ├── FuzzRead │ │ │ │ ├── 48c07ddc40007f0b │ │ │ │ ├── 212816a63358983b │ │ │ │ ├── f21bc1b3b9bfb37d │ │ │ │ ├── b27cc02acdd412b9 │ │ │ │ └── 7dac166db0afb1bc │ │ │ └── FuzzCLJConformance │ │ │ │ ├── 02d4bc9f4e567048ebe719db6d94d22ed0788a56e3bdf4aa1fa3a546b5a681a4 │ │ │ │ ├── 1d43ee52085cb4aad7154239440cb299d4a48f78d96d63fe26ea00c7dcb9e698 │ │ │ │ ├── 31bb42ef51bae0f76b5b9dd265fac08fb8b28665c63e940824755a75e73a205d │ │ │ │ ├── 3a24a4b99c37a98576562a3f7f431e56d72d8d0c681577a6fd6fdc01053de065 │ │ │ │ ├── 3def8fda4a9dc5128964954a6474893faa18048a6c1324e2c21a62eaad2ed446 │ │ │ │ ├── 483ca9154683fc8926f40bae096c030463ba51b0b91082f98802937933b7fa63 │ │ │ │ ├── 48ef306e49107c071b90d32fc4a2b9677fb2a177cb7dad51580eb15639475714 │ │ │ │ ├── 4d625360ee9191e5cffd04495127be328f03c1afc4164c857dcc9f1ae3abab3e │ │ │ │ ├── 52aa6175d6125a3f5063f3b1d236b39843653b08f7220e26c7be988cd74ea077 │ │ │ │ ├── 55de2f97915e5dde3530fb6b0db159bc170199a237a8f275c884df1536224f71 │ │ │ │ ├── 5943f9224b12cfd579d0653db31663f9843f66874a589ae0a23564c860c2d720 │ │ │ │ ├── 5c19709a556b49091409d495c88b21d4aee47cc9718542737e1ec15894b6883a │ │ │ │ ├── 68c711c91a705bfe3284f0b678d9cdcba431ae50d418b3511fcb78b71785fff0 │ │ │ │ ├── 771e938e4458e983a736261a702e27c7a414fd660a15b63034f290b146d2f217 │ │ │ │ ├── 8643a79bab153d1be09b197e5c55f603de7e1e5e621f40e0ed3b0a78c72577b3 │ │ │ │ ├── 901ac8d1fcb285b2648ccd3ebdc0870184c5450cf6b4e64b8679674ae49f4216 │ │ │ │ ├── 93076e9d6b6510b69fe6b4892585737d8f59f77af8c519662c3e42c70fd291cc │ │ │ │ ├── 9cd6a05bc1ae5acbe271feaa951795361decc9602f8dd65a3f981906fb5387c0 │ │ │ │ ├── 9cd9b70960b4a733b309f718204b9e2fc9096e34d36e37538cf8eb3e975347ad │ │ │ │ ├── a27a375a64900aed02211ffab0e408801415706b848eae4c08d57d60ba0b458f │ │ │ │ ├── a7284e0107a1b9c47f4660ff283add8437e956cef83f2a2d8156aa54def53f6a │ │ │ │ ├── abff33899e7cd4f9c816358b8dea680db5b9bfd8032464c08c33ae5ca4f5baab │ │ │ │ ├── b158e8273e40dac4165aef51dfff14afc647c9a2a3117f91f5df0c5fc22bde3c │ │ │ │ ├── bc06c83720e3dbbfbc44a8a95242076759a1c24f7d3b5ebfef9e788fa2917076 │ │ │ │ ├── bca2c1ea57d0749a2032539d6db0b89a879d9fada415cdcf76f5e859c1755cf7 │ │ │ │ ├── bf5ee1bf3f1fb87edcbcd435f8bf98cb469d3fe8cf4ee9a300b9f9b369bee52e │ │ │ │ ├── c2f7992b1414c3d48a8fd9d2435590e89a7f19696e5ab036902f7906750c6190 │ │ │ │ ├── c908d15e31b0b6890e437b7d9b33e407fba3b18415fcceb6cfbc724cca7ab8a7 │ │ │ │ ├── cb1240a631c35f1022d4246e951fbc9399c61669e0d974a31737486f1ac22eb7 │ │ │ │ ├── d9b1ca1afa03e9a8808db7d85d3856a4b4c16342dd98e0fc9c074fee43bf6431 │ │ │ │ ├── dc9ce74647d8ec8794d72d6d600e6e6913536f02e1709e7ea509a9e95e6c4bde │ │ │ │ ├── e1bebd2820af67a54c100a7a0783a029cdc0b33825aeb823d250e0cb2e9d6bdd │ │ │ │ ├── ed853cd85118209a87c0dfa0851a94c355d94ed694ddfdb8a0c28b23d3915d61 │ │ │ │ ├── f2f1bb2b4123163cc61f3eb116a2b878980a3d9825af646dcbd9422b1f0a9903 │ │ │ │ ├── 4c76574832cd26648a52e7a31fc9bae6aa08247b2b1dd2f3e0cb2fb021585b92 │ │ │ │ ├── c5be14bdbf4455ba10edc2089caac62bb8d680ca3ce84c0bb5c5d65ec438094a │ │ │ │ ├── 0d61a1dbd59935194751d448f2c2f626154cf7d8175f111d72f3995d873483d0 │ │ │ │ ├── 4a021516918d4410b80c80af5d5f442b6cc356358b7bd6b748f96c92234d551c │ │ │ │ ├── 0d40b8d4398e177684afa1c78fd95d9b348d7125c248de27028a7a4c766340d0 │ │ │ │ └── fa8fa90d3f58681a069ffff014c397d6159904cd6e4a0c44888341ef72724323 │ │ ├── reader_error │ │ │ ├── unexpected00.glj │ │ │ ├── string00.glj │ │ │ ├── string01.glj │ │ │ ├── char00.glj │ │ │ ├── bad_reader_cond.glj │ │ │ ├── oddconditional.glj │ │ │ ├── dispatch00.glj │ │ │ └── bad_namespace_map00.glj │ │ ├── clj-cache │ │ │ └── read │ │ │ │ ├── 1816bb27c4c92717c970297a3c23f8f4ef85e4b53e79f8d06db691098a507592.glj │ │ │ │ ├── 3ac179851354cf9a588d482e1fdf439b640f0017d9e48efa6944fab4c787f4ad.glj │ │ │ │ ├── 522bf19c1792fca7b20f2e60c63a67e0f510483a3f99da0c6ea38e9ef046bd94.glj │ │ │ │ ├── 5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9.glj │ │ │ │ ├── 7a3e6b16cb75f48fb897eff3ae732f3154f6d203b53f33660f01b4c3b6bc2df9.glj │ │ │ │ ├── 8a5edab282632443219e051e4ade2d1d5bbc671c781051bf1437897cbdfea0f1.glj │ │ │ │ ├── d9a847a1a79ab44825bbf8e5720bca771d53d1344d871bf9be1567382a022718.glj │ │ │ │ ├── f1534392279bddbf9d43dde8701cb5be14b82f76ec6607bf8d6ad557f60f304e.glj │ │ │ │ ├── f755b7c0125cdd02920bc8cdc6795fd25eeeaa08b072367695fc2055c6920c1e.glj │ │ │ │ ├── 17576d04a48f54985c12ebc1d2b2cfafcb6d27445b37136e1fd9c012527b9a9e.glj │ │ │ │ ├── 18cd36b2b7bdd213791af85934f3f09c0388f67340cd035a1f3632ed6c4c244b.glj │ │ │ │ ├── 19e89348f2a9d5f3d0c5fca8e2a7068d9c5d71687a355f759009a5ed2527eb2c.glj │ │ │ │ ├── 537838579ebd2316a949dba9a7c6c95ab79d198348130479b2bdd5fdb77f97b0.glj │ │ │ │ ├── 7cf33476e7cff8b85c6eff7cd370f29253b2121045ac88f190714c42e61648b1.glj │ │ │ │ ├── 86f80fa2ae598a66c489b827703b248e40bb3bba20ca04ce2d1b134999ae087f.glj │ │ │ │ ├── 892f6e09c02c35b5381a165395599f21770c19b4f05d2ed71ccd2a45af414b1c.glj │ │ │ │ ├── 9466e130e88346e09e754f11f5539093f6df5223fe9922c21323ebab98fd518b.glj │ │ │ │ ├── 98345a97b187dd1b1d9c14c9fb65a5a03123b39b5d0225ffff27f7968e01baba.glj │ │ │ │ ├── a6ad39efc4146b973475110caace1ca3ddc5757e1990274af759acce54af7a74.glj │ │ │ │ ├── a7315e659d891864a2c43206529d9cd6ff5905e95edaf5b03f11373cd93d81ae.glj │ │ │ │ ├── ae2b22faeae432b15083761c19a42d3b12c1d967e225a44854db345fe545ef43.glj │ │ │ │ ├── bfd59979737de1a540673045c3adb336332c2c205d2c5fbaff6b1c7a9f3fc9ef.glj │ │ │ │ ├── c00e2650a826a4886c2bab00b70f6f3a00f8e1590c8cadc560ef493c3562028c.glj │ │ │ │ ├── ce5829e1a023d49c88eca9ff6812ded618d4c3ed0d2a572b545609b9c4983153.glj │ │ │ │ ├── cf9ec8eb3fae228a832bb4e79983f4276cc29937092963844c402eb1daf51dd5.glj │ │ │ │ ├── f0092274c1b6d874b85112c3f3105eebfe094e8ac93ae4205983bcc9c2a98a77.glj │ │ │ │ ├── 7a7c9bc75f4202399d3acbe523ddf827d14e429c0242eb3323033115e9f70e8c.glj │ │ │ │ ├── f72460b27e8859789e9de498997d25154a6bf23348496dc6c19bc1510bce38b6.glj │ │ │ │ ├── 7e4515b7bece699b0593b3ae77cc33c07583566bac75e7376aa85236dfee421a.glj │ │ │ │ ├── e014bb4c05531ad4d5cecaf1c0e4164122fc50966cb3e2b6242f9e9317dc6d7c.glj │ │ │ │ ├── 1c92f033fb8eabff5381b8eb26e5106d3b15acb67b35df470434501863d5c4b0.glj │ │ │ │ ├── 86dfc0c18ea77232f4958ff59c0bc43fb678e4b8ed937fc39e0d2ac95ec07cb5.glj │ │ │ │ ├── ca0978f602106306f9205296c326e9a064767ea92d6f2989e33ed0c5a355d3da.glj │ │ │ │ ├── 9a6940e4e50dd0fa87479a53b76c8e421b7cac3edc24581e0d9de1a513188fae.glj │ │ │ │ ├── 580e62d910e87c00a12cca2e2092507f00d916dbccb06d2f2b1047f3fe20443c.glj │ │ │ │ ├── d31676b6e22bb403dac0099b7069b4c1f54f3e1edab582328eec835ca4db5a65.glj │ │ │ │ ├── db2d18ebe5b8d84771f5dbc61b6767e4ceac6b79e25b23e4b009eae8fdbdcd26.glj │ │ │ │ ├── c7ca714eca149d2f3c131e85e902718498a6804ca5aec211f93a059b946f40e0.glj │ │ │ │ ├── ef0306541d2c08e833060aeddc13f80cfd1c009c497796bf7407048538be81df.glj │ │ │ │ ├── 7e53e99ddf3f4633fc756c3ab0c1ed3a626df352e25980f958d4cedac2a7aab3.glj │ │ │ │ ├── 8488b046fc941dff7d38fef7d35530ca809704737e3c19966a419a64ce2baee2.glj │ │ │ │ ├── edcfcd1d0ea9bc946ceacc01583775040b571d4022f659a96bd95495f5f06f75.glj │ │ │ │ ├── 64c6501ad7d25f9467390fdbe37ff9025a72f2976f07850c22e071704e00297f.glj │ │ │ │ ├── 1c864710144023cf5ab9669c361dbe90d2b18d18c76b86183a572eb8e9d0264e.glj │ │ │ │ ├── 36a9e7f1c95b82ffb99743e0c5c4ce95d83c9a430aac59f84ef3cbfab6145068.glj │ │ │ │ ├── 45822f54a0d0e030f75698c9252f1345982e743c9466806048f542467b3ca4f9.glj │ │ │ │ ├── 6a699d31f54deb4cb7de7c507420ce32f6c71298d58a1178bdb7ad3051910497.glj │ │ │ │ ├── 06c901ba18e7c2418ebcfc5e2ed24023e8e711547233e135641597d9738e3ee1.glj │ │ │ │ ├── 5442aa159e97a9592740654d29dca1201b7cfb3c1f1ffe1cc6ee9f9a95170ca5.glj │ │ │ │ ├── 71546855d6279ef70d20909b292c42c2dcb02cd06bde01485da52d13e304ebf4.glj │ │ │ │ ├── 90c2120b8a77bd7ed9e949639bbbeb9be53852d0e4671a1fdcb55b3c99d19ae5.glj │ │ │ │ ├── c3641f8544d7c02f3580b07c0f9887f0c6a27ff5ab1d4a3e29caf197cfc299ae.glj │ │ │ │ ├── fdf6e585b66f6d5c79b90b044c834be4120ffd68e7b97a695ea6e0465c5c65ab.glj │ │ │ │ ├── 323783be9a53a31e158ec9600626a4703e99f4e183bc1acb8772cbdf5c3a1ece.glj │ │ │ │ ├── 03a51bd6760f743e8fe6ea0488a26c58043fc41dd54b7dd606e585084b280a2b.glj │ │ │ │ ├── 07bef2206a89b8ddd31002970520c9aae135576c12aaa5fe72c96de3b217157d.glj │ │ │ │ ├── 1d390f0b8303d9a29c38041266c7db7a60677d273f083132c39a5041c4fc0f1f.glj │ │ │ │ ├── 32b6854d07aefbdacaf7dc8c7b391d3b6f3d1b487873d38508a8841daefc3010.glj │ │ │ │ ├── b595ba2f09697488cf65428142351adb60a5b947b514f82364637a047bef87b2.glj │ │ │ │ ├── e48504b10d842f7ae2e4c9ab110dbdcca844fac4523f3614781674f43a5fe7ce.glj │ │ │ │ ├── f18e9301eae406bbc26e46022df9bdf29f1b6393f8a2f47f03961797a6897d4e.glj │ │ │ │ ├── f2a0fd79585924076c10bc6a4adf4dbe0d06951e6d6950f07387ba999ae4a5c1.glj │ │ │ │ ├── a00644508de97e85263c9d0cd67e63d3c3b5d733bcaddc65b110e501ad5a7b73.glj │ │ │ │ └── 30e28cb5f9df7ee57fbbf9f7f81765043d2e69a56db713e107d8f32c34c207bb.glj │ │ └── clj-equiv-cache │ │ │ ├── 7e6073968842178d176dfbc5042b5193e9c4c5bafa339d8908fe600a941c63fa │ │ │ └── efe5931ea5c44cd13420973e881027e0261414ca43a4872137e3508ebff9ca58 │ └── resolver.go ├── glj │ ├── doc.go │ ├── read.go │ ├── glj_test.go │ ├── init-aot.go │ ├── var.go │ └── init.go ├── lang │ ├── namespace_test.go │ ├── persistenttreemap.go │ ├── lib.go │ ├── afn.go │ ├── truthiness_test.go │ ├── string.go │ ├── lazyseq_test.go │ ├── box.go │ ├── reduced.go │ ├── keyword_test.go │ ├── truthiness.go │ ├── volatile.go │ ├── ifn.go │ ├── arraylist.go │ ├── type.go │ ├── chunkbuffer.go │ ├── functional.go │ ├── hashes_test.go │ ├── writer.go │ ├── delay.go │ ├── slicechunk.go │ ├── amapentry.go │ ├── apersistentset.go │ ├── map.go │ ├── ref.go │ ├── equals_test.go │ ├── lazilypersistentvector.go │ ├── exception_info.go │ ├── catch.go │ ├── iteration.go │ ├── cons.go │ ├── seq.go │ ├── char.go │ ├── struct.go │ ├── aseq.go │ ├── chunkedcons.go │ ├── atom.go │ ├── slices.go │ ├── agent.go │ ├── stringseq.go │ ├── persistenthashmap_test.go │ ├── apersistentmap.go │ ├── sort.go │ ├── sliceseq.go │ ├── equal.go │ └── repeat.go ├── stdlib │ ├── stdlib.go │ └── clojure │ │ ├── uuid.glj │ │ └── template.glj ├── repl │ └── options.go └── pkgmap │ └── pkgmap.go ├── doc ├── logo.png ├── favicon.png └── logo_small.webp ├── scripts ├── rewrite-core │ ├── deps.edn │ ├── .gitignore │ ├── run.sh │ ├── originals │ │ ├── uuid.clj │ │ └── template.clj │ └── update-clojure-sources.sh ├── build-glj.sh ├── gen-gljimports.sh └── replace_kw.py ├── .gitignore ├── .gitmodules ├── test └── glojure │ └── test_glojure │ ├── import.glj │ ├── lcm.glj │ ├── parallel.glj │ ├── builtins.glj │ ├── core │ └── async │ │ └── basic.glj │ └── cli.glj ├── cmd └── glj │ ├── main_wasm.go │ └── main.go ├── .github └── workflows │ ├── ci.yml │ └── static.yml ├── shell.nix ├── nix └── sources.json └── go.mod /.envrc: -------------------------------------------------------------------------------- 1 | use_nix 2 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @jfhamlin 2 | -------------------------------------------------------------------------------- /internal/deps/pull.go: -------------------------------------------------------------------------------- 1 | package deps 2 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/stdlib02.glj: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/stdlib02.out: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/testtab.out: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/functional00.out: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/prog00.out: -------------------------------------------------------------------------------- 1 | 6 2 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/seq00.out: -------------------------------------------------------------------------------- 1 | 10 2 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/sort00.out: -------------------------------------------------------------------------------- 1 | OK 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/bigint00.glj: -------------------------------------------------------------------------------- 1 | 0N 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/bigint00.out: -------------------------------------------------------------------------------- 1 | 0N 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/set00.glj: -------------------------------------------------------------------------------- 1 | #{:a} 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/set00.out: -------------------------------------------------------------------------------- 1 | #{:a} 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/shebang.out: -------------------------------------------------------------------------------- 1 | 42 2 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/def01.out: -------------------------------------------------------------------------------- 1 | #'user/x 2 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/interop04.out: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/let00.out: -------------------------------------------------------------------------------- 1 | 5 2 | 14 3 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/macros00.out: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/bigfloat00.glj: -------------------------------------------------------------------------------- 1 | 0M 2 | 2.0M -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/hex00.glj: -------------------------------------------------------------------------------- 1 | 0x123 2 | 010 -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/hex00.out: -------------------------------------------------------------------------------- 1 | 291 2 | 8 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/strings0.glj: -------------------------------------------------------------------------------- 1 | "\\\\" 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/strings0.out: -------------------------------------------------------------------------------- 1 | "\\\\" 2 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/def00.out: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 3 4 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/interop02.out: -------------------------------------------------------------------------------- 1 | [a b c] 2 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/reduceslice00.out: -------------------------------------------------------------------------------- 1 | abc 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/bigfloat00.out: -------------------------------------------------------------------------------- 1 | 0M 2 | 2M 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/nil00.glj: -------------------------------------------------------------------------------- 1 | nil 2 | [nil] 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/nil00.out: -------------------------------------------------------------------------------- 1 | nil 2 | [nil] 3 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/char00.out: -------------------------------------------------------------------------------- 1 | a b c 2 | Ω 3 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/fnarity00.out: -------------------------------------------------------------------------------- 1 | 0 2 | 1 3 | 2 4 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/def01.glj: -------------------------------------------------------------------------------- 1 | (println (def x 42)) 2 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/interop03.out: -------------------------------------------------------------------------------- 1 | http.HandlerFunc 2 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/print.out: -------------------------------------------------------------------------------- 1 | foo 2 | bar 3 | nil 4 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/core004.glj: -------------------------------------------------------------------------------- 1 | #(str %4 % 1 %2 2 %&) 2 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/functions00.out: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 3 4 | 4 5 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/require00.out: -------------------------------------------------------------------------------- 1 | 100 200 300 2 | true 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/ignoreform00.out: -------------------------------------------------------------------------------- 1 | 12 2 | 42 3 | {:foo :bar} 4 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/multiline.out: -------------------------------------------------------------------------------- 1 | "Foo bar\nbaz doesn't\"" 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/numbers00.out: -------------------------------------------------------------------------------- 1 | 2 2 | 100000000000000000000N 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/symbolic00.out: -------------------------------------------------------------------------------- 1 | ##Inf 2 | ##-Inf 3 | ##NaN 4 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/print.glj: -------------------------------------------------------------------------------- 1 | (println (println "foo\nbar")) 2 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/quasiquote00.glj: -------------------------------------------------------------------------------- 1 | (println `[x# ~@[`x#]]) 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/map00.out: -------------------------------------------------------------------------------- 1 | {:foo 42, :bar "hello", 1 [1 2 3]} 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/numbers00.glj: -------------------------------------------------------------------------------- 1 | 2r10 2 | 100000000000000000000 3 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/interop04.glj: -------------------------------------------------------------------------------- 1 | (prn (. (new bytes.Reader) Size)) 2 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/math00.out: -------------------------------------------------------------------------------- 1 | 0 2 | 1 3 | 3 4 | 1 5 | 3 6 | 3.5 7 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/prog02.out: -------------------------------------------------------------------------------- 1 | 1 1 2 | true 1 3 | false 0 4 | 0 1 5 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/quasiquote00.out: -------------------------------------------------------------------------------- 1 | [x__0__auto__ x__1__auto__] 2 | -------------------------------------------------------------------------------- /doc/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glojurelang/glojure/HEAD/doc/logo.png -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/comment00.out: -------------------------------------------------------------------------------- 1 | (print (quote (mixer dry reverb))) 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/float00.glj: -------------------------------------------------------------------------------- 1 | 1E10 2 | 12E3 3 | 12E-3 4 | 1.35e-12 5 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/shebang.glj: -------------------------------------------------------------------------------- 1 | #! this is treated as a comment 2 | 42 3 | -------------------------------------------------------------------------------- /doc/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glojurelang/glojure/HEAD/doc/favicon.png -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzRead/48c07ddc40007f0b: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0/0") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/core003.glj: -------------------------------------------------------------------------------- 1 | (get options :hierarchy #'global-hierarchy) 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/map01.out: -------------------------------------------------------------------------------- 1 | {:prefix/foo 42, :nope/bar 43, 1 {:nested 2}} 2 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/case00.out: -------------------------------------------------------------------------------- 1 | baz 2 | nil 3 | default 4 | it was one of these 5 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/char00.glj: -------------------------------------------------------------------------------- 1 | (println \tab \a \space \b\c \newline \u03A9) 2 | -------------------------------------------------------------------------------- /doc/logo_small.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glojurelang/glojure/HEAD/doc/logo_small.webp -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzRead/212816a63358983b: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("#{0 0}") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzRead/f21bc1b3b9bfb37d: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("`A:/0") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/core000.out: -------------------------------------------------------------------------------- 1 | (def list (. clojure.lang.PersistentList creator)) 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/core003.out: -------------------------------------------------------------------------------- 1 | (get options :hierarchy (var global-hierarchy)) 2 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/ns_skip.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.ns-skip) 2 | 3 | :skip 4 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzRead/b27cc02acdd412b9: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0#?@(:glj()0)") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/char.out: -------------------------------------------------------------------------------- 1 | (println \a \b \c \Ω) 2 | (println \newline \space \tab) 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/multiline.glj: -------------------------------------------------------------------------------- 1 | ;;;SKIP_PRINT_TEST 2 | "Foo bar 3 | baz doesn't\"" 4 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/symbolic00.glj: -------------------------------------------------------------------------------- 1 | ;;;SKIP_PRINT_TEST 2 | ##Inf 3 | ##-Inf 4 | ##NaN 5 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/quote00.out: -------------------------------------------------------------------------------- 1 | () 2 | (1 2 3) 3 | (sym) 4 | ("foo" (quote (1 2 3)) sym) 5 | -------------------------------------------------------------------------------- /scripts/rewrite-core/deps.edn: -------------------------------------------------------------------------------- 1 | {:deps 2 | {rewrite-clj/rewrite-clj {:mvn/version "1.1.45"}}} 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/core002.glj: -------------------------------------------------------------------------------- 1 | #"(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9_]+))?(?:-(SNAPSHOT))?" 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/float00.out: -------------------------------------------------------------------------------- 1 | 10000000000.0 2 | 12000.0 3 | 0.012 4 | 0.00000000000135 5 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/map00.glj: -------------------------------------------------------------------------------- 1 | ;;;SKIP_PRINT_TEST 2 | { :foo 42, :bar "hello" 1 [1 2 3] } 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/readcond.out: -------------------------------------------------------------------------------- 1 | (Glojure expression) 2 | (fallthrough expression) 3 | 42 4 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/map01.glj: -------------------------------------------------------------------------------- 1 | ;;;SKIP_PRINT_TEST 2 | #:prefix{:foo 42 :nope/bar 43 1 {:nested 2}} 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/readcondunusual.out: -------------------------------------------------------------------------------- 1 | [(quote symbol)] 2 | [splice with a nested splice] 3 | [] 4 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader_error/unexpected00.glj: -------------------------------------------------------------------------------- 1 | ;;;ERROR: :2:1: unexpected ')' 2 | ) 3 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/interop02.glj: -------------------------------------------------------------------------------- 1 | (println (strings.FieldsFunc "a,b,c" (fn [c] (= (char c) \,)))) 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/1816bb27c4c92717c970297a3c23f8f4ef85e4b53e79f8d06db691098a507592.glj: -------------------------------------------------------------------------------- 1 | true 2 | 0 -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/3ac179851354cf9a588d482e1fdf439b640f0017d9e48efa6944fab4c787f4ad.glj: -------------------------------------------------------------------------------- 1 | true 2 | 2 -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/522bf19c1792fca7b20f2e60c63a67e0f510483a3f99da0c6ea38e9ef046bd94.glj: -------------------------------------------------------------------------------- 1 | true 2 | 2 -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9.glj: -------------------------------------------------------------------------------- 1 | true 2 | 0 -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/7a3e6b16cb75f48fb897eff3ae732f3154f6d203b53f33660f01b4c3b6bc2df9.glj: -------------------------------------------------------------------------------- 1 | true 2 | 1 -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/8a5edab282632443219e051e4ade2d1d5bbc671c781051bf1437897cbdfea0f1.glj: -------------------------------------------------------------------------------- 1 | true 2 | / -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/d9a847a1a79ab44825bbf8e5720bca771d53d1344d871bf9be1567382a022718.glj: -------------------------------------------------------------------------------- 1 | true 2 | 0 -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/f1534392279bddbf9d43dde8701cb5be14b82f76ec6607bf8d6ad557f60f304e.glj: -------------------------------------------------------------------------------- 1 | true 2 | 0 -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/f755b7c0125cdd02920bc8cdc6795fd25eeeaa08b072367695fc2055c6920c1e.glj: -------------------------------------------------------------------------------- 1 | true 2 | 1 -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-equiv-cache/7e6073968842178d176dfbc5042b5193e9c4c5bafa339d8908fe600a941c63fa: -------------------------------------------------------------------------------- 1 | true 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-equiv-cache/efe5931ea5c44cd13420973e881027e0261414ca43a4872137e3508ebff9ca58: -------------------------------------------------------------------------------- 1 | true 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzRead/7dac166db0afb1bc: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("##```````````````````````s#") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader_error/string00.glj: -------------------------------------------------------------------------------- 1 | ;;;ERROR: :2:4: error reading string: EOF 2 | " 3 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/reduceslice00.glj: -------------------------------------------------------------------------------- 1 | (println (reduce str "" ((go-sliceof string) ["a" "b" "c"]))) 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/17576d04a48f54985c12ebc1d2b2cfafcb6d27445b37136e1fd9c012527b9a9e.glj: -------------------------------------------------------------------------------- 1 | true 2 | 0N -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/18cd36b2b7bdd213791af85934f3f09c0388f67340cd035a1f3632ed6c4c244b.glj: -------------------------------------------------------------------------------- 1 | true 2 | 10 -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/19e89348f2a9d5f3d0c5fca8e2a7068d9c5d71687a355f759009a5ed2527eb2c.glj: -------------------------------------------------------------------------------- 1 | true 2 | :0 -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/537838579ebd2316a949dba9a7c6c95ab79d198348130479b2bdd5fdb77f97b0.glj: -------------------------------------------------------------------------------- 1 | true 2 | 0M -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/7cf33476e7cff8b85c6eff7cd370f29253b2121045ac88f190714c42e61648b1.glj: -------------------------------------------------------------------------------- 1 | true 2 | 291 -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/86f80fa2ae598a66c489b827703b248e40bb3bba20ca04ce2d1b134999ae087f.glj: -------------------------------------------------------------------------------- 1 | true 2 | \0 -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/892f6e09c02c35b5381a165395599f21770c19b4f05d2ed71ccd2a45af414b1c.glj: -------------------------------------------------------------------------------- 1 | true 2 | 0.0 -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/9466e130e88346e09e754f11f5539093f6df5223fe9922c21323ebab98fd518b.glj: -------------------------------------------------------------------------------- 1 | true 2 | 1.0E10 -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/98345a97b187dd1b1d9c14c9fb65a5a03123b39b5d0225ffff27f7968e01baba.glj: -------------------------------------------------------------------------------- 1 | true 2 | nil -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/a6ad39efc4146b973475110caace1ca3ddc5757e1990274af759acce54af7a74.glj: -------------------------------------------------------------------------------- 1 | true 2 | A# -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/a7315e659d891864a2c43206529d9cd6ff5905e95edaf5b03f11373cd93d81ae.glj: -------------------------------------------------------------------------------- 1 | true 2 | 0N -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/ae2b22faeae432b15083761c19a42d3b12c1d967e225a44854db345fe545ef43.glj: -------------------------------------------------------------------------------- 1 | true 2 | ##Inf -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/bfd59979737de1a540673045c3adb336332c2c205d2c5fbaff6b1c7a9f3fc9ef.glj: -------------------------------------------------------------------------------- 1 | true 2 | 42 -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/c00e2650a826a4886c2bab00b70f6f3a00f8e1590c8cadc560ef493c3562028c.glj: -------------------------------------------------------------------------------- 1 | true 2 | 0M -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/ce5829e1a023d49c88eca9ff6812ded618d4c3ed0d2a572b545609b9c4983153.glj: -------------------------------------------------------------------------------- 1 | true 2 | .0 -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/cf9ec8eb3fae228a832bb4e79983f4276cc29937092963844c402eb1daf51dd5.glj: -------------------------------------------------------------------------------- 1 | true 2 | "\\\\" -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/f0092274c1b6d874b85112c3f3105eebfe094e8ac93ae4205983bcc9c2a98a77.glj: -------------------------------------------------------------------------------- 1 | true 2 | 0M -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/basic00.glj: -------------------------------------------------------------------------------- 1 | 1 2 | "foobar" 3 | true 4 | false 5 | :key 6 | (def x '(my quoted list)) 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/readcondsplicing.out: -------------------------------------------------------------------------------- 1 | [1 2 3 4] 2 | [2 4 6 8] 3 | [1 2 3 4] 4 | {:foo 42, :bar :baz} 5 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/functions01.out: -------------------------------------------------------------------------------- 1 | 1 2 | 1 3 | 2 4 | 3 5 | 5 6 | 8 7 | 13 8 | 21 9 | 34 10 | 55 11 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/interop01.out: -------------------------------------------------------------------------------- 1 | true 2 | [foo----bar ----] 3 | [false nil] 4 | [true nil] 5 | [true nil] 6 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval_error/let00.in: -------------------------------------------------------------------------------- 1 | ;;;ERROR=let00.in:2:9: undefined symbol: x 2 | (let [y x x 2] (+ x y)) 3 | -------------------------------------------------------------------------------- /pkg/glj/doc.go: -------------------------------------------------------------------------------- 1 | // Package glj provides a minimal interface to bootstrap Glojure 2 | // access from Go. 3 | package glj 4 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/7a7c9bc75f4202399d3acbe523ddf827d14e429c0242eb3323033115e9f70e8c.glj: -------------------------------------------------------------------------------- 1 | true 2 | #{:c :b :a} -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/f72460b27e8859789e9de498997d25154a6bf23348496dc6c19bc1510bce38b6.glj: -------------------------------------------------------------------------------- 1 | true 2 | #{:c :b :a} -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/basic00.out: -------------------------------------------------------------------------------- 1 | 1 2 | "foobar" 3 | true 4 | false 5 | :key 6 | (def x (quote (my quoted list))) 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/char.glj: -------------------------------------------------------------------------------- 1 | ;;;SKIP_PRINT_TEST 2 | (println \a \b \c \u03a9) 3 | (println \newline \space \tab) 4 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader_error/string01.glj: -------------------------------------------------------------------------------- 1 | ;;;ERROR: :2:13: error reading string: EOF 2 | "something\ -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/const_keyword.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.const-keyword) 2 | 3 | (def const-keyword :foo) 4 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval_error/let01.in: -------------------------------------------------------------------------------- 1 | ;;;ERROR=let01.in:2:6: invalid let: cannot bind to int64 2 | (let [4 2] true) 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /doc/repl/glj.wasm 3 | /report.html 4 | .direnv 5 | 6 | # useful to symlink in for context 7 | /clojure 8 | -------------------------------------------------------------------------------- /pkg/lang/namespace_test.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import "testing" 4 | 5 | func TestNamespace(t *testing.T) { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/comment00.glj: -------------------------------------------------------------------------------- 1 | ;;;SKIP_PRINT_TEST 2 | (print '(mixer dry reverb) 3 | ;; reverb 4 | ) 5 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/7e4515b7bece699b0593b3ae77cc33c07583566bac75e7376aa85236dfee421a.glj: -------------------------------------------------------------------------------- 1 | true 2 | (println \a \b \c \Ω) -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/e014bb4c05531ad4d5cecaf1c0e4164122fc50966cb3e2b6242f9e9317dc6d7c.glj: -------------------------------------------------------------------------------- 1 | true 2 | (ns clojure.core) -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/core004.out: -------------------------------------------------------------------------------- 1 | (fn* [p1__4# p2__5# p3__7# p4__3# & rest__6#] (str p4__3# p1__4# 1 p2__5# 2 rest__6#)) 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/1c92f033fb8eabff5381b8eb26e5106d3b15acb67b35df470434501863d5c4b0.glj: -------------------------------------------------------------------------------- 1 | true 2 | (clojure.core/deref 0) -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/86dfc0c18ea77232f4958ff59c0bc43fb678e4b8ed937fc39e0d2ac95ec07cb5.glj: -------------------------------------------------------------------------------- 1 | true 2 | (clojure.core/unquote 0) -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/ca0978f602106306f9205296c326e9a064767ea92d6f2989e33ed0c5a355d3da.glj: -------------------------------------------------------------------------------- 1 | true 2 | "Foo bar\nbaz doesn't\"" -------------------------------------------------------------------------------- /pkg/reader/testdata/reader_error/char00.glj: -------------------------------------------------------------------------------- 1 | ;;;ERROR: :2:7: invalid character literal: unexpected rune count 2 | \foobar 3 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/const_string.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.const-string) 2 | 3 | (def ^:hello const-string "Hello, World!") 4 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/9a6940e4e50dd0fa87479a53b76c8e421b7cac3edc24581e0d9de1a513188fae.glj: -------------------------------------------------------------------------------- 1 | true 2 | (load-file "./functional00.in") -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/stdlib01.out: -------------------------------------------------------------------------------- 1 | () 2 | (a b c) 3 | (a b c 1 2 3) 4 | (h e l l o) 5 | (1 2 3 4 5 6 102 111 111 98 97 114) 6 | (2 3) 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/580e62d910e87c00a12cca2e2092507f00d916dbccb06d2f2b1047f3fe20443c.glj: -------------------------------------------------------------------------------- 1 | true 2 | (print (quote (mixer dry reverb))) -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/d31676b6e22bb403dac0099b7069b4c1f54f3e1edab582328eec835ca4db5a65.glj: -------------------------------------------------------------------------------- 1 | true 2 | {:foo 42, :bar "hello", 1 [1 2 3]} -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/02d4bc9f4e567048ebe719db6d94d22ed0788a56e3bdf4aa1fa3a546b5a681a4: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("A/") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/1d43ee52085cb4aad7154239440cb299d4a48f78d96d63fe26ea00c7dcb9e698: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("/0") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/31bb42ef51bae0f76b5b9dd265fac08fb8b28665c63e940824755a75e73a205d: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("10#") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/3a24a4b99c37a98576562a3f7f431e56d72d8d0c681577a6fd6fdc01053de065: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("~0") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/3def8fda4a9dc5128964954a6474893faa18048a6c1324e2c21a62eaad2ed446: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0!") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/483ca9154683fc8926f40bae096c030463ba51b0b91082f98802937933b7fa63: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0*") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/48ef306e49107c071b90d32fc4a2b9677fb2a177cb7dad51580eb15639475714: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string(": ") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/4d625360ee9191e5cffd04495127be328f03c1afc4164c857dcc9f1ae3abab3e: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0&") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/52aa6175d6125a3f5063f3b1d236b39843653b08f7220e26c7be988cd74ea077: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0A") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/55de2f97915e5dde3530fb6b0db159bc170199a237a8f275c884df1536224f71: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0\"") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/5943f9224b12cfd579d0653db31663f9843f66874a589ae0a23564c860c2d720: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0$") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/5c19709a556b49091409d495c88b21d4aee47cc9718542737e1ec15894b6883a: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("A:") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/68c711c91a705bfe3284f0b678d9cdcba431ae50d418b3511fcb78b71785fff0: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("08") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/771e938e4458e983a736261a702e27c7a414fd660a15b63034f290b146d2f217: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/8643a79bab153d1be09b197e5c55f603de7e1e5e621f40e0ed3b0a78c72577b3: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string(" ") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/901ac8d1fcb285b2648ccd3ebdc0870184c5450cf6b4e64b8679674ae49f4216: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0.") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/93076e9d6b6510b69fe6b4892585737d8f59f77af8c519662c3e42c70fd291cc: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string(".0") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/9cd6a05bc1ae5acbe271feaa951795361decc9602f8dd65a3f981906fb5387c0: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0M") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/9cd9b70960b4a733b309f718204b9e2fc9096e34d36e37538cf8eb3e975347ad: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("/") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/a27a375a64900aed02211ffab0e408801415706b848eae4c08d57d60ba0b458f: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0B0") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/a7284e0107a1b9c47f4660ff283add8437e956cef83f2a2d8156aa54def53f6a: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("@0") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/abff33899e7cd4f9c816358b8dea680db5b9bfd8032464c08c33ae5ca4f5baab: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("00") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/b158e8273e40dac4165aef51dfff14afc647c9a2a3117f91f5df0c5fc22bde3c: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0N") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/bc06c83720e3dbbfbc44a8a95242076759a1c24f7d3b5ebfef9e788fa2917076: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0X") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/bca2c1ea57d0749a2032539d6db0b89a879d9fada415cdcf76f5e859c1755cf7: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("{0}") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/bf5ee1bf3f1fb87edcbcd435f8bf98cb469d3fe8cf4ee9a300b9f9b369bee52e: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string(":0") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/c2f7992b1414c3d48a8fd9d2435590e89a7f19696e5ab036902f7906750c6190: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0Y") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/c908d15e31b0b6890e437b7d9b33e407fba3b18415fcceb6cfbc724cca7ab8a7: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("::") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/cb1240a631c35f1022d4246e951fbc9399c61669e0d974a31737486f1ac22eb7: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("001") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/d9b1ca1afa03e9a8808db7d85d3856a4b4c16342dd98e0fc9c074fee43bf6431: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("A::00") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/dc9ce74647d8ec8794d72d6d600e6e6913536f02e1709e7ea509a9e95e6c4bde: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("A#") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/e1bebd2820af67a54c100a7a0783a029cdc0b33825aeb823d250e0cb2e9d6bdd: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("\\0") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/ed853cd85118209a87c0dfa0851a94c355d94ed694ddfdb8a0c28b23d3915d61: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("0%") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/f2f1bb2b4123163cc61f3eb116a2b878980a3d9825af646dcbd9422b1f0a9903: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("@") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader_error/bad_reader_cond.glj: -------------------------------------------------------------------------------- 1 | ;;;ERROR: :2:11: reader conditional feature must be a keyword 2 | #?(none 42) 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "test/clojure-test-suite"] 2 | path = test/clojure-test-suite 3 | url = git@github.com:glojurelang/clojure-test-suite.git 4 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader_error/oddconditional.glj: -------------------------------------------------------------------------------- 1 | ;;;ERROR: :2:18: read-cond requires an even number of forms 2 | #?(:clj :foo :glj) 3 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/const_number.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.const-number) 2 | 3 | (def const-number 4 | "A constant number." 5 | 42) 6 | -------------------------------------------------------------------------------- /internal/persistent/vector/testdata/fuzz/FuzzTransient/e94aecaacbdb36f657e0f2350dd640e7b7f05ca739adbfa945fdbb61ace36369: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("[[]]") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/db2d18ebe5b8d84771f5dbc61b6767e4ceac6b79e25b23e4b009eae8fdbdcd26.glj: -------------------------------------------------------------------------------- 1 | true 2 | {:prefix/foo 42, :nope/bar 43, 1 {:nested 2}} -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/4c76574832cd26648a52e7a31fc9bae6aa08247b2b1dd2f3e0cb2fb021585b92: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("#{ a :b::c}\n") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/c5be14bdbf4455ba10edc2089caac62bb8d680ca3ce84c0bb5c5d65ec438094a: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("#{:a :b :c} ") 3 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/quote_simple.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.quote-simple) 2 | 3 | (defn ^{:expected-output (list 1 2 3)} -main [] 4 | '(1 2 3)) 5 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/c7ca714eca149d2f3c131e85e902718498a6804ca5aec211f93a059b946f40e0.glj: -------------------------------------------------------------------------------- 1 | true 2 | (get options :hierarchy (var global-hierarchy)) -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/ef0306541d2c08e833060aeddc13f80cfd1c009c497796bf7407048538be81df.glj: -------------------------------------------------------------------------------- 1 | true 2 | (def list (. clojure.lang.PersistentList creator)) -------------------------------------------------------------------------------- /pkg/reader/testdata/reader_error/dispatch00.glj: -------------------------------------------------------------------------------- 1 | ;;;ERROR: :6:4: invalid dispatch character: w 2 | ( 3 | 1 4 | (2) 5 | true 6 | (#what) 7 | ) 8 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/0d61a1dbd59935194751d448f2c2f626154cf7d8175f111d72f3995d873483d0: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("#(str %4 % @ %2 2 %&)\n") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader_error/bad_namespace_map00.glj: -------------------------------------------------------------------------------- 1 | ;;;ERROR: :2:9: namespaced map must specify a valid namespace: :foo/bar 2 | #:foo/bar{:baz 1} 3 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/the_var.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.the-var) 2 | 3 | (def foo 42) 4 | 5 | (defn ^{:expected-output 42} -main [] 6 | (.get #'foo)) 7 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/with_meta.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.with-meta) 2 | 3 | (defn ^{:expected-output :bar} -main [] 4 | (:foo (meta ^{:foo :bar}[]))) 5 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/7e53e99ddf3f4633fc756c3ab0c1ed3a626df352e25980f958d4cedac2a7aab3.glj: -------------------------------------------------------------------------------- 1 | true 2 | #"(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9_]+))?(?:-(SNAPSHOT))?" -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/functions01.glj: -------------------------------------------------------------------------------- 1 | (defn fib [n] 2 | (if (< n 2) 3 | 1 4 | (+ (fib (- n 1)) (fib (- n 2))))) 5 | 6 | (map println (map fib (range 10))) 7 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/prog00.glj: -------------------------------------------------------------------------------- 1 | (defn mulTwo [a b] (* a b)) 2 | 3 | (println (mulTwo 2 3)) 4 | 5 | (defn weirdosc [] 6 | (* (mrat.osc/sin 261.625565) 0.5)) 7 | -------------------------------------------------------------------------------- /internal/goid/goid.go: -------------------------------------------------------------------------------- 1 | //go:build !wasm 2 | 3 | package goid 4 | 5 | import "github.com/modern-go/gls" 6 | 7 | func Get() int64 { 8 | return gls.GoID() 9 | } 10 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/8488b046fc941dff7d38fef7d35530ca809704737e3c19966a419a64ce2baee2.glj: -------------------------------------------------------------------------------- 1 | true 2 | #"(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9_]+))?(?:-(SNAPSHOT)) 3 | " -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/throw_simple.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.throw-simple) 2 | 3 | (defn ^{:expected-throw "uncaught error"} -main [] 4 | (throw "uncaught error")) 5 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/maybe_class.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.maybe-class) 2 | 3 | (defn ^{:expected-output ["1" "2" "3"]} -main [] 4 | (vec (strings.Split "1,2,3" ","))) 5 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/vector00.out: -------------------------------------------------------------------------------- 1 | [] 2 | [1] 3 | [foo 8 :key] 4 | [42 8 :key] 5 | true 6 | true 7 | false 8 | [1 2 3 4 5] 9 | 1 10 | (2 3 4 5) 11 | 1 12 | 3 13 | (1 2 3 4) 14 | -------------------------------------------------------------------------------- /pkg/lang/persistenttreemap.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | func CreatePersistentTreeMap(keyvals interface{}) interface{} { 4 | // TODO: implement 5 | return NewMap(seqToSlice(Seq(keyvals))...) 6 | } 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/edcfcd1d0ea9bc946ceacc01583775040b571d4022f659a96bd95495f5f06f75.glj: -------------------------------------------------------------------------------- 1 | true 2 | (fn* [p1__4# p2__5# p3__7# p4__3# & rest__6#] (str p4__3# p1__4# 1 p2__5# 2 rest__6#)) -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/testtab.glj: -------------------------------------------------------------------------------- 1 | (defn testTable [tbl func] 2 | (if (eq? tbl '()) 3 | true ;; done 4 | (list 5 | (func (first tbl)) 6 | (testTable (rest tbl) func)))) 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/core001.out: -------------------------------------------------------------------------------- 1 | (defn not= "Same as (not (= obj1 obj2))" {:tag Boolean, :added "1.0", :static true} ([x] false) ([x y] (not (= x y))) ([x y & more] (not (apply = x y more)))) 2 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/quasiquote00.glj: -------------------------------------------------------------------------------- 1 | ;;;SKIP_PRINT_TEST 2 | `foobar 3 | `() 4 | `(a b c) 5 | `(a ~(+ 1 2)) 6 | `(a ~@[(+ 1 2)]) 7 | `(do.not.Qualify) 8 | `{:x ~[]} 9 | `(aliased/foo) 10 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/def00.glj: -------------------------------------------------------------------------------- 1 | (def foo 1) 2 | (println foo) 3 | (def foo 2) 4 | (println foo) 5 | 6 | (defn reset-foo [val] (def foo val)) 7 | 8 | (reset-foo 3) 9 | (println foo) 10 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/math00.glj: -------------------------------------------------------------------------------- 1 | (print (+)) ;; 0 2 | (print (+ 1)) ;; 1 3 | (print (+ 1 2)) ;; 3 4 | (print (+ (int 1))) ;; 1 5 | (print (+ (int 1) 2)) ;; 3 6 | (print (+ (int 1) 2.5)) ;; 3.5 7 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval_error/stack00.in: -------------------------------------------------------------------------------- 1 | ;;;ERROR_RE=panic: cannot convert string to Ops..* 2 | 3 | (def add 4 | (fn [a b] (+ a b))) 5 | (defn bad-use [x] 6 | (add x "hello")) 7 | (bad-use 1) 8 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/4a021516918d4410b80c80af5d5f442b6cc356358b7bd6b748f96c92234d551c: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string(";;;SKIP_PRINT_TEST\n{ :foo 42, :bar \"hel\x00\x10\" 1 [1 2 3] }\n") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/core000.glj: -------------------------------------------------------------------------------- 1 | (def 2 | ^{:arglists '([& items]) 3 | :doc "Creates a new list containing the items." 4 | :added "1.0"} 5 | list (. clojure.lang.PersistentList creator)) 6 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/ignoreform00.glj: -------------------------------------------------------------------------------- 1 | 12 2 | #_(anything goes) 3 | 42 4 | {:foo #_(or within a map #_(or within another)) :bar #_(or at the end of a collection)} 5 | #_(even at the end of the file) 6 | -------------------------------------------------------------------------------- /pkg/lang/lib.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | func Pop(stk IPersistentStack) IPersistentStack { 4 | return stk.Pop() 5 | } 6 | 7 | func Peek(stk IPersistentStack) interface{} { 8 | return stk.Peek() 9 | } 10 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/0d40b8d4398e177684afa1c78fd95d9b348d7125c248de27028a7a4c766340d0: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("#\"(\\d+)\\.(\\d+)\\.(\\d+)(?:-([a-zA-Z0-9_]{{{{{+)(SNAPSHOT))?\"\n") 3 | -------------------------------------------------------------------------------- /pkg/reader/testdata/fuzz/FuzzCLJConformance/fa8fa90d3f58681a069ffff014c397d6159904cd6e4a0c44888341ef72724323: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("#\"(\\d+)\\.(\\d+)\\.(\\d+)(?:-([a-zA-Z0-9_]+))?(?:-(SNAPSHOT))\n\"?") 3 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/fnarity00.glj: -------------------------------------------------------------------------------- 1 | (def arity-check 2 | (fn arity-check 3 | ([] 0) 4 | ([a] 1) 5 | ([a b] 2))) 6 | 7 | (map print (map (fn [args] (apply arity-check args)) [[] [1] [1 2]])) 8 | -------------------------------------------------------------------------------- /pkg/lang/afn.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | type ( 4 | AFn interface { 5 | IFn 6 | } 7 | ) 8 | 9 | func afnApplyTo(a AFn, args ISeq) any { 10 | slc := seqToSlice(args) 11 | return a.Invoke(slc...) 12 | } 13 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/64c6501ad7d25f9467390fdbe37ff9025a72f2976f07850c22e071704e00297f.glj: -------------------------------------------------------------------------------- 1 | true 2 | (fn* [p1__4# p2__5# p3__7# p4__3# & rest__6#] (str p4__3# p1__4# (clojure.core/deref p2__5#) 2 rest__6#)) -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/functional00.glj: -------------------------------------------------------------------------------- 1 | (defn map [func lst] 2 | (if (not-empty? lst) 3 | (concat (list (func (first lst))) (map func (rest lst))) 4 | (list))) 5 | 6 | (defn sum [lst] (apply + lst)) 7 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval_error/invalid_set.in: -------------------------------------------------------------------------------- 1 | ;;;ERROR=invalid_set.in:3:1: invalid set! expression, cannot assign int64 to http.Handler 2 | (def server (new net/http.Server :Addr ":8080")) 3 | (set! (. server Handler) 42) 4 | -------------------------------------------------------------------------------- /pkg/stdlib/stdlib.go: -------------------------------------------------------------------------------- 1 | // Package stdlib provides the standard library for the mratlang language. 2 | package stdlib 3 | 4 | import ( 5 | "embed" 6 | ) 7 | 8 | //go:embed glojure clojure 9 | var StdLib embed.FS 10 | -------------------------------------------------------------------------------- /scripts/rewrite-core/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | profiles.clj 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | /.lein-* 10 | /.nrepl-port 11 | /.prepl-port 12 | .hgignore 13 | .hg/ 14 | .cpcache -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/ref.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.ref) 2 | 3 | (def test-ref 4 | "A reference to a set." 5 | (ref #{1 2 3})) 6 | 7 | (defn 8 | ^{:expected-output #{1 2 3}} 9 | -main [] 10 | @test-ref) 11 | -------------------------------------------------------------------------------- /pkg/lang/truthiness_test.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestIsTruthy(t *testing.T) { 8 | var v *Var 9 | if IsTruthy(v) { 10 | t.Errorf("nil should not be truthy: %v", v) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/quote00.glj: -------------------------------------------------------------------------------- 1 | (load-file "./testtab.glj") 2 | 3 | (def table 4 | (list 5 | '() 6 | '(1 2 3) 7 | '(sym) 8 | '("foo" '(1 2 3) sym) 9 | ) 10 | ) 11 | 12 | (testTable table println) 13 | -------------------------------------------------------------------------------- /pkg/lang/string.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import "strings" 4 | 5 | func ConcatStrings(strs ...string) string { 6 | b := strings.Builder{} 7 | for _, str := range strs { 8 | b.WriteString(str) 9 | } 10 | return b.String() 11 | } 12 | -------------------------------------------------------------------------------- /internal/seq/seq.go: -------------------------------------------------------------------------------- 1 | // Package seq provides a definition of an internal Seq interface for 2 | // use across packages. It is used to avoid circular dependencies. 3 | package seq 4 | 5 | type Seq interface { 6 | First() any 7 | Next() Seq 8 | } 9 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/readcondunusual.glj: -------------------------------------------------------------------------------- 1 | ;; skip-clj 2 | [^#?@(:glj [:foo 'symbol])] 3 | 4 | [#?@(:glj [splice with #?@(:glj [a nested splice])])] 5 | 6 | [#?(:clj [no match at end of collection])] 7 | 8 | #?(:clj [no match at end of input]) 9 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/values.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.values) 2 | 3 | (def big-decimal 10M) 4 | (def big-decimal-type (type big-decimal)) 5 | 6 | (defn ^{:expected-output [10M (type big-decimal)]} -main [] 7 | [big-decimal big-decimal-type]) 8 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/basic01.out: -------------------------------------------------------------------------------- 1 | (load-file "./functional00.in") 2 | (def print mrat.core.io.print) 3 | (apply print (map (fn (x) (* x 100)) (list 1 2 3))) 4 | (def numList (list 1 2 3 4 5 6 7 8 9 10)) 5 | (print (eq? (reduce + 0 numList) (sum numList))) 6 | -------------------------------------------------------------------------------- /pkg/lang/lazyseq_test.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestLazySeq(t *testing.T) { 8 | ls := NewLazySeq(func() interface{} { return nil }) 9 | // should be empty 10 | if Seq(ls) != nil { 11 | t.Fail() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/1c864710144023cf5ab9669c361dbe90d2b18d18c76b86183a572eb8e9d0264e.glj: -------------------------------------------------------------------------------- 1 | true 2 | (defn not= "Same as (not (= obj1 obj2))" {:tag Boolean, :added "1.0", :static true} ([x] false) ([x y] (not (= x y))) ([x y & more] (not (apply = x y more)))) -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/loop_simple.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.loop-simple) 2 | 3 | (defn simple-loop [] 4 | (loop [i 0] 5 | (if (< i 10) 6 | (recur (inc i)) 7 | i))) 8 | 9 | (defn ^{:expected-output 10} 10 | -main [] (simple-loop)) 11 | -------------------------------------------------------------------------------- /test/glojure/test_glojure/import.glj: -------------------------------------------------------------------------------- 1 | (ns glojure.test-glojure.import 2 | (:use clojure.test) 3 | (:import (strings TrimPrefix) 4 | (io Reader))) 5 | 6 | (deftest Import 7 | (is (= "bar" (TrimPrefix "foobar" "foo")))) 8 | 9 | (run-tests) 10 | 11 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/36a9e7f1c95b82ffb99743e0c5c4ce95d83c9a430aac59f84ef3cbfab6145068.glj: -------------------------------------------------------------------------------- 1 | false 2 | Execution error at user/eval1 (REPL:1). 3 | EOF while reading 4 | 5 | Full report at: 6 | /var/folders/0m/y60b85m145lffbfdfk86129r0000gn/T/clojure-207498583948934392.edn 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/45822f54a0d0e030f75698c9252f1345982e743c9466806048f542467b3ca4f9.glj: -------------------------------------------------------------------------------- 1 | false 2 | Execution error at user/eval1 (REPL:1). 3 | Invalid token: : 4 | 5 | Full report at: 6 | /var/folders/mm/bhf4r1f11zq3fc1gckc5wt6h0000gn/T/clojure-6834870222029270924.edn 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/6a699d31f54deb4cb7de7c507420ce32f6c71298d58a1178bdb7ad3051910497.glj: -------------------------------------------------------------------------------- 1 | false 2 | Execution error at user/eval1 (REPL:1). 3 | Invalid token: A: 4 | 5 | Full report at: 6 | /var/folders/mm/bhf4r1f11zq3fc1gckc5wt6h0000gn/T/clojure-688689602208585938.edn 7 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/interop00.out: -------------------------------------------------------------------------------- 1 | Hello, world! 2 | Hello, ["world"]! 3 | Hello, world! This should say "nil": nil 4 | A bunch of types: 42 (42 [1 2 3]) [42] foobar 5 | true 6 | false 7 | foo, bar, baz 8 | bar quux 9 | bar quux 10 | 0 11 | localhost:8080 12 | :8080 13 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/06c901ba18e7c2418ebcfc5e2ed24023e8e711547233e135641597d9738e3ee1.glj: -------------------------------------------------------------------------------- 1 | false 2 | Execution error at user/eval1 (REPL:1). 3 | Invalid token: /0 4 | 5 | Full report at: 6 | /var/folders/0m/y60b85m145lffbfdfk86129r0000gn/T/clojure-15326428723233583541.edn 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/5442aa159e97a9592740654d29dca1201b7cfb3c1f1ffe1cc6ee9f9a95170ca5.glj: -------------------------------------------------------------------------------- 1 | false 2 | Execution error at user/eval1 (REPL:1). 3 | Invalid token: A::00 4 | 5 | Full report at: 6 | /var/folders/mm/bhf4r1f11zq3fc1gckc5wt6h0000gn/T/clojure-1022201777820521476.edn 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/71546855d6279ef70d20909b292c42c2dcb02cd06bde01485da52d13e304ebf4.glj: -------------------------------------------------------------------------------- 1 | false 2 | Execution error at user/eval1 (REPL:1). 3 | Invalid token: :: 4 | 5 | Full report at: 6 | /var/folders/0m/y60b85m145lffbfdfk86129r0000gn/T/clojure-10759612282868890124.edn 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/90c2120b8a77bd7ed9e949639bbbeb9be53852d0e4671a1fdcb55b3c99d19ae5.glj: -------------------------------------------------------------------------------- 1 | false 2 | Execution error at user/eval1 (REPL:1). 3 | Invalid token: :b::c 4 | 5 | Full report at: 6 | /var/folders/0m/y60b85m145lffbfdfk86129r0000gn/T/clojure-5969457839634569061.edn 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/c3641f8544d7c02f3580b07c0f9887f0c6a27ff5ab1d4a3e29caf197cfc299ae.glj: -------------------------------------------------------------------------------- 1 | false 2 | Execution error at user/eval1 (REPL:1). 3 | EOF while reading 4 | 5 | Full report at: 6 | /var/folders/0m/y60b85m145lffbfdfk86129r0000gn/T/clojure-14410054664658272439.edn 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/fdf6e585b66f6d5c79b90b044c834be4120ffd68e7b97a695ea6e0465c5c65ab.glj: -------------------------------------------------------------------------------- 1 | false 2 | Execution error at user/eval1 (REPL:1). 3 | Invalid token: A/ 4 | 5 | Full report at: 6 | /var/folders/0m/y60b85m145lffbfdfk86129r0000gn/T/clojure-17301162166733883095.edn 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/readcond.glj: -------------------------------------------------------------------------------- 1 | ;; skip-clj 2 | 3 | #?(:clj (Clojure expression) 4 | :glj (Glojure expression) 5 | :default (fallthrough expression)) 6 | #?(:clj (Clojure expression) 7 | :default (fallthrough expression)) 8 | #?(:clj (Clojure expression)) 9 | 42 10 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/interop03.glj: -------------------------------------------------------------------------------- 1 | (let [handler (fn [w r] (. w (WriteHeader 200))) 2 | http-server (new net/http.Server 3 | :Addr "localhost:8080" 4 | :Handler (net/http.HandlerFunc handler))] 5 | (println (. http-server Handler))) 6 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/stdlib01.glj: -------------------------------------------------------------------------------- 1 | (println (concat)) 2 | (println (concat '(a b c))) 3 | (println (concat '(a b c) [1 2 3])) 4 | (println (concat "hello")) 5 | (println (concat '(1 2 3) [4 5 6] ((go-sliceof byte) "foobar"))) 6 | (println (map (fn [a] (+ a 1)) ((go-sliceof int64) [1 2]))) 7 | -------------------------------------------------------------------------------- /cmd/glj/main_wasm.go: -------------------------------------------------------------------------------- 1 | //go:build wasm 2 | 3 | package main 4 | 5 | import ( 6 | "os" 7 | 8 | // Bootstrap the runtime 9 | _ "github.com/glojurelang/glojure/pkg/glj" 10 | "github.com/glojurelang/glojure/pkg/gljmain" 11 | ) 12 | 13 | func main() { 14 | gljmain.Main(os.Args[1:]) 15 | } 16 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/323783be9a53a31e158ec9600626a4703e99f4e183bc1acb8772cbdf5c3a1ece.glj: -------------------------------------------------------------------------------- 1 | false 2 | Execution error (NumberFormatException) at user/eval1 (REPL:1). 3 | Invalid number: 08 4 | 5 | Full report at: 6 | /var/folders/0m/y60b85m145lffbfdfk86129r0000gn/T/clojure-417524314772446402.edn 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/03a51bd6760f743e8fe6ea0488a26c58043fc41dd54b7dd606e585084b280a2b.glj: -------------------------------------------------------------------------------- 1 | false 2 | Execution error (NumberFormatException) at user/eval1 (REPL:1). 3 | Invalid number: 0A 4 | 5 | Full report at: 6 | /var/folders/0m/y60b85m145lffbfdfk86129r0000gn/T/clojure-17535360402534382248.edn 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/07bef2206a89b8ddd31002970520c9aae135576c12aaa5fe72c96de3b217157d.glj: -------------------------------------------------------------------------------- 1 | false 2 | Execution error (NumberFormatException) at user/eval1 (REPL:1). 3 | Invalid number: 0Y 4 | 5 | Full report at: 6 | /var/folders/0m/y60b85m145lffbfdfk86129r0000gn/T/clojure-14833423478432257569.edn 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/1d390f0b8303d9a29c38041266c7db7a60677d273f083132c39a5041c4fc0f1f.glj: -------------------------------------------------------------------------------- 1 | false 2 | Execution error (NumberFormatException) at user/eval1 (REPL:1). 3 | Invalid number: 0X 4 | 5 | Full report at: 6 | /var/folders/0m/y60b85m145lffbfdfk86129r0000gn/T/clojure-15801889601755281512.edn 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/32b6854d07aefbdacaf7dc8c7b391d3b6f3d1b487873d38508a8841daefc3010.glj: -------------------------------------------------------------------------------- 1 | false 2 | Execution error (NumberFormatException) at user/eval1 (REPL:1). 3 | Invalid number: 0B0 4 | 5 | Full report at: 6 | /var/folders/mm/bhf4r1f11zq3fc1gckc5wt6h0000gn/T/clojure-7143773060875952008.edn 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/b595ba2f09697488cf65428142351adb60a5b947b514f82364637a047bef87b2.glj: -------------------------------------------------------------------------------- 1 | false 2 | Execution error (NumberFormatException) at user/eval1 (REPL:1). 3 | Invalid number: 0$ 4 | 5 | Full report at: 6 | /var/folders/0m/y60b85m145lffbfdfk86129r0000gn/T/clojure-12154683543392409338.edn 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/e48504b10d842f7ae2e4c9ab110dbdcca844fac4523f3614781674f43a5fe7ce.glj: -------------------------------------------------------------------------------- 1 | false 2 | Execution error (NumberFormatException) at user/eval1 (REPL:1). 3 | Invalid number: 0& 4 | 5 | Full report at: 6 | /var/folders/0m/y60b85m145lffbfdfk86129r0000gn/T/clojure-5788371493046977629.edn 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/f18e9301eae406bbc26e46022df9bdf29f1b6393f8a2f47f03961797a6897d4e.glj: -------------------------------------------------------------------------------- 1 | false 2 | Execution error (NumberFormatException) at user/eval1 (REPL:1). 3 | Invalid number: 0* 4 | 5 | Full report at: 6 | /var/folders/0m/y60b85m145lffbfdfk86129r0000gn/T/clojure-16385527581731569401.edn 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/f2a0fd79585924076c10bc6a4adf4dbe0d06951e6d6950f07387ba999ae4a5c1.glj: -------------------------------------------------------------------------------- 1 | false 2 | Execution error (NumberFormatException) at user/eval1 (REPL:1). 3 | Invalid number: 0! 4 | 5 | Full report at: 6 | /var/folders/0m/y60b85m145lffbfdfk86129r0000gn/T/clojure-15986292232001759957.edn 7 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/readcondsplicing.glj: -------------------------------------------------------------------------------- 1 | ;; skip-clj 2 | 3 | [1 2 #?@(:clj (Clojure expression) 4 | :glj (3) 5 | :default (fallthrough expression)) 6 | 4] 7 | 8 | [#?@(:glj (2 4 6 8))] 9 | 10 | [#?@(:glj ( 1 2 3 #?@(:clj 42) 4) )] 11 | 12 | {:foo #?@(:glj [42 :bar :baz])} 13 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/a00644508de97e85263c9d0cd67e63d3c3b5d733bcaddc65b110e501ad5a7b73.glj: -------------------------------------------------------------------------------- 1 | false 2 | Execution error at user/eval1 (REPL:1). 3 | Map literal must contain an even number of forms 4 | 5 | Full report at: 6 | /var/folders/mm/bhf4r1f11zq3fc1gckc5wt6h0000gn/T/clojure-5905763281008845846.edn 7 | -------------------------------------------------------------------------------- /pkg/glj/read.go: -------------------------------------------------------------------------------- 1 | package glj 2 | 3 | import "github.com/glojurelang/glojure/pkg/runtime" 4 | 5 | // Read reads one object from the string s. Reads data in the edn 6 | // format. Read panics if the string is not a valid edn object. 7 | func Read(s string) interface{} { 8 | return runtime.RTReadString(s) 9 | } 10 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/letfn.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.letfn) 2 | 3 | (defn 4 | ^{:expected-output 55} 5 | -main [] 6 | ;; recursive function using letfn 7 | (letfn [(fib [n] 8 | (if (<= n 1) 9 | n 10 | (+ (fib (- n 1)) (fib (- n 2)))))] 11 | (fib 10))) 12 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/let00.glj: -------------------------------------------------------------------------------- 1 | (println 2 | (let [x 2 3 | y 3] 4 | (+ x y))) 5 | 6 | ;; test nested with the same name 7 | (println 8 | (let [x 2 9 | y 3] 10 | (+ x 11 | y 12 | (let [x 4 13 | z (- x 2)] 14 | (+ x y z))))) 15 | ;; 2 + 3 + 4 + 3 + 2 = 14 16 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/def_simple.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.def-simple) 2 | 3 | (defn deftest [val] 4 | (def no-init) 5 | (def with-init val)) 6 | 7 | (defn 8 | ^{:expected-output ["Unbound: #'codegen.test.def-simple/no-init" 42]} 9 | -main [] 10 | (deftest 42) 11 | [(str (deref #'no-init)) with-init]) 12 | -------------------------------------------------------------------------------- /pkg/reader/resolver.go: -------------------------------------------------------------------------------- 1 | package reader 2 | 3 | import "github.com/glojurelang/glojure/pkg/lang" 4 | 5 | type ( 6 | SymbolResolver interface { 7 | CurrentNS() *lang.Symbol 8 | ResolveStruct(*lang.Symbol) *lang.Symbol 9 | ResolveAlias(*lang.Symbol) *lang.Symbol 10 | ResolveVar(*lang.Symbol) *lang.Symbol 11 | } 12 | ) 13 | -------------------------------------------------------------------------------- /scripts/build-glj.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | PLATFORM=$2 6 | 7 | IFS='_' read -ra OS_ARCH <<< "$PLATFORM" 8 | 9 | OS=${OS_ARCH[0]} 10 | ARCH=${OS_ARCH[1]} 11 | 12 | if [ "$ARCH" == "" ]; then 13 | BUILD_TAG="$OS" 14 | else 15 | BUILD_TAG="$ARCH && $OS" 16 | fi 17 | 18 | GOOS=$OS GOARCH=$ARCH go build -o $1 ./cmd/glj 19 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/stdlib00.out: -------------------------------------------------------------------------------- 1 | PASS: (sequential? (quote (1 2 3))) 2 | PASS: (sequential? [1 2 3]) 3 | PASS: (sequential? (range 1 5)) 4 | PASS: (sequential? (quote ())) 5 | PASS: (sequential? []) 6 | PASS: (sequential? nil) 7 | PASS: (sequential? 1) 8 | PASS: (sequential? "abc") 9 | PASS: (sequential? (concat (quote (1 2)) (quote (3 4)))) 10 | -------------------------------------------------------------------------------- /test/glojure/test_glojure/lcm.glj: -------------------------------------------------------------------------------- 1 | (ns glojure.test-glojure.lcm 2 | (:use clojure.test)) 3 | 4 | (defn lcm 5 | [& x] 6 | (letfn [(gcd [a b] (if (zero? b) a (gcd b (mod a b)))) 7 | (lcm [a b] (/ (* a b) (gcd a b)))] 8 | (reduce lcm x))) 9 | 10 | (deftest lcmtest 11 | (is (= 6 (lcm 2 3)) 12 | "lcm of 2 and 3 is 6")) 13 | 14 | (run-tests) 15 | -------------------------------------------------------------------------------- /pkg/glj/glj_test.go: -------------------------------------------------------------------------------- 1 | package glj 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/glojurelang/glojure/pkg/lang" 7 | ) 8 | 9 | func TestGLJ(t *testing.T) { 10 | mp := Var("clojure.core", "map") 11 | inc := Var("clojure.core", "inc") 12 | res := lang.PrintString(mp.Invoke(inc, Read("[1 2 3]"))) 13 | if res != "(2 3 4)" { 14 | t.Errorf("Expected (2 3 4), got %v", res) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/interop01.glj: -------------------------------------------------------------------------------- 1 | (def regexp (regexp.MustCompile "foo(.*)bar")) 2 | (println (. regexp (Match ((go-sliceof byte) "foo----bar")))) 3 | (println (. regexp (FindStringSubmatch "foo----bar"))) 4 | 5 | (println (regexp.Match "foo.*bar" nil)) 6 | (println (regexp.Match "foo.*bar" ((go-sliceof byte) "foobar"))) 7 | (println (regexp.Match "foo(.*)bar" ((go-sliceof byte) "foo----bar"))) 8 | -------------------------------------------------------------------------------- /pkg/lang/box.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | // Box is a wrapper around a value that can be used to store values of 4 | // different types in atomic.Value, which requires all stored values 5 | // to have the same concrete type. 6 | type Box struct { 7 | val interface{} 8 | } 9 | 10 | // NewBox returns a new Box that wraps the given value. 11 | func NewBox(val interface{}) *Box { 12 | return &Box{val} 13 | } 14 | -------------------------------------------------------------------------------- /pkg/lang/reduced.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | type Reduced struct { 4 | val interface{} 5 | } 6 | 7 | var ( 8 | _ IDeref = (*Reduced)(nil) 9 | ) 10 | 11 | func IsReduced(v interface{}) bool { 12 | _, ok := v.(*Reduced) 13 | return ok 14 | } 15 | 16 | func NewReduced(v interface{}) *Reduced { 17 | return &Reduced{val: v} 18 | } 19 | 20 | func (r *Reduced) Deref() interface{} { 21 | return r.val 22 | } 23 | -------------------------------------------------------------------------------- /pkg/glj/init-aot.go: -------------------------------------------------------------------------------- 1 | //go:build !glj_no_aot_stdlib 2 | 3 | package glj 4 | 5 | import ( 6 | // Add NS loaders for the standard library. 7 | _ "github.com/glojurelang/glojure/pkg/stdlib/clojure/core" 8 | _ "github.com/glojurelang/glojure/pkg/stdlib/clojure/core/async" 9 | _ "github.com/glojurelang/glojure/pkg/stdlib/clojure/core/protocols" 10 | _ "github.com/glojurelang/glojure/pkg/stdlib/glojure/go/io" 11 | ) 12 | -------------------------------------------------------------------------------- /pkg/glj/var.go: -------------------------------------------------------------------------------- 1 | package glj 2 | 3 | import "github.com/glojurelang/glojure/pkg/lang" 4 | 5 | // Var returns an IFn associated with the namespace and name. 6 | func Var(ns, name interface{}) lang.IFn { 7 | return lang.InternVarName(asSym(ns), asSym(name)) 8 | } 9 | 10 | func asSym(x interface{}) *lang.Symbol { 11 | if str, ok := x.(string); ok { 12 | return lang.NewSymbol(str) 13 | } 14 | return x.(*lang.Symbol) 15 | } 16 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/fn_closure.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.fn-closure) 2 | 3 | (defn gen-closure [the-result] (fn [] the-result)) 4 | 5 | (def closed (gen-closure 42)) 6 | 7 | (let [hidden-atom (atom 0)] 8 | (defn inc-atom [] 9 | (swap! hidden-atom inc)) 10 | (defn get-atom [] 11 | @hidden-atom)) 12 | 13 | (defn ^{:expected-output [42 1]} -main [] 14 | (inc-atom) 15 | [(closed) 16 | (get-atom)]) 17 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/require00.glj: -------------------------------------------------------------------------------- 1 | ;; todo: implement in terms of lower-level functions. load-file should 2 | ;; not be able to load arbitrary files from the file system 3 | (load-file "./functional00.glj") 4 | 5 | ;; map should now be defined 6 | (apply print (map (fn [x] (* x 100)) (list 1 2 3))) 7 | 8 | (def numList (list 1 2 3 4 5 6 7 8 9 10)) 9 | 10 | (print 11 | (eq? (reduce + 0 numList) 12 | (sum numList))) 13 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/fn_recur.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.fn-recur) 2 | 3 | (defn fib 4 | "Calculates the nth Fibonacci number using iteration with recur." 5 | [n] 6 | (let [fib-helper (fn fib-helper [a b i] 7 | (if (= i n) 8 | a 9 | (recur b (+ a b) (inc i))))] 10 | (fib-helper 0 1 0))) 11 | 12 | (defn ^{:expected-output 55} -main [] 13 | (fib 10)) 14 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/vector00.glj: -------------------------------------------------------------------------------- 1 | (def foo 42) 2 | 3 | (print []) 4 | (print [1]) 5 | (print '[foo 8 :key]) 6 | (print [foo 8 :key]) 7 | 8 | (print (empty? [])) 9 | (print (eq? [1 2] [1 (- 3 1)])) 10 | (print (eq? [1 2] [1 3])) 11 | 12 | (def myvec (vector 1 2 3 4 5)) 13 | (print myvec) 14 | (print (first myvec)) 15 | (print (rest myvec)) 16 | 17 | (print (myvec 0)) 18 | (print (myvec 2)) 19 | 20 | (print (concat [1 2] [3 4])) 21 | -------------------------------------------------------------------------------- /pkg/reader/testdata/clj-cache/read/30e28cb5f9df7ee57fbbf9f7f81765043d2e69a56db713e107d8f32c34c207bb.glj: -------------------------------------------------------------------------------- 1 | false 2 | Execution error (PatternSyntaxException) at java.util.regex.Pattern/error (Pattern.java:2028). 3 | Illegal repetition near index 37 4 | (\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9_]{{{{{+)(SNAPSHOT))? 5 | ^ 6 | 7 | Full report at: 8 | /var/folders/0m/y60b85m145lffbfdfk86129r0000gn/T/clojure-708558860762531557.edn 9 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/case_switch.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.case-switch) 2 | 3 | (defn case-test [x] 4 | (case x 5 | 1 :one 6 | 2 :two 7 | 3 :three 8 | :other)) 9 | 10 | (defn case-test-throw [x] 11 | (try 12 | (case x 13 | 1 :nope) 14 | (catch go/any e 15 | :caught))) 16 | 17 | (defn ^{:expected-output [:two :other :caught]} -main [] 18 | [(case-test 2) (case-test 42) (case-test-throw 42)]) 19 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/set_bang.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.set-bang) 2 | 3 | (def ^:dynamic x 10) 4 | 5 | (defn set!-var [] 6 | (binding [x 15] 7 | (set! x 20) 8 | (set! x (+ x 5)) 9 | x)) 10 | 11 | (defn set!-host [] 12 | (let [c (new net:http.Client)] 13 | (set! (. c Timeout) (time.Duration 1000)) 14 | (. c Timeout))) 15 | 16 | (defn ^{:expected-output [25 (time.Duration 1000)]} -main [] 17 | [(set!-var) (set!-host)]) 18 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/multifn.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.multifn) 2 | 3 | (defmulti add "a multimethod for addition" (fn [a b] (type a))) 4 | (defmethod add go/int64 [a b] (str "int64: " (+ a b))) 5 | (defmethod add go/float64 [a b] (str "float64: " (+ a b))) 6 | (defmethod add :default [a b] (str "default: " a " " b)) 7 | 8 | (defn ^{:expected-output "int64: 7 float64: 8.0 default: 3 4"} -main [] 9 | (str (add 3 4) " " (add 3.5 4.5) " " (add "3" "4"))) 10 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/macros00.glj: -------------------------------------------------------------------------------- 1 | (defmacro or-macro 2 | ;; "Evaluates exprs one at a time, from left to right. If a form 3 | ;; returns a logical true value, or returns that value and doesn't 4 | ;; evaluate any of the other expressions, otherwise it returns the 5 | ;; value of the last expression. (or) returns nil." 6 | [x & next] 7 | `(let [or# ~x] 8 | (if or# or# (or ~@next)))) 9 | 10 | (print (or-macro 1 (invalid function call))) 11 | 12 | -------------------------------------------------------------------------------- /scripts/rewrite-core/run.sh: -------------------------------------------------------------------------------- 1 | # this script should be run from the root of the repository 2 | 3 | # Most mutations are implemented in rewrite.clj, but some are 4 | # implemented in this script. Number tags are removed, as go doesn't 5 | # have boxed numbers. We also truncate core.glj at deftype. 6 | 7 | # discard file after defype for now 8 | 9 | cd scripts/rewrite-core 10 | clj -M ./rewrite.clj "../../$1" | \ 11 | sed 's/\^Number //g' | \ 12 | sed 's/:tag Number//g' 13 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/basic01.glj: -------------------------------------------------------------------------------- 1 | ;;;SKIP_PRINT_TEST 2 | ;; todo: implement in terms of lower-level functions. load-file should 3 | ;; not be able to load arbitrary files from the file system 4 | (load-file "./functional00.in") 5 | 6 | (def print mrat.core.io.print) 7 | 8 | ;; map should now be defined 9 | (apply print (map (fn (x) (* x 100)) (list 1 2 3))) 10 | 11 | (def numList (list 1 2 3 4 5 6 7 8 9 10)) 12 | 13 | (print 14 | (eq? (reduce + 0 numList) 15 | (sum numList))) 16 | -------------------------------------------------------------------------------- /internal/persistent/vector/doc.go: -------------------------------------------------------------------------------- 1 | // Package vector implements persistent vector. 2 | // 3 | // This is a Go clone of Clojure's PersistentVector type 4 | // (https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/PersistentVector.java). 5 | // For an introduction to the internals, see 6 | // https://hypirion.com/musings/understanding-persistent-vector-pt-1. 7 | // 8 | // This code has been copied from the package "src.elv.sh/pkg/persistent/vector" 9 | // with modifications for efficiency. 10 | package vector 11 | -------------------------------------------------------------------------------- /pkg/lang/keyword_test.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import "testing" 4 | 5 | func TestKeyword(t *testing.T) { 6 | kw1 := NewKeyword("foo") 7 | kw2 := NewKeyword("foo") 8 | 9 | if kw1 != kw2 { 10 | t.Errorf("NewKeyword(\"foo\") != NewKeyword(\"foo\")") 11 | } 12 | if !kw1.Equals(kw2) { 13 | t.Errorf("kw1.Equals(kw2) == false") 14 | } 15 | 16 | kw3 := NewKeyword("not-foo") 17 | if kw1 == kw3 { 18 | t.Errorf("kw1 == kw3") 19 | } 20 | if kw1.Equals(kw3) { 21 | t.Errorf("kw1.Equals(kw3) == true") 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /pkg/lang/truthiness.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import "reflect" 4 | 5 | // IsTruthy returns true if the value is truthy. 6 | func IsTruthy(v interface{}) bool { 7 | switch v := v.(type) { 8 | case nil: 9 | return false 10 | case bool: 11 | return v 12 | default: 13 | return !IsNil(v) 14 | } 15 | } 16 | 17 | func IsNil(v interface{}) bool { 18 | if v == nil { 19 | return true 20 | } 21 | rv := reflect.ValueOf(v) 22 | if rv.Kind() == reflect.Ptr && rv.IsNil() { 23 | return true 24 | } 25 | return false 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | paths-ignore: 5 | - '**.md' 6 | pull_request: 7 | branches: 8 | - main 9 | paths-ignore: 10 | - '**.md' 11 | 12 | jobs: 13 | test: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | with: 18 | submodules: recursive 19 | - uses: cachix/install-nix-action@v19 20 | with: 21 | nix_path: nixpkgs=channel:nixos-unstable 22 | - run: nix-shell --run 'PATH=$(go env GOPATH)/bin:$PATH make test' 23 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/goroutine.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.goroutine) 2 | 3 | (defn send-on-chan [ch val] 4 | (let [chVal (reflect.ValueOf ch) 5 | valVal (reflect.ValueOf val)] 6 | (.Send chVal valVal))) 7 | 8 | (defn ^{:expected-output 42} -main [] 9 | (let [ch (go/make (go/chan-of go/int64)) 10 | chVal (reflect.ValueOf ch)] 11 | (go/go (send-on-chan ch 42)) 12 | (let [[recvResult ok] (.Recv chVal)] 13 | (if (not ok) 14 | (throw (fmt.Errorf "Channel closed"))) 15 | (.Interface recvResult)))) 16 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { sources ? import ./nix/sources.nix 2 | , pkgs ? import sources.nixpkgs {} 3 | }: 4 | 5 | # set MACOSX_DEPLOYMENT_TARGET=12.0 env var in shell 6 | 7 | pkgs.mkShell { 8 | nativeBuildInputs = [ 9 | pkgs.go_1_24 10 | pkgs.clojure 11 | ]; 12 | 13 | shellHook = '' 14 | export MACOSX_DEPLOYMENT_TARGET=12.0 15 | 16 | # Remove xcbuild's xcrun from PATH 17 | export PATH=$(echo "$PATH" | sed "s|${pkgs.xcbuild.xcrun}/bin||g") 18 | 19 | # Let Xcode pick its own developer dir 20 | unset DEVELOPER_DIR 21 | ''; 22 | } 23 | -------------------------------------------------------------------------------- /pkg/lang/volatile.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import "sync" 4 | 5 | type Volatile struct { 6 | val interface{} 7 | mtx sync.RWMutex 8 | } 9 | 10 | var ( 11 | _ IDeref = (*Volatile)(nil) 12 | ) 13 | 14 | func NewVolatile(val interface{}) *Volatile { 15 | return &Volatile{ 16 | val: val, 17 | } 18 | } 19 | 20 | func (v *Volatile) Deref() interface{} { 21 | v.mtx.RLock() 22 | defer v.mtx.RUnlock() 23 | return v.val 24 | } 25 | 26 | func (v *Volatile) Reset(val interface{}) interface{} { 27 | v.mtx.Lock() 28 | defer v.mtx.Unlock() 29 | v.val = val 30 | return val 31 | } 32 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/prog02.glj: -------------------------------------------------------------------------------- 1 | (def table 2 | ;; condition, ifval, elseval 3 | ( list 4 | (list 1 1 0) 5 | (list true 1 0) 6 | (list false 1 0) 7 | (list 0 1 0) 8 | )) 9 | 10 | (defn testTable [tbl] 11 | (if (eq? tbl (list)) 12 | true ;; done 13 | (list 14 | (runTest (first tbl)) 15 | (testTable (rest tbl))))) 16 | 17 | (defn runTest [test] 18 | (print 19 | (first test) 20 | (if (first test) 21 | (first (rest test)) 22 | (first (rest (rest test)))))) 23 | 24 | (testTable table) 25 | -------------------------------------------------------------------------------- /nix/sources.json: -------------------------------------------------------------------------------- 1 | { 2 | "nixpkgs": { 3 | "branch": "master", 4 | "description": "Nix Packages collection", 5 | "homepage": "", 6 | "owner": "NixOS", 7 | "repo": "nixpkgs", 8 | "rev": "2b78ecc45e163d655c08c9a4cffb4a91c66d0493", 9 | "sha256": "092l8qa82w97hj2pal2hzdzxnw20jya5pz9x2igj7swhpzmhxyzk", 10 | "type": "tarball", 11 | "url": "https://github.com/NixOS/nixpkgs/archive/2b78ecc45e163d655c08c9a4cffb4a91c66d0493.tar.gz", 12 | "url_template": "https://github.com///archive/.tar.gz" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/sort00.glj: -------------------------------------------------------------------------------- 1 | (defn check-sorted [xs] 2 | (let [sorted (sort xs) 3 | ok (reduce #(and %1 %2) 4 | true 5 | (map #(<= (nth sorted %) (nth sorted (+ % 1))) 6 | (range (- (count sorted) 1))))] 7 | (if ok 8 | (println "OK") 9 | (do (println "FAIL") 10 | (println " sorted: " sorted) 11 | (println " xs: " xs))))) 12 | 13 | ;;(check-sorted [1 5 2]) 14 | ;; This length (261) exposed some odd behavior when using 15 | ;; sort.SliceStable. 16 | (check-sorted (apply vector (range 261))) 17 | -------------------------------------------------------------------------------- /pkg/lang/ifn.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | // FnFunc is a wrapped Go function that implements the IFn interface. 4 | type FnFunc func(args ...any) any 5 | 6 | var ( 7 | _ IFn = FnFunc(nil) 8 | ) 9 | 10 | func NewFnFunc(fn func(args ...any) any) FnFunc { 11 | return FnFunc(fn) 12 | } 13 | 14 | func (f FnFunc) Invoke(args ...any) any { 15 | return f(args...) 16 | } 17 | 18 | func (f FnFunc) ApplyTo(args ISeq) any { 19 | return f(seqToSlice(args)...) 20 | } 21 | 22 | func (f FnFunc) Meta() IPersistentMap { 23 | return nil 24 | } 25 | 26 | func (f FnFunc) WithMeta(meta IPersistentMap) any { 27 | // no-op 28 | return f 29 | } 30 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/case00.glj: -------------------------------------------------------------------------------- 1 | ;; simple case 2 | (print (case (+ 41 1) 3 | "foo" "bar" 4 | 42 "baz" 5 | "qux" "quux")) 6 | 7 | ;; no match 8 | (print (case (+ 1 2) 9 | "foo" "bar" 10 | 42 "baz" 11 | "qux" "quux")) 12 | 13 | ;; no match with default 14 | (print (case (+ 1 2) 15 | "foo" "bar" 16 | 42 "baz" 17 | "qux" "quux" 18 | "default")) 19 | 20 | ;; list of options 21 | (print (case (+ 1 2) 22 | "foo" "bar" 23 | 42 "baz" 24 | "qux" "quux" 25 | (9 8 3 2) "it was one of these" 26 | "default")) 27 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/functions00.glj: -------------------------------------------------------------------------------- 1 | ;; Define functions with the "def" special form whose first argument 2 | ;; is a list of symbols. The first symbol is the name of the function, 3 | ;; and the rest are the arguments. All expressions that follow define 4 | ;; the function body. The final expression in the function body is the 5 | ;; return value of the function. 6 | (defn test [] 7 | (print 1) 8 | (print 2)) 9 | (test) 10 | 11 | ;; The above is a shorthand for defining a symbol whose value is an 12 | ;; anonymous lambda (the "fn" special form). 13 | (def testFn 14 | (fn [] 15 | (print 3) 16 | (print 4))) 17 | (testFn) 18 | -------------------------------------------------------------------------------- /pkg/lang/arraylist.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | // ArrayList is a minimal implementation of a subset of Java's 4 | // ArrayList to replace uses of java.util.ArrayList in the Clojure 5 | // standard library. 6 | type ArrayList struct { 7 | data []any 8 | } 9 | 10 | func NewArrayList(items []any) *ArrayList { 11 | return &ArrayList{ 12 | data: items, 13 | } 14 | } 15 | 16 | func (al *ArrayList) Add(item any) { 17 | al.data = append(al.data, item) 18 | } 19 | 20 | func (al *ArrayList) Clear() { 21 | al.data = []any{} 22 | } 23 | 24 | func (al *ArrayList) IsEmpty() bool { 25 | return len(al.data) == 0 26 | } 27 | 28 | func (al *ArrayList) ToArray() []any { 29 | return al.data 30 | } 31 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/regex_literal.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.regex-literal) 2 | 3 | ;; Test regex literal compilation with various patterns 4 | (def pattern-literal #"[a-z]+") 5 | 6 | ;; Test with special characters that need escaping 7 | (def pattern-with-backslash #"\\d+") 8 | (def pattern-with-quotes #"\"quoted\"") 9 | (def pattern-with-newline #"line1\nline2") 10 | (def pattern-complex #"^(?:foo|bar)\s*=\s*(['\"]?)(.+?)$") 11 | 12 | ;; Test re-pattern function call 13 | (def pattern-function (re-pattern "[0-9]+")) 14 | 15 | ;; Test using the patterns 16 | (defn test-match [s] 17 | (and (re-matches pattern-literal "hello") 18 | (re-matches pattern-function "12345"))) -------------------------------------------------------------------------------- /internal/deps/deps.go: -------------------------------------------------------------------------------- 1 | package deps 2 | 3 | import "fmt" 4 | 5 | // Deps is a struct that contains all the dependencies for the 6 | // Glojure application. 7 | type Deps struct { 8 | goModName string 9 | goModDir string 10 | 11 | Pkgs []string 12 | Deps map[string]map[string]string 13 | } 14 | 15 | func (d *Deps) Gen() error { 16 | if err := d.Get(); err != nil { 17 | return fmt.Errorf("failed to fetch dependencies: %w", err) 18 | } 19 | if err := d.Embed(); err != nil { 20 | return fmt.Errorf("failed to generate embed for packages: %w", err) 21 | } 22 | if err := d.GLJ(); err != nil { 23 | return fmt.Errorf("failed to generate glj main: %w", err) 24 | } 25 | 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/seq00.glj: -------------------------------------------------------------------------------- 1 | ;; TODO: rename the test. this is actually a tail call test 2 | 3 | (defn = [a b] (eq? a b)) 4 | 5 | (defn <= [a b] 6 | (or (< a b) (= a b))) 7 | 8 | (defn >= [a b] 9 | (or (> a b) (= a b))) 10 | 11 | (defn range [beg end] 12 | (let [range-tail 13 | (fn range-tail [suffix beg end] 14 | (if (>= beg end) 15 | suffix 16 | (range-tail (conj suffix (- end 1)) beg (- end 1))))] 17 | (if (<= beg end) 18 | (range-tail '() beg end) 19 | '()))) 20 | 21 | (defn add [a b] 22 | (+ a b)) 23 | 24 | (println (length (range 0 10))) 25 | ;;;; requires tail call optimization 26 | ;; (println (length (range 0 1000000))) 27 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/quasiquote00.out: -------------------------------------------------------------------------------- 1 | (quote user/foobar) 2 | (clojure.core/list) 3 | (clojure.core/seq (clojure.core/concat (clojure.core/list (quote user/a)) (clojure.core/list (quote user/b)) (clojure.core/list (quote user/c)))) 4 | (clojure.core/seq (clojure.core/concat (clojure.core/list (quote user/a)) (clojure.core/list (+ 1 2)))) 5 | (clojure.core/seq (clojure.core/concat (clojure.core/list (quote user/a)) [(+ 1 2)])) 6 | (clojure.core/seq (clojure.core/concat (clojure.core/list (quote do.not.Qualify)))) 7 | (clojure.core/apply clojure.core/hash-map (clojure.core/seq (clojure.core/concat (clojure.core/list :x) (clojure.core/list [])))) 8 | (clojure.core/seq (clojure.core/concat (clojure.core/list (quote resolved.alias/foo)))) 9 | -------------------------------------------------------------------------------- /pkg/runtime/scope.go: -------------------------------------------------------------------------------- 1 | package runtime 2 | 3 | import "github.com/glojurelang/glojure/pkg/lang" 4 | 5 | type scope struct { 6 | parent *scope 7 | syms map[string]interface{} 8 | } 9 | 10 | func newScope() *scope { 11 | return &scope{syms: make(map[string]interface{})} 12 | } 13 | 14 | func (s *scope) define(sym *lang.Symbol, val interface{}) { 15 | s.syms[sym.String()] = val 16 | } 17 | 18 | func (s *scope) push() *scope { 19 | return &scope{parent: s, syms: make(map[string]interface{})} 20 | } 21 | 22 | func (s *scope) lookup(sym *lang.Symbol) (interface{}, bool) { 23 | if v, ok := s.syms[sym.String()]; ok { 24 | return v, true 25 | } 26 | if s.parent == nil { 27 | return nil, false 28 | } 29 | return s.parent.lookup(sym) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/runtime/nsloaders.go: -------------------------------------------------------------------------------- 1 | package runtime 2 | 3 | var ( 4 | // nsLoaders is a map of namespace resource names to their loader 5 | // functions. Used for pre-compiled namespaces. 6 | nsLoaders = map[string]func(){} 7 | ) 8 | 9 | // RegisterNSLoader registers a loader function for a namespace given its resource name 10 | // (i.e. root path with slashes, no extension). 11 | func RegisterNSLoader(nsResource string, loader func()) { 12 | if _, exists := nsLoaders[nsResource]; exists { 13 | panic("namespace loader already registered for " + nsResource) 14 | } 15 | nsLoaders[nsResource] = loader 16 | } 17 | 18 | // GetNSLoader retrieves the loader function for a namespace given its resource name. 19 | func GetNSLoader(nsResource string) func() { 20 | return nsLoaders[nsResource] 21 | } 22 | -------------------------------------------------------------------------------- /pkg/glj/init.go: -------------------------------------------------------------------------------- 1 | package glj 2 | 3 | import ( 4 | "io" 5 | "os" 6 | 7 | // Add the Go standard library to the pkgmap. 8 | _ "github.com/glojurelang/glojure/pkg/gen/gljimports" 9 | "github.com/glojurelang/glojure/pkg/lang" 10 | 11 | "github.com/glojurelang/glojure/pkg/runtime" 12 | ) 13 | 14 | func init() { 15 | initEnv(os.Stdout) 16 | } 17 | 18 | func initEnv(stdout io.Writer) lang.Environment { 19 | // TODO: clean up this code. copied from rtcompat.go. 20 | kvs := make([]interface{}, 0, 3) 21 | for _, vr := range []*lang.Var{lang.VarCurrentNS, lang.VarWarnOnReflection, lang.VarUncheckedMath, lang.VarDataReaders} { 22 | kvs = append(kvs, vr, vr.Deref()) 23 | } 24 | lang.PushThreadBindings(lang.NewMap(kvs...)) 25 | 26 | return runtime.NewEnvironment(runtime.WithStdout(stdout)) 27 | } 28 | -------------------------------------------------------------------------------- /scripts/gen-gljimports.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # make a temp directory with mktemp and build the project there 6 | DIR=$(mktemp -d) 7 | EXE="${DIR}/gen-import-interop" 8 | 9 | go build -o "${EXE}" ./cmd/gen-import-interop 10 | 11 | OUTPUT_FILE=$1 12 | PLATFORM=$2 13 | GO=$3 14 | 15 | IFS='_' read -ra OS_ARCH <<< "$PLATFORM" 16 | 17 | OS=${OS_ARCH[0]} 18 | ARCH=${OS_ARCH[1]} 19 | 20 | if [ "$ARCH" == "" ]; then 21 | BUILD_TAG="$OS" 22 | else 23 | BUILD_TAG="$ARCH && $OS" 24 | fi 25 | 26 | # disable CGO to avoid cross-compilation issues on darwin. 27 | IMPORTS=$(GOROOT=$($GO env GOROOT) CGO_ENABLED=0 GOOS=$OS GOARCH=$ARCH "$EXE") 28 | echo "//go:build $BUILD_TAG" > "$OUTPUT_FILE" 29 | echo >> "$OUTPUT_FILE" 30 | echo "$IMPORTS" >> "$OUTPUT_FILE" 31 | 32 | # clean up 33 | rm -rf "${DIR}" 34 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/glojurelang/glojure 2 | 3 | go 1.24 4 | 5 | require ( 6 | bitbucket.org/pcastools/hash v1.0.5 7 | github.com/chzyer/readline v1.5.1 8 | github.com/kylelemons/godebug v1.1.0 9 | github.com/mitchellh/hashstructure/v2 v2.0.2 10 | github.com/modern-go/gls v0.0.0-20220109145502-612d0167dce5 11 | github.com/stretchr/testify v1.8.1 12 | go4.org/intern v0.0.0-20220617035311-6925f38cc365 13 | ) 14 | 15 | require ( 16 | github.com/davecgh/go-spew v1.1.1 // indirect 17 | github.com/google/uuid v1.6.0 // indirect 18 | github.com/modern-go/reflect2 v1.0.2 // indirect 19 | github.com/pmezard/go-difflib v1.0.0 // indirect 20 | go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 // indirect 21 | golang.org/x/sys v0.6.0 // indirect 22 | gopkg.in/yaml.v3 v3.0.1 // indirect 23 | ) 24 | -------------------------------------------------------------------------------- /internal/goid/goid_wasm.go: -------------------------------------------------------------------------------- 1 | //go:build wasm 2 | 3 | package goid 4 | 5 | import ( 6 | "bytes" 7 | "runtime" 8 | "strconv" 9 | ) 10 | 11 | var ( 12 | goroutinePrefix = []byte("goroutine ") 13 | ) 14 | 15 | func Get() int64 { 16 | buf := make([]byte, 32) 17 | n := runtime.Stack(buf, false) 18 | buf = buf[:n] 19 | // goroutine 1 [running]: ... 20 | 21 | if !bytes.HasPrefix(buf, goroutinePrefix) { 22 | panic("unexpected goroutine stack format, missing prefix") 23 | } 24 | buf = buf[len(goroutinePrefix):] 25 | 26 | i := bytes.IndexByte(buf, ' ') 27 | if i < 0 { 28 | panic("unexpected goroutine stack format, missing space") 29 | } 30 | 31 | id, err := strconv.Atoi(string(buf[:i])) 32 | if err != nil { 33 | panic("unexpected goroutine stack format, invalid id") 34 | } 35 | return int64(id) 36 | } 37 | -------------------------------------------------------------------------------- /pkg/lang/type.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "bytes" 5 | "reflect" 6 | "regexp" 7 | ) 8 | 9 | var ( 10 | Throwable = reflect.TypeOf((interface{})(nil)) 11 | 12 | // TODO: convert use of 'matcher' in core.glj to fit go's 13 | // regexps. This supresses errors but doesn't actually work. 14 | Matcher = reflect.TypeOf(®exp.Regexp{}) 15 | 16 | // TODO: rework use of PrintWriter in core.glj 17 | PrintWriter = reflect.TypeOf(&bytes.Buffer{}) 18 | ) 19 | 20 | func HasType(t reflect.Type, v interface{}) bool { 21 | if v == nil { 22 | return false 23 | } 24 | vType := reflect.TypeOf(v) 25 | switch { 26 | case vType == t, vType.AssignableTo(t): 27 | return true 28 | default: 29 | return false 30 | } 31 | } 32 | 33 | func TypeOf(v interface{}) reflect.Type { 34 | return reflect.TypeOf(v) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/bool00.out: -------------------------------------------------------------------------------- 1 | ### AND 2 | eval true 3 | eval true 4 | (true true) => true 5 | eval true 6 | eval false 7 | (true false) => false 8 | eval false 9 | (false true) => false 10 | eval false 11 | (false false) => false 12 | eval nil 13 | (nil true) => nil 14 | eval nil 15 | (nil false) => nil 16 | eval true 17 | eval nil 18 | (true nil) => nil 19 | eval false 20 | (false nil) => false 21 | 22 | ### OR 23 | eval true 24 | (true true) => true 25 | eval true 26 | (true false) => true 27 | eval false 28 | eval true 29 | (false true) => true 30 | eval false 31 | eval false 32 | (false false) => false 33 | eval nil 34 | eval true 35 | (nil true) => true 36 | eval nil 37 | eval false 38 | (nil false) => false 39 | eval true 40 | (true nil) => true 41 | eval false 42 | eval nil 43 | (false nil) => false 44 | 45 | ### bool conversion 46 | 2 47 | 1 48 | -------------------------------------------------------------------------------- /pkg/repl/options.go: -------------------------------------------------------------------------------- 1 | package repl 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/glojurelang/glojure/pkg/lang" 7 | ) 8 | 9 | type options struct { 10 | stdin io.Reader 11 | stdout io.Writer 12 | namespace string 13 | env lang.Environment 14 | } 15 | 16 | // Option is a functional option for the REPL. 17 | type Option func(*options) 18 | 19 | // WithStdin sets the stdin for the REPL. 20 | func WithStdin(r io.Reader) Option { 21 | return func(o *options) { 22 | o.stdin = r 23 | } 24 | } 25 | 26 | // WithStdout sets the stdout for the REPL. 27 | func WithStdout(w io.Writer) Option { 28 | return func(o *options) { 29 | o.stdout = w 30 | } 31 | } 32 | 33 | // WithEnvironment sets the execution environment for the REPL. 34 | func WithEnvironment(env lang.Environment) Option { 35 | return func(o *options) { 36 | o.env = env 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /pkg/reader/testdata/reader/core001.glj: -------------------------------------------------------------------------------- 1 | #_(defn = 2 | "Equality. Returns true if x equals y, false if not. Same as Java 3 | x.equals(y) except it also works for nil. Boxed numbers must have 4 | same type. Clojure's immutable data structures define equals() (and 5 | thus =) as a value, not an identity, comparison." 6 | {:inline (fn [x y] `(. clojure.lang.Util equals ~x ~y)) 7 | :inline-arities #{2} 8 | :added "1.0"} 9 | ([x] true) 10 | ([x y] (clojure.lang.Util/equals x y)) 11 | ([x y & more] 12 | (if (= x y) 13 | (if (next more) 14 | (recur y (first more) (next more)) 15 | (= y (first more))) 16 | false))) 17 | 18 | (defn not= 19 | "Same as (not (= obj1 obj2))" 20 | {:tag Boolean 21 | :added "1.0" 22 | :static true} 23 | ([x] false) 24 | ([x y] (not (= x y))) 25 | ([x y & more] 26 | (not (apply = x y more)))) 27 | -------------------------------------------------------------------------------- /pkg/lang/chunkbuffer.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | type ( 4 | ChunkBuffer struct { 5 | buffer []interface{} 6 | end int 7 | } 8 | ) 9 | 10 | var ( 11 | _ Counted = (*ChunkBuffer)(nil) 12 | ) 13 | 14 | func NewChunkBuffer(capacity int) *ChunkBuffer { 15 | return &ChunkBuffer{ 16 | buffer: make([]interface{}, capacity), 17 | end: 0, 18 | } 19 | } 20 | 21 | func (cb *ChunkBuffer) Add(item interface{}) { 22 | // following Clojure's implementation, we pre-allocate the 23 | // buffer. additions beyond the initial capacity will cause a 24 | // runtime error. 25 | cb.buffer[cb.end] = item 26 | cb.end++ 27 | } 28 | 29 | func (cb *ChunkBuffer) Chunk() IChunk { 30 | newSlice := NewSliceChunk(cb.buffer[:cb.end]) 31 | 32 | cb.buffer = nil 33 | cb.end = 0 34 | 35 | return newSlice 36 | } 37 | 38 | func (cb *ChunkBuffer) Count() int { 39 | return cb.end 40 | } 41 | 42 | func (cb *ChunkBuffer) xxx_counted() {} 43 | -------------------------------------------------------------------------------- /pkg/lang/functional.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | // Reduce applies the given function to each element of the ISeq. 4 | func Reduce(f func(interface{}, interface{}) interface{}, seq ISeq) interface{} { 5 | if Seq(seq) == nil { 6 | panic("reduce of empty sequence without initial value") 7 | } 8 | return ReduceInit(f, seq.First(), seq.Next()) 9 | } 10 | 11 | func ReduceInit(f func(interface{}, interface{}) interface{}, init interface{}, seq ISeq) interface{} { 12 | var res interface{} = init 13 | for ; seq != nil; seq = seq.Next() { 14 | res = f(res, seq.First()) 15 | if IsReduced(res) { 16 | return res.(IDeref).Deref() 17 | } 18 | } 19 | return res 20 | } 21 | 22 | func ReduceKV(f func(init, k, v interface{}) interface{}, init, coll interface{}) interface{} { 23 | return ReduceInit(func(init, e interface{}) interface{} { 24 | return f(init, e.(IMapEntry).Key(), e.(IMapEntry).Val()) 25 | }, init, Seq(coll)) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/stdlib00.glj: -------------------------------------------------------------------------------- 1 | (defmacro expect 2 | [test-expr expect] 3 | `(let [got ~test-expr] 4 | (if (not (= got ~expect)) 5 | (print "FAIL: " '~test-expr " got " got " expected " ~expect) 6 | (print "PASS: " '~test-expr)))) 7 | 8 | 9 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 10 | ;; Sequential tests 11 | ;; Examples from: https://clojuredocs.org/clojure.core/sequential_q 12 | (expect (sequential? '(1 2 3)) true) 13 | (expect (sequential? [1 2 3]) true) 14 | (expect (sequential? (range 1 5)) true) 15 | (expect (sequential? '()) true) 16 | (expect (sequential? []) true) 17 | (expect (sequential? nil) false) 18 | (expect (sequential? 1) false) 19 | (expect (sequential? "abc") false) 20 | (expect (sequential? (concat '(1 2) '(3 4))) true) 21 | 22 | ;; TODO 23 | ;; (expect (sequential? {:a 2 :b 1}) false) 24 | 25 | ;; TODO 26 | ;; (expect (sequential? #{1 2 3}) false) 27 | -------------------------------------------------------------------------------- /scripts/rewrite-core/originals/uuid.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.uuid) 10 | 11 | (defn- default-uuid-reader [form] 12 | (if (string? form) 13 | (java.util.UUID/fromString form) 14 | (throw (IllegalArgumentException. "#uuid data reader expected string")))) 15 | 16 | (defmethod print-method java.util.UUID [uuid ^java.io.Writer w] 17 | (.write w (str "#uuid \"" (str uuid) "\""))) 18 | 19 | (defmethod print-dup java.util.UUID [o w] 20 | (print-method o w)) 21 | -------------------------------------------------------------------------------- /cmd/glj/main.go: -------------------------------------------------------------------------------- 1 | //go:build !wasm 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "log" 8 | "os" 9 | "os/exec" 10 | "syscall" 11 | 12 | "github.com/glojurelang/glojure/internal/deps" 13 | 14 | // Bootstrap the runtime 15 | _ "github.com/glojurelang/glojure/pkg/glj" 16 | "github.com/glojurelang/glojure/pkg/gljmain" 17 | ) 18 | 19 | func main() { 20 | dps, err := deps.Load() 21 | if err != nil { 22 | log.Fatal(err) 23 | } 24 | if dps != nil { 25 | if err := dps.Gen(); err != nil { 26 | panic(err) 27 | } 28 | 29 | exe, err := exec.LookPath("go") 30 | if err != nil { 31 | panic(fmt.Errorf("failed to find `go` executable: %v", err)) 32 | } 33 | 34 | argv := append([]string{"go", "run", "./glj/cmd/glj"}, os.Args[1:]...) 35 | if err := syscall.Exec(exe, argv, os.Environ()); err != nil { 36 | log.Fatalf("failed to run %v: %v", exe, err) 37 | } 38 | panic("a successful exec syscall should replace this process") 39 | } 40 | 41 | gljmain.Main(os.Args[1:]) 42 | } 43 | -------------------------------------------------------------------------------- /pkg/stdlib/clojure/uuid.glj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns clojure.uuid) 10 | 11 | (defn- default-uuid-reader [form] 12 | (if (string? form) 13 | (java.util.UUID/fromString form) 14 | (throw (github.com:glojurelang:glojure:pkg:lang.NewIllegalArgumentError "#uuid data reader expected string")))) 15 | 16 | (defmethod print-method github.com:google:uuid.UUID [uuid ^io.Writer w] 17 | (github.com:glojurelang:glojure:pkg:lang.WriteWriter w (str "#uuid \"" (str uuid) "\""))) 18 | 19 | (defmethod print-dup github.com:google:uuid.UUID [o w] 20 | (print-method o w)) 21 | -------------------------------------------------------------------------------- /pkg/lang/hashes_test.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestHashEquivalences(t *testing.T) { 9 | // test cases are sets of values that should hash to the same value 10 | testCases := [][]any{ 11 | {nil, uint32(0)}, 12 | {NewList(NewKeyword("a"), NewKeyword("b")), NewVector(NewKeyword("a"), NewKeyword("b"))}, 13 | {NewList(), NewVector()}, 14 | {NewMap(NewKeyword("a"), NewKeyword("b")), NewPersistentHashMap(NewKeyword("a"), NewKeyword("b"))}, 15 | {NewMap(), NewPersistentHashMap()}, 16 | } 17 | 18 | for i, group := range testCases { 19 | group := group // capture range variable 20 | t.Run(fmt.Sprintf("group_%d", i), func(t *testing.T) { 21 | if len(group) < 2 { 22 | t.Fatalf("test case must have at least two elements") 23 | } 24 | expectedHash := Hash(group[0]) 25 | for _, v := range group[1:] { 26 | h := Hash(v) 27 | if h != expectedHash { 28 | t.Errorf("Hash(%v [%T]) = %d; want %d", v, v, h, expectedHash) 29 | } 30 | } 31 | }) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /pkg/lang/writer.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | // AppendWriter is a shim for clojure.core's use of Java's append() 9 | // method, which is not available on go's io.Writer interface. 10 | func AppendWriter(w io.Writer, v interface{}) io.Writer { 11 | var err error 12 | switch v := v.(type) { 13 | case string: 14 | _, err = w.Write([]byte(v)) 15 | case []byte: 16 | _, err = w.Write(v) 17 | case rune: 18 | _, err = w.Write([]byte{byte(v)}) 19 | case Char: 20 | _, err = w.Write([]byte{byte(v)}) 21 | default: 22 | err = fmt.Errorf("unsupported type %T", v) 23 | } 24 | 25 | if err != nil { 26 | panic(err) 27 | } 28 | return w 29 | } 30 | 31 | func WriteWriter(w io.Writer, v interface{}) io.Writer { 32 | var err error 33 | switch v := v.(type) { 34 | case string: 35 | _, err = w.Write([]byte(v)) 36 | case []byte: 37 | _, err = w.Write(v) 38 | default: 39 | err = fmt.Errorf("unsupported type %T", v) 40 | } 41 | if err != nil { 42 | panic(err) 43 | } 44 | return w 45 | } 46 | -------------------------------------------------------------------------------- /internal/deps/glj.go: -------------------------------------------------------------------------------- 1 | package deps 2 | 3 | import ( 4 | "fmt" 5 | "go/format" 6 | "os" 7 | ) 8 | 9 | // GLJ generates a Go main package in glj/cmd/glj/main.go that 10 | // implements the glj command with the project's dependencies and 11 | // packages available. 12 | func (d *Deps) GLJ() error { 13 | main := fmt.Sprintf(mainTemplate, d.goModName, d.goModName) 14 | src, err := format.Source([]byte(main)) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | if err := os.MkdirAll("./glj/cmd/glj", 0755); err != nil { 20 | return err 21 | } 22 | 23 | f, err := os.Create("./glj/cmd/glj/main.go") 24 | if err != nil { 25 | return err 26 | } 27 | defer f.Close() 28 | 29 | if _, err := f.Write(src); err != nil { 30 | return err 31 | } 32 | 33 | return nil 34 | } 35 | 36 | const ( 37 | mainTemplate = ` 38 | // Code generated by glj. DO NOT EDIT. 39 | 40 | package main 41 | 42 | import ( 43 | "os" 44 | 45 | "github.com/glojurelang/glojure/pkg/gljmain" 46 | 47 | _ %q 48 | _ "%s/glj/gljimports" 49 | ) 50 | 51 | func main() { 52 | gljmain.Main(os.Args[1:]) 53 | } 54 | ` 55 | ) 56 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/bool00.glj: -------------------------------------------------------------------------------- 1 | (load-file "./testtab.glj") 2 | (load-file "./functional00.glj") 3 | 4 | (def tbl 5 | '((true true) 6 | (true false) 7 | (false true) 8 | (false false) 9 | (nil true) 10 | (nil false) 11 | (true nil) 12 | (false nil))) 13 | 14 | (defn pret [val] 15 | ;; print val then return it 16 | (println "eval" val) 17 | val) 18 | 19 | ;;; AND 20 | 21 | (defn testAnd [bools] 22 | (if (empty? bools) 23 | true 24 | (and (pret (first bools)) 25 | (testAnd (rest bools))))) 26 | 27 | (println "### AND") 28 | (testTable tbl 29 | (fn [bools] 30 | (println bools "=>" (testAnd bools)))) 31 | 32 | ;;; OR 33 | 34 | (defn testOr [bools] 35 | (if (empty? bools) 36 | false 37 | (or (pret (first bools)) 38 | (testOr (rest bools))))) 39 | 40 | (println) 41 | (println "### OR") 42 | (testTable tbl 43 | (fn [bools] 44 | (println bools "=>" (testOr bools)))) 45 | 46 | ;; bool conversion 47 | (println) 48 | (println "### bool conversion") 49 | (println (and true 2)) 50 | (println (or false 1)) 51 | -------------------------------------------------------------------------------- /pkg/lang/delay.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import "sync" 4 | 5 | type ( 6 | Delay struct { 7 | val any 8 | exception any 9 | fn IFn 10 | mtx *sync.Mutex 11 | } 12 | ) 13 | 14 | var ( 15 | _ IDeref = (*Delay)(nil) 16 | _ IPending = (*Delay)(nil) 17 | ) 18 | 19 | func NewDelay(fn IFn) *Delay { 20 | return &Delay{ 21 | fn: fn, 22 | mtx: &sync.Mutex{}, 23 | } 24 | } 25 | 26 | func (d *Delay) realize() { 27 | l := d.mtx 28 | if l == nil { 29 | return 30 | } 31 | 32 | l.Lock() 33 | defer l.Unlock() 34 | 35 | if d.fn != nil { 36 | defer func() { 37 | if r := recover(); r != nil { 38 | d.exception = r 39 | } 40 | }() 41 | d.val = d.fn.Invoke() 42 | d.fn = nil 43 | d.mtx = nil 44 | } 45 | } 46 | 47 | func (d *Delay) Deref() any { 48 | if d.mtx != nil { 49 | d.realize() 50 | } 51 | if d.exception != nil { 52 | // TODO: look into Util.sneakyThrow in clojure. can we do something similar in Go? 53 | panic(d.exception) 54 | } 55 | return d.val 56 | } 57 | 58 | func (d *Delay) IsRealized() bool { 59 | return d.mtx == nil 60 | } 61 | 62 | func ForceDelay(x any) any { 63 | if d, ok := x.(*Delay); ok { 64 | return d.Deref() 65 | } 66 | return x 67 | } 68 | -------------------------------------------------------------------------------- /pkg/lang/slicechunk.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import "errors" 4 | 5 | type ( 6 | SliceChunk struct { 7 | slc []interface{} 8 | } 9 | ) 10 | 11 | var ( 12 | _ IChunk = (*SliceChunk)(nil) 13 | ) 14 | 15 | func NewSliceChunk(slc []interface{}) *SliceChunk { 16 | return &SliceChunk{ 17 | slc: slc, 18 | } 19 | } 20 | 21 | func (sc *SliceChunk) Count() int { 22 | return len(sc.slc) 23 | } 24 | 25 | func (sc *SliceChunk) xxx_counted() {} 26 | 27 | func (sc *SliceChunk) DropFirst() IChunk { 28 | if len(sc.slc) == 0 { 29 | panic(errors.New("DropFirst of empty chunk")) 30 | } 31 | return NewSliceChunk(sc.slc[1:]) 32 | } 33 | 34 | func (sc *SliceChunk) Nth(i int) interface{} { 35 | return sc.slc[i] 36 | } 37 | 38 | func (sc *SliceChunk) NthDefault(i int, def interface{}) interface{} { 39 | if i >= 0 && i < len(sc.slc) { 40 | return sc.Nth(i) 41 | } 42 | return def 43 | } 44 | 45 | func (sc *SliceChunk) ReduceInit(fn IFn, init interface{}) interface{} { 46 | ret := fn.Invoke(init, sc.slc[0]) 47 | if IsReduced(ret) { 48 | return ret 49 | } 50 | for i := 1; i < len(sc.slc); i++ { 51 | ret = fn.Invoke(ret, sc.slc[i]) 52 | if IsReduced(ret) { 53 | return ret 54 | } 55 | } 56 | return ret 57 | } 58 | -------------------------------------------------------------------------------- /pkg/lang/amapentry.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | type ( 4 | AMapEntry interface { 5 | APersistentVector 6 | 7 | IMapEntry 8 | } 9 | ) 10 | 11 | func amapentryNth(a AMapEntry, i int) any { 12 | if i == 0 { 13 | return a.Key() 14 | } 15 | if i == 1 { 16 | return a.Val() 17 | } 18 | panic(NewIndexOutOfBoundsError()) 19 | } 20 | 21 | func amapentryNthDefault(a AMapEntry, i int, notFound any) any { 22 | if i == 0 { 23 | return a.Key() 24 | } 25 | if i == 1 { 26 | return a.Val() 27 | } 28 | return notFound 29 | } 30 | 31 | func amapentryAssocN(a AMapEntry, i int, val any) IPersistentVector { 32 | return amapentryAsVector(a).AssocN(i, val) 33 | } 34 | 35 | func amapentryCount(a AMapEntry) int { 36 | return 2 37 | } 38 | 39 | func amapentryAsVector(a AMapEntry) IPersistentVector { 40 | return NewVector(a.Key(), a.Val()) 41 | } 42 | 43 | func amapentrySeq(a AMapEntry) ISeq { 44 | return amapentryAsVector(a).Seq() 45 | } 46 | 47 | func amapentryCons(a AMapEntry, o any) Conser { 48 | return amapentryAsVector(a).Cons(o) 49 | } 50 | 51 | func amapentryEmpty(a AMapEntry) IPersistentCollection { 52 | return nil 53 | } 54 | 55 | func amapentryPop(a AMapEntry) IPersistentStack { 56 | return NewVector(a.Key()) 57 | } 58 | -------------------------------------------------------------------------------- /pkg/lang/apersistentset.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import "github.com/glojurelang/glojure/internal/murmur3" 4 | 5 | type ( 6 | APersistentSet interface { 7 | AFn 8 | IPersistentSet 9 | IHashEq 10 | Hasher 11 | } 12 | ) 13 | 14 | func apersistentsetEquiv(a APersistentSet, o any) bool { 15 | set, ok := o.(IPersistentSet) // TODO: more general? 16 | if !ok { 17 | return false 18 | } 19 | 20 | if a.Count() != set.Count() { 21 | return false 22 | } 23 | 24 | for s := a.Seq(); s != nil; s = s.Next() { 25 | if !set.Contains(s.First()) { 26 | return false 27 | } 28 | } 29 | return true 30 | } 31 | 32 | func apersistentsetHash(hc *uint32, a APersistentSet) uint32 { 33 | if *hc != 0 { 34 | return *hc 35 | } 36 | 37 | // Following Clojure's APersistentSet.hashCode logic: 38 | // Sum of hash values of all elements 39 | var hash uint32 = 0 40 | for seq := a.Seq(); seq != nil; seq = seq.Next() { 41 | hash += Hash(seq.First()) 42 | } 43 | *hc = hash 44 | return hash 45 | } 46 | 47 | func apersistentsetHashEq(hc *uint32, a APersistentSet) uint32 { 48 | if *hc != 0 { 49 | return *hc 50 | } 51 | 52 | hash := murmur3.HashUnordered(seqToInternalSeq(a.Seq()), HashEq) 53 | *hc = hash 54 | return hash 55 | } 56 | -------------------------------------------------------------------------------- /pkg/pkgmap/pkgmap.go: -------------------------------------------------------------------------------- 1 | package pkgmap 2 | 3 | import ( 4 | "strings" 5 | "sync" 6 | ) 7 | 8 | var ( 9 | pkgMap = map[string]interface{}{} 10 | // TODO: lock-free map 11 | mtx sync.RWMutex 12 | ) 13 | 14 | // Set sets the value of the given package and export name. 15 | func Set(export string, value interface{}) { 16 | pkg, name := SplitExport(export) 17 | 18 | mtx.Lock() 19 | defer mtx.Unlock() 20 | 21 | pkgMap[mungePkg(pkg)+"."+name] = value 22 | } 23 | 24 | // Get returns the value of the given package and export name and 25 | // whether it was found. 26 | func Get(export string) (interface{}, bool) { 27 | pkg, name := SplitExport(export) 28 | 29 | mtx.RLock() 30 | defer mtx.RUnlock() 31 | 32 | v, ok := pkgMap[mungePkg(pkg)+"."+name] 33 | return v, ok 34 | } 35 | 36 | func SplitExport(export string) (string, string) { 37 | lastDot := strings.LastIndex(export, ".") 38 | if lastDot == -1 { 39 | return "", export 40 | } 41 | pkg := export[:lastDot] 42 | name := export[lastDot+1:] 43 | return pkg, name 44 | } 45 | 46 | func mungePkg(pkg string) string { 47 | return strings.Replace(pkg, "/", ":", -1) 48 | } 49 | 50 | func UnmungePkg(pkg string) string { 51 | return strings.Replace(pkg, ":", "/", -1) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/lang/map.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | func SafeMerge(m1, m2 IPersistentMap) IPersistentMap { 4 | if m1 == nil { 5 | return m2 6 | } 7 | if m2 == nil { 8 | return m1 9 | } 10 | return Merge(m1, m2) 11 | } 12 | 13 | func Merge(m1, m2 IPersistentMap) IPersistentMap { 14 | // TODO: use transient 15 | var res Associative = m1 16 | for seq := Seq(m2); seq != nil; seq = seq.Next() { 17 | entry := seq.First().(IMapEntry) 18 | res = res.Assoc(entry.Key(), entry.Val()) 19 | } 20 | return res.(IPersistentMap) 21 | } 22 | 23 | func mapEquals(m IPersistentMap, v2 interface{}) bool { 24 | if m == v2 { 25 | return true 26 | } 27 | 28 | if c, ok := v2.(Counted); ok { 29 | if m.Count() != c.Count() { 30 | return false 31 | } 32 | } 33 | assoc, ok := v2.(Associative) 34 | if !ok { 35 | return false 36 | } 37 | 38 | for seq := m.Seq(); seq != nil; seq = seq.Next() { 39 | entry := seq.First().(IMapEntry) 40 | if !assoc.ContainsKey(entry.Key()) { 41 | return false 42 | } 43 | if !Equals(entry.Val(), assoc.EntryAt(entry.Key()).Val()) { 44 | return false 45 | } 46 | } 47 | 48 | return true 49 | } 50 | 51 | func equalKey(k1, k2 interface{}) bool { 52 | if k1, ok := k1.(Keyword); ok { 53 | return k1 == k2 54 | } 55 | return Equals(k1, k2) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/lang/ref.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "errors" 5 | "sync/atomic" 6 | ) 7 | 8 | // Ref is a reference to a value that can be updated transactionally. 9 | type Ref struct { 10 | val interface{} 11 | } 12 | 13 | func NewRef(val interface{}) *Ref { 14 | // TODO: implement for real 15 | return &Ref{ 16 | val: val, 17 | } 18 | } 19 | 20 | func (r *Ref) Deref() interface{} { 21 | return r.val 22 | } 23 | 24 | func (r *Ref) Commute(fn IFn, args ISeq) interface{} { 25 | return LockingTransaction.doCommute(r, fn, args) 26 | } 27 | 28 | type LockingTransactor struct { 29 | txCount atomic.Int64 30 | } 31 | 32 | var ( 33 | LockingTransaction = &LockingTransactor{} 34 | 35 | ErrNoTransaction = errors.New("no transaction running") 36 | ) 37 | 38 | func (lt *LockingTransactor) RunInTransaction(fn IFn) interface{} { 39 | lt.txCount.Add(1) 40 | defer lt.txCount.Add(-1) 41 | return fn.Invoke() 42 | } 43 | 44 | func (lt *LockingTransactor) doCommute(ref *Ref, fn IFn, args ISeq) interface{} { 45 | if lt.txCount.Load() <= 0 { 46 | panic(ErrNoTransaction) 47 | } 48 | // TODO: implement for real. for now, just commute. 49 | ret := fn.ApplyTo(NewCons(ref.Deref(), args)) 50 | 51 | // TODO: this is not concurrency-safe. nor is it correct for transctions. 52 | ref.val = ret 53 | return ret 54 | } 55 | -------------------------------------------------------------------------------- /scripts/replace_kw.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import re 3 | import os 4 | 5 | def replace_kw_calls(match): 6 | s = match.group(1).strip('"') 7 | prefix, suffix = "", "" 8 | 9 | if s.endswith("?"): 10 | prefix = "Is" 11 | s = s[:-1] 12 | elif s.endswith("!"): 13 | suffix = "Bang" 14 | s = s[:-1] 15 | 16 | s = re.sub(r'[-/]', ' ', s).title().replace(' ', '') 17 | return f"KW{prefix}{s}{suffix}" 18 | 19 | def main(): 20 | if len(sys.argv) < 2: 21 | print("Usage: python replace_kw.py ") 22 | sys.exit(1) 23 | 24 | input_file = sys.argv[1] 25 | output_file = os.path.splitext(input_file)[0] + "_replaced.go" 26 | 27 | kw_pattern = re.compile(r'kw\(\s*"([^"]*)"\s*\)') 28 | 29 | try: 30 | with open(input_file, 'r') as infile, open(output_file, 'w') as outfile: 31 | for line in infile: 32 | replaced_line = kw_pattern.sub(replace_kw_calls, line) 33 | outfile.write(replaced_line) 34 | except FileNotFoundError: 35 | print(f"File '{input_file}' not found.") 36 | except Exception as e: 37 | print(f"Error occurred: {e}") 38 | else: 39 | print(f"Replacements made successfully. Output saved to '{output_file}'") 40 | 41 | if __name__ == "__main__": 42 | main() 43 | -------------------------------------------------------------------------------- /pkg/lang/equals_test.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestEquiv(t *testing.T) { 9 | equivs := [][]any{ 10 | {nil, nil}, 11 | {true, true}, 12 | {false, false}, 13 | {1, 1}, 14 | {1.0, 1.0}, 15 | {"a", "a"}, 16 | {NewVector(), emptyList}, 17 | {NewVector(1, 2, 3), NewList(1, 2, 3)}, 18 | {NewPersistentHashMap(), emptyMap}, 19 | {NewPersistentHashMap(1, 2, 3, 4), NewMap(1, 2, 3, 4), NewMap(3, 4, 1, 2)}, 20 | {NewMap(1, 2).Seq(), NewVector(NewList(1, 2)), NewList(NewVector(1, 2))}, 21 | // empty lazy seqs are equal 22 | {NewLazySeq(func() interface{} { return nil }), NewLazySeq(func() interface{} { return nil })}, 23 | {NewList(1, 2), NewLongRange(0, 3, 1).Next()}, 24 | } 25 | 26 | for _, els := range equivs { 27 | els := els 28 | t.Run(fmt.Sprintf("%v", els), func(t *testing.T) { 29 | t.Parallel() 30 | for i := range els { 31 | for j := range els { 32 | if !Equiv(els[i], els[j]) { 33 | t.Errorf("expected %v to equiv %v", els[i], els[j]) 34 | } 35 | 36 | hasheqI := HashEq(els[i]) 37 | hasheqJ := HashEq(els[j]) 38 | // check hashes are equal 39 | if hasheqI != hasheqJ { 40 | t.Errorf("%v != %v, expected %v to hasheq to %v", hasheqI, hasheqJ, els[i], els[j]) 41 | } 42 | } 43 | } 44 | }) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/ns_skip/load.go.out: -------------------------------------------------------------------------------- 1 | // Code generated by glojure codegen. DO NOT EDIT. 2 | 3 | package ns_DASH_skip 4 | 5 | import ( 6 | fmt "fmt" 7 | lang "github.com/glojurelang/glojure/pkg/lang" 8 | runtime "github.com/glojurelang/glojure/pkg/runtime" 9 | reflect "reflect" 10 | ) 11 | 12 | func init() { 13 | runtime.RegisterNSLoader("codegen/test/ns_skip", LoadNS) 14 | } 15 | 16 | func checkDerefVar(v *lang.Var) any { 17 | if v.IsMacro() { 18 | panic(lang.NewIllegalArgumentError(fmt.Sprintf("can't take value of macro: %v", v))) 19 | } 20 | return v.Get() 21 | } 22 | 23 | func checkArity(args []any, expected int) { 24 | if len(args) != expected { 25 | panic(lang.NewIllegalArgumentError("wrong number of arguments (" + fmt.Sprint(len(args)) + ")")) 26 | } 27 | } 28 | 29 | func checkArityGTE(args []any, min int) { 30 | if len(args) < min { 31 | panic(lang.NewIllegalArgumentError("wrong number of arguments (" + fmt.Sprint(len(args)) + ")")) 32 | } 33 | } 34 | 35 | // LoadNS initializes the namespace "codegen.test.ns-skip" 36 | func LoadNS() { 37 | sym_codegen_DOT_test_DOT_ns_DASH_skip := lang.NewSymbol("codegen.test.ns-skip") 38 | // reference fmt to avoid unused import error 39 | _ = fmt.Printf 40 | // reference reflect to avoid unused import error 41 | _ = reflect.TypeOf 42 | ns := lang.FindOrCreateNamespace(sym_codegen_DOT_test_DOT_ns_DASH_skip) 43 | _ = ns 44 | } 45 | -------------------------------------------------------------------------------- /pkg/lang/lazilypersistentvector.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import "reflect" 4 | 5 | // CreateOwningLazilyPersistentVector creates a persistent vector that 6 | // owns the items in items. items must be a slice or array. 7 | func CreateOwningLazilyPersistentVector(items any) IPersistentVector { 8 | itemsVal := reflect.ValueOf(items) 9 | if itemsVal.Kind() != reflect.Slice && itemsVal.Kind() != reflect.Array { 10 | panic(NewIllegalArgumentError("CreateOwningLazilyPersistentVector argument must be a slice or array")) 11 | } 12 | // TODO: optimize by building the tree directly within a PersistentVector. 13 | args := make([]any, itemsVal.Len()) 14 | for i := 0; i < itemsVal.Len(); i++ { 15 | args[i] = itemsVal.Index(i).Interface() 16 | } 17 | return NewVector(args...) 18 | } 19 | 20 | func CreateLazilyPersistentVector(obj any) IPersistentVector { 21 | switch obj := obj.(type) { 22 | case IReduceInit: 23 | return obj.ReduceInit(NewFnFunc(func(args ...any) any { 24 | acc, item := args[0], args[1] 25 | return acc.(IPersistentVector).Cons(item) 26 | }), emptyVector).(IPersistentVector) 27 | case ISeq: 28 | // TODO: optimize for ISeq by building the tree directly with a 29 | // PersistentVector implementaiton 30 | slc := seqToSlice(obj) 31 | return NewVector(slc...) 32 | case Seqable: 33 | return CreateLazilyPersistentVector(obj.Seq()) 34 | default: 35 | return NewVector(ToSlice(obj)...) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /pkg/lang/exception_info.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | type ExceptionInfo struct { 9 | message string 10 | data IPersistentMap 11 | cause error 12 | } 13 | 14 | var _ IExceptionInfo = (*ExceptionInfo)(nil) 15 | var _ error = (*ExceptionInfo)(nil) 16 | 17 | func NewExceptionInfo(msg string, data IPersistentMap) *ExceptionInfo { 18 | return &ExceptionInfo{ 19 | message: msg, 20 | data: data, 21 | } 22 | } 23 | 24 | func NewExceptionInfoWithCause(msg string, data IPersistentMap, cause error) *ExceptionInfo { 25 | return &ExceptionInfo{ 26 | message: msg, 27 | data: data, 28 | cause: cause, 29 | } 30 | } 31 | 32 | func (e *ExceptionInfo) Error() string { 33 | if e.cause != nil { 34 | return fmt.Sprintf("%s: %v", e.message, e.cause) 35 | } 36 | return e.message 37 | } 38 | 39 | func (e *ExceptionInfo) GetData() IPersistentMap { 40 | return e.data 41 | } 42 | 43 | func (e *ExceptionInfo) Unwrap() error { 44 | return e.cause 45 | } 46 | 47 | func (e *ExceptionInfo) Message() string { 48 | return e.message 49 | } 50 | 51 | func (e *ExceptionInfo) Cause() error { 52 | return e.cause 53 | } 54 | 55 | func (e *ExceptionInfo) Is(target error) bool { 56 | _, ok := target.(*ExceptionInfo) 57 | return ok 58 | } 59 | 60 | func GetExData(err error) IPersistentMap { 61 | var ei IExceptionInfo 62 | if errors.As(err, &ei) { 63 | return ei.GetData() 64 | } 65 | return nil 66 | } 67 | -------------------------------------------------------------------------------- /pkg/lang/catch.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "errors" 5 | "reflect" 6 | ) 7 | 8 | var ( 9 | errorType = reflect.TypeOf((*error)(nil)).Elem() 10 | ) 11 | 12 | // CatchMatches checks if a recovered panic value matches an expected catch type. 13 | // This implements the semantics of Clojure's try/catch matching. 14 | func CatchMatches(r, expect any) bool { 15 | if IsNil(expect) || IsNil(r) { 16 | return false 17 | } 18 | 19 | expectType := expect.(reflect.Type) 20 | 21 | // if expect is an error type, check if r is an instance of it 22 | if rErr, ok := r.(error); ok { 23 | if expectType.Implements(errorType) { 24 | // if expectType is a pointer type, instantiate a new value of that type 25 | // and check if rErr is an instance of it 26 | if expectType.Kind() == reflect.Ptr { 27 | if errors.As(rErr, reflect.New(expectType).Interface()) { 28 | return true 29 | } 30 | } 31 | // if expectType is an interface type, check if rErr implements it 32 | if expectType.Kind() == reflect.Interface { 33 | if errors.As(rErr, reflect.New(expectType).Interface()) { 34 | return true 35 | } 36 | } 37 | // otherwise, create a new value of the expectType and check if 38 | // rErr is an instance of it 39 | expectVal := reflect.New(expectType).Interface() 40 | if errors.As(rErr, expectVal) { 41 | return true 42 | } 43 | } 44 | } 45 | 46 | return reflect.TypeOf(r).AssignableTo(expectType) 47 | } 48 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/try_advanced.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.try-advanced) 2 | 3 | (defn try-custom-value [] 4 | (try 5 | (throw "custom error") 6 | (catch go/any e 7 | e))) 8 | 9 | (defn catch-binding-scope [] 10 | (let [e "outer"] 11 | (try 12 | (throw "test") 13 | (catch go/any e 14 | e)))) ; should be the exception, not "outer" 15 | 16 | (defn catch-binding-scope-2 [] 17 | (let [e "outer"] 18 | (try 19 | (throw "test") 20 | (catch go/any e 21 | e)) 22 | e)) ; should be "outer", not the exception 23 | 24 | (defn finally-with-return [] 25 | (try 26 | 42 27 | (finally 28 | 100))) ; should return 42, not 100 29 | 30 | (defn ^{:expected-output "advanced tests passed"} -main [] 31 | ;; Test that custom values can be thrown and caught 32 | (if (= (try-custom-value) "custom error") 33 | nil 34 | (throw "try-custom-value failed")) 35 | 36 | ;; Test that catch binding shadows outer scope 37 | (if (= (catch-binding-scope) "test") 38 | nil 39 | (throw "catch-binding-scope failed")) 40 | 41 | ;; Test that catch binding does not affect outer scope 42 | (if (= (catch-binding-scope-2) "outer") 43 | nil 44 | (throw "catch-binding-scope-2 failed")) 45 | 46 | ;; Test that finally doesn't affect return value 47 | (if (= (finally-with-return) 42) 48 | nil 49 | (throw "finally-with-return failed")) 50 | 51 | "advanced tests passed") 52 | -------------------------------------------------------------------------------- /test/glojure/test_glojure/parallel.glj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | ; Author: Frantisek Sodomka 10 | 11 | 12 | (ns glojure.test-glojure.parallel 13 | (:use clojure.test)) 14 | 15 | ;; !! Tests for the parallel library will be in a separate file clojure_parallel.clj !! 16 | 17 | ; future 18 | ; pmap 19 | ; pcalls 20 | ; pvalues 21 | 22 | 23 | ;; pmap 24 | ;; 25 | (deftest pmap-does-its-thing 26 | ;; regression fixed in r1218; was OutOfMemoryError 27 | (is (= '(1) (pmap inc [0])))) 28 | 29 | ;; future-call 30 | ;; 31 | (deftest future-call-timeout 32 | (is (= 42 (deref (future-call #(time.Sleep time.Second)) 1 42)))) 33 | 34 | (def ^:dynamic *test-value* 1) 35 | 36 | (deftest future-fn-properly-retains-conveyed-bindings 37 | (let [a (atom [])] 38 | (binding [*test-value* 2] 39 | @(future (dotimes [_ 3] 40 | ;; we need some binding to trigger binding pop 41 | (binding [*print-dup* false] 42 | (swap! a conj *test-value*)))) 43 | (is (= [2 2 2] @a))))) 44 | 45 | 46 | (run-tests) 47 | -------------------------------------------------------------------------------- /scripts/rewrite-core/update-clojure-sources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Script to update Clojure source files from a specific GitHub tag 4 | # Usage: ./update-clojure-sources.sh 5 | # Example: ./update-clojure-sources.sh clojure-1.12.1 6 | 7 | set -euo pipefail 8 | 9 | # Check if tag argument is provided 10 | if [ $# -eq 0 ]; then 11 | echo "Error: Please provide a Clojure tag as an argument" 12 | echo "Usage: $0 " 13 | echo "Example: $0 clojure-1.12.1" 14 | exit 1 15 | fi 16 | 17 | TAG=$1 18 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 19 | ORIGINALS_DIR="${SCRIPT_DIR}/originals" 20 | BASE_URL="https://raw.githubusercontent.com/clojure/clojure/${TAG}/src/clj/clojure" 21 | 22 | # List of files to download (based on current files in originals/) 23 | FILES=( 24 | "core.clj" 25 | "core_print.clj" 26 | "template.clj" 27 | "test.clj" 28 | "uuid.clj" 29 | "walk.clj" 30 | ) 31 | 32 | echo "Updating Clojure source files from tag: ${TAG}" 33 | echo "Destination: ${ORIGINALS_DIR}" 34 | echo 35 | 36 | # Ensure originals directory exists 37 | mkdir -p "${ORIGINALS_DIR}" 38 | 39 | # Download each file 40 | for file in "${FILES[@]}"; do 41 | url="${BASE_URL}/${file}" 42 | dest="${ORIGINALS_DIR}/${file}" 43 | 44 | echo -n "Downloading ${file}... " 45 | 46 | if curl -fsSL "${url}" -o "${dest}"; then 47 | echo "✓" 48 | else 49 | echo "✗" 50 | echo "Error: Failed to download ${file} from ${url}" 51 | exit 1 52 | fi 53 | done 54 | 55 | echo 56 | echo "Successfully updated ${#FILES[@]} files from Clojure ${TAG}" -------------------------------------------------------------------------------- /pkg/lang/iteration.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | // Nther is an interface for compound values whose elements can be 9 | // accessed by index. 10 | type Nther interface { 11 | Nth(int) (v interface{}, ok bool) 12 | } 13 | 14 | // MustNth returns the nth element of the vector. It panics if the 15 | // index is out of range. 16 | func MustNth(x interface{}, i int) interface{} { 17 | v, ok := Nth(x, i) 18 | if !ok { 19 | panic("index out of range") 20 | } 21 | return v 22 | } 23 | 24 | func Nth(x interface{}, n int) (interface{}, bool) { 25 | switch x := x.(type) { 26 | // Deprecate this 27 | case Nther: 28 | return x.Nth(n) 29 | case Indexed: 30 | val := x.NthDefault(n, notFound) 31 | if val == notFound { 32 | return nil, false 33 | } 34 | return val, true 35 | case ISeq: 36 | x = Seq(x) 37 | for i := 0; i <= n; i++ { 38 | if x == nil { 39 | return nil, false 40 | } 41 | if i == n { 42 | return x.First(), true 43 | } 44 | x = x.Next() 45 | } 46 | case string: 47 | if n < 0 || n >= len(x) { 48 | return nil, false 49 | } 50 | return NewChar([]rune(x)[n]), true 51 | } 52 | 53 | if seq := Seq(x); seq != nil { 54 | if seq == x { 55 | panic(fmt.Errorf("unexpected Seq result equal to input")) 56 | } 57 | return Nth(seq, n) 58 | } 59 | 60 | reflectVal := reflect.ValueOf(x) 61 | switch reflectVal.Kind() { 62 | case reflect.Array, reflect.Slice: 63 | if n < 0 || n >= reflectVal.Len() { 64 | return nil, false 65 | } 66 | return reflectVal.Index(n).Interface(), true 67 | } 68 | 69 | return nil, false 70 | } 71 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/eval/interop00.glj: -------------------------------------------------------------------------------- 1 | (print (fmt.Sprintf "Hello, %s!" "world")) 2 | (print (fmt.Sprintf "Hello, %s!" ["world"])) 3 | (print (fmt.Sprintf "Hello, %s! This should say \"nil\": %v" "world" "nil")) 4 | (print (fmt.Sprintf 5 | "A bunch of types: %v %v %v %v" 6 | 42 '(42 [1 2 3]) [42] "foobar")) 7 | 8 | (print (strings.HasPrefix "foobar" "foo")) 9 | (print (strings.HasPrefix "what" "no")) 10 | 11 | (let [s (strings.Split "foo,bar,baz" ",")] 12 | (print (strings.Join s ", "))) 13 | 14 | ;; dot notation 15 | 16 | ;;; Method call forms 17 | (let [replacer (strings.NewReplacer "foo" "bar" "baz" "quux")] 18 | ;; (. instance-expr (method-symbol args*)) 19 | (print (. replacer (Replace "foo baz"))) 20 | ;; (. instance-expr method-symbol args*) 21 | (print (. replacer Replace "foo baz"))) 22 | 23 | ;;; new and Field access forms 24 | (let [http-server (new net/http.Server)] 25 | ;; (. instance-expr member-symbol) 26 | (print (count (. http-server Addr)))) 27 | 28 | ;;; new with keyword args 29 | (let [http-server (new net/http.Server :Addr "localhost:8080" :Handler nil)] 30 | (print (. http-server Addr))) 31 | 32 | ;;; set struct fields 33 | (let [http-server (new net/http.Server)] 34 | (set! (. http-server Addr) ":8080") 35 | (print (. http-server Addr))) 36 | 37 | ;; (defn handler-func [w r] 38 | ;; (. w (Write nil))) 39 | ;; (let [http-server (new go/net/http.Server :Addr ":8080" :Handler handler-func)] 40 | ;; (. http-server (ListenAndServe))) 41 | 42 | 43 | ;;; TODO: support for creating non-pointers 44 | 45 | ;;; consider: syntactic sugar for new (trailing dot) 46 | 47 | -------------------------------------------------------------------------------- /test/glojure/test_glojure/builtins.glj: -------------------------------------------------------------------------------- 1 | (ns glojure.test-glojure.builtins 2 | (:use clojure.test)) 3 | 4 | (deftest append-slice 5 | (are [exp init vals] (= exp 6 | (go/len 7 | (apply go/append 8 | (go/make (go/slice-of go/int) init) 9 | (map go/int vals)))) 10 | 0 0 [] 11 | 1 0 [1] 12 | 2 1 [1])) 13 | 14 | (deftest ptr-deref 15 | (are [zero typ] (= zero (go/deref (new typ))) 16 | 0 go/int 17 | 0.0 go/float32 18 | "" go/string 19 | false go/bool)) 20 | 21 | (let [ch (go/make (go/chan-of go/int64)) 22 | num 100] 23 | (go/go (#(do (doseq [n (range num)] 24 | (go/send ch n)) 25 | (go/close ch)))) 26 | (deftest channels 27 | (is (= (reduce + (range num)) 28 | (loop [sum 0] 29 | (if-let [n (go/recv ch)] 30 | (recur (+ sum n)) 31 | sum)))))) 32 | 33 | (deftest channels 34 | (let [ch (go/make (go/chan-of go/int64)) 35 | num 100] 36 | (go/go (#(do (doseq [n (range num)] 37 | (go/send ch n)) 38 | (go/close ch)))) 39 | (is (= (reduce + (range num)) 40 | (loop [sum 0] 41 | (let [[n ok] (go/recv ch)] 42 | (if ok 43 | (recur (+ sum n)) 44 | sum))))))) 45 | 46 | (deftest maps 47 | (let [mp (go/make (go/map-of go/string go/any))] 48 | (go/set-map-index mp "foo" 42) 49 | (is (= 42 (go/map-index mp "foo"))) 50 | (is (= ["foo" 42] (first (seq mp)))))) 51 | 52 | (run-tests) 53 | -------------------------------------------------------------------------------- /internal/deps/get.go: -------------------------------------------------------------------------------- 1 | package deps 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "strings" 8 | 9 | "github.com/glojurelang/glojure/internal/genpkg" 10 | ) 11 | 12 | // Get gets the dependencies for the given package and generates a 13 | // package map for any go dependencies. 14 | func (d *Deps) Get() error { 15 | packages := make([]string, 0, len(d.Deps)) 16 | 17 | for dep, depMap := range d.Deps { 18 | version, ok := depMap["version"] 19 | if !ok { 20 | version = "latest" 21 | } 22 | 23 | if err := getDep(dep, version); err != nil { 24 | return err 25 | } 26 | 27 | packages = append(packages, dep) 28 | } 29 | 30 | if err := os.MkdirAll("./glj/gljimports", 0755); err != nil { 31 | return err 32 | } 33 | 34 | f, err := os.Create("./glj/gljimports/gljimports.go") 35 | if err != nil { 36 | return err 37 | } 38 | 39 | genpkg.GenPkgs(packages, genpkg.WithWriter(f)) 40 | 41 | return nil 42 | } 43 | 44 | func getDep(dep, version string) error { 45 | out, err := exec.Command("go", "list", "-f", "{{.Module}}", dep).Output() 46 | if err == nil { 47 | // do we already have the right version? 48 | parts := strings.Split(string(out), " ") 49 | if len(parts) == 2 { 50 | curVersion := strings.TrimSpace(parts[1]) 51 | if curVersion == version { 52 | // already installed with desired version 53 | return nil 54 | } 55 | } 56 | } 57 | 58 | // go get @ 59 | cmd := exec.Command("go", "get", dep+"@"+version) 60 | if out, err := cmd.CombinedOutput(); err != nil { 61 | return fmt.Errorf("failed to `go get %s@%s`: %w\n%s", dep, version, err, out) 62 | } 63 | return nil 64 | } 65 | -------------------------------------------------------------------------------- /pkg/lang/cons.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | type Cons struct { 4 | meta IPersistentMap 5 | hash, hasheq uint32 6 | 7 | first any 8 | more ISeq 9 | } 10 | 11 | var ( 12 | _ ASeq = (*Cons)(nil) 13 | ) 14 | 15 | func NewCons(x any, xs any) ISeq { 16 | switch xs := xs.(type) { 17 | case nil: 18 | return NewList(x) 19 | case ISeq: 20 | return &Cons{first: x, more: xs} 21 | default: 22 | return NewCons(x, Seq(xs)) 23 | } 24 | } 25 | 26 | func (c *Cons) xxx_sequential() {} 27 | 28 | func (c *Cons) Seq() ISeq { 29 | return c 30 | } 31 | 32 | func (c *Cons) First() any { 33 | return c.first 34 | } 35 | 36 | func (c *Cons) Next() ISeq { 37 | return c.More().Seq() 38 | } 39 | 40 | func (c *Cons) More() ISeq { 41 | if c.more == nil { 42 | return emptyList 43 | } 44 | return c.more 45 | } 46 | 47 | func (c *Cons) Meta() IPersistentMap { 48 | return c.meta 49 | } 50 | 51 | func (c *Cons) WithMeta(meta IPersistentMap) any { 52 | if meta == c.meta { 53 | return c 54 | } 55 | 56 | return &Cons{first: c.first, more: c.more, meta: meta} 57 | } 58 | 59 | func (c *Cons) Cons(o any) Conser { 60 | return aseqCons(c, o) 61 | } 62 | 63 | func (c *Cons) Count() int { 64 | return 1 + Count(c.more) 65 | } 66 | 67 | func (c *Cons) Empty() IPersistentCollection { 68 | return aseqEmpty() 69 | } 70 | 71 | func (c *Cons) Equals(o any) bool { 72 | return aseqEquals(c, o) 73 | } 74 | 75 | func (c *Cons) Equiv(o any) bool { 76 | return aseqEquiv(c, o) 77 | } 78 | 79 | func (c *Cons) Hash() uint32 { 80 | return aseqHash(&c.hash, c) 81 | } 82 | 83 | func (c *Cons) HashEq() uint32 { 84 | return aseqHashEq(&c.hasheq, c) 85 | } 86 | 87 | func (c *Cons) String() string { 88 | return aseqString(c) 89 | } 90 | -------------------------------------------------------------------------------- /.github/workflows/static.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy static content to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["main"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: "pages" 22 | cancel-in-progress: false 23 | 24 | jobs: 25 | # Single deploy job since we're just deploying 26 | deploy: 27 | environment: 28 | name: github-pages 29 | url: ${{ steps.deployment.outputs.page_url }} 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v4 34 | - name: Setup Pages 35 | uses: actions/configure-pages@v4 36 | - uses: cachix/install-nix-action@v31 37 | with: 38 | nix_path: nixpkgs=channel:nixos-unstable 39 | - name: Build WASM 40 | run: nix-shell --run 'PATH=$(go env GOPATH)/bin:$PATH make bin/js_wasm/glj.wasm && cp bin/js_wasm/glj.wasm doc/repl/' 41 | - name: Upload artifact 42 | uses: actions/upload-pages-artifact@v3 43 | with: 44 | # Upload entire repository 45 | path: './doc/' 46 | - name: Deploy to GitHub Pages 47 | id: deployment 48 | uses: actions/deploy-pages@v4 49 | -------------------------------------------------------------------------------- /internal/murmur3/murmur3.go: -------------------------------------------------------------------------------- 1 | package murmur3 2 | 3 | import ( 4 | "math/bits" 5 | 6 | "github.com/glojurelang/glojure/internal/seq" 7 | ) 8 | 9 | const ( 10 | seed = 0 11 | c1 = 0xcc9e2d51 12 | c2 = 0x1b873593 13 | ) 14 | 15 | func HashInt(input int32) uint32 { 16 | if input == 0 { 17 | return 0 18 | } 19 | k1 := mixK1(uint32(input)) 20 | h1 := mixH1(seed, k1) 21 | 22 | return fmix(h1, 4) 23 | } 24 | 25 | func HashLong(input int64) uint32 { 26 | if input == 0 { 27 | return 0 28 | } 29 | low := uint32(input) 30 | high := uint32((input >> 32) & 0xffffffff) 31 | 32 | k1 := mixK1(low) 33 | h1 := mixH1(seed, k1) 34 | 35 | k1 = mixK1(high) 36 | h1 = mixH1(h1, k1) 37 | 38 | return fmix(h1, 8) 39 | } 40 | 41 | func HashOrdered(xs seq.Seq, elHash func(any) uint32) uint32 { 42 | var n uint32 43 | var hash uint32 = 1 44 | for ; xs != nil; xs = xs.Next() { 45 | eh := elHash(xs.First()) 46 | hash = 31*hash + eh 47 | n++ 48 | } 49 | return MixCollHash(hash, n) 50 | } 51 | 52 | func HashUnordered(xs seq.Seq, elHash func(any) uint32) uint32 { 53 | var n uint32 54 | var hash uint32 55 | for ; xs != nil; xs = xs.Next() { 56 | eh := elHash(xs.First()) 57 | hash += eh 58 | n++ 59 | } 60 | return MixCollHash(hash, n) 61 | } 62 | 63 | func MixCollHash(hash, count uint32) uint32 { 64 | h1 := uint32(seed) 65 | k1 := mixK1(hash) 66 | h1 = mixH1(h1, k1) 67 | return fmix(h1, count) 68 | } 69 | 70 | func mixK1(k1 uint32) uint32 { 71 | k1 *= c1 72 | k1 = bits.RotateLeft32(k1, 15) 73 | k1 *= c2 74 | return k1 75 | } 76 | 77 | func mixH1(h1, k1 uint32) uint32 { 78 | h1 ^= k1 79 | h1 = bits.RotateLeft32(h1, 13) 80 | h1 = h1*5 + 0xe6546b64 81 | return h1 82 | } 83 | 84 | func fmix(h1, length uint32) uint32 { 85 | h1 ^= length 86 | h1 ^= h1 >> 16 87 | h1 *= 0x85ebca6b 88 | h1 ^= h1 >> 13 89 | h1 *= 0xc2b2ae35 90 | h1 ^= h1 >> 16 91 | return h1 92 | } 93 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/try_basic.glj: -------------------------------------------------------------------------------- 1 | (ns codegen.test.try-basic) 2 | 3 | (defn basic-try [] 4 | (try 5 | "success" 6 | (catch go/any e 7 | "caught"))) 8 | 9 | (defn try-with-throw [] 10 | (try 11 | (throw "test error") 12 | "not reached" 13 | (catch go/any e 14 | "caught exception"))) 15 | 16 | (defn try-no-catch [] 17 | (try 18 | "just body")) 19 | 20 | (defn try-finally-only [] 21 | (try 22 | "body" 23 | (finally 24 | nil))) ; finally doesn't affect return value 25 | 26 | (defn try-catch-finally [] 27 | (try 28 | (throw "error") 29 | (catch go/any e 30 | "caught") 31 | (finally 32 | nil))) 33 | 34 | (defn nested-try [] 35 | (try 36 | (try 37 | (throw "inner") 38 | (catch go/any e 39 | "inner caught")) 40 | (catch go/any e 41 | "outer caught"))) 42 | 43 | (defn ^{:expected-output "all tests passed"} -main [] 44 | ;; Test that basic-try returns success (no exception thrown) 45 | (if (= (basic-try) "success") 46 | nil 47 | (throw "basic-try failed")) 48 | 49 | ;; Test that try-with-throw catches the exception 50 | (if (= (try-with-throw) "caught exception") 51 | nil 52 | (throw "try-with-throw failed")) 53 | 54 | ;; Test that try-no-catch returns the body 55 | (if (= (try-no-catch) "just body") 56 | nil 57 | (throw "try-no-catch failed")) 58 | 59 | ;; Test that try-finally-only returns body (not finally result) 60 | (if (= (try-finally-only) "body") 61 | nil 62 | (throw "try-finally-only failed")) 63 | 64 | ;; Test that try-catch-finally catches the exception 65 | (if (= (try-catch-finally) "caught") 66 | nil 67 | (throw "try-catch-finally failed")) 68 | 69 | ;; Test that nested-try catches in inner block 70 | (if (= (nested-try) "inner caught") 71 | nil 72 | (throw "nested-try failed")) 73 | 74 | "all tests passed") 75 | -------------------------------------------------------------------------------- /test/glojure/test_glojure/core/async/basic.glj: -------------------------------------------------------------------------------- 1 | (ns glojure.test-glojure.core.async.basic 2 | (:use clojure.test) 3 | (:require [clojure.core.async :as a])) 4 | 5 | (defn- chan-with 6 | [val] 7 | (let [c (a/chan 1)] 8 | (a/>! c val) 9 | c)) 10 | 11 | (defn- closed-chan 12 | [] 13 | (let [c (a/chan)] 14 | (a/close! c) 15 | c)) 16 | 17 | (deftest simple-go 18 | (is (= 42 (a/! c 42) 32 | [v ch] (a/alts! [c])] 33 | (is (= 42 v)) 34 | (is (= c ch))) 35 | (let [rc (a/chan) 36 | wc (a/chan 1) 37 | [v ch] (a/alts! [rc [wc 42]])] 38 | (is (= true v)) 39 | (is (= wc ch))) 40 | (let [rc (a/chan) 41 | [v ch] (a/alts! [rc] :default 42)])) 42 | 43 | (deftest alt! 44 | (let [wc (a/chan 1) 45 | [rc1 rc2 rc3] (repeatedly 3 #(a/chan)) 46 | val 42 47 | res (a/alt! 48 | [rc1 rc2] ([v ch] [:read v ch]) 49 | rc3 ([v] v) 50 | [[wc val]] :wrote 51 | :default :none-ready)] 52 | (is (= :wrote res))) 53 | (let [ch (a/chan 2) 54 | _ (a/>! ch :init) 55 | val 42 56 | err (try 57 | (a/alt! 58 | ch :read 59 | [[ch val]] :wrote) 60 | nil 61 | (catch go/any x 62 | x))] 63 | (is (not (nil? err)) "duplicate ports should throw"))) 64 | 65 | (run-tests) 66 | -------------------------------------------------------------------------------- /internal/deps/embed.go: -------------------------------------------------------------------------------- 1 | package deps 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "go/format" 7 | "os" 8 | "path/filepath" 9 | "sort" 10 | "strings" 11 | ) 12 | 13 | // Embed generates an gljembed.go file that embeds the directories 14 | // from gljdeps.edn's pkgs and adds them to the Glojure load path when 15 | // imported. 16 | func (d *Deps) Embed() error { 17 | sort.Strings(d.Pkgs) 18 | 19 | // if goModRootDir (an absolute path) is not the current directory, 20 | // return an error. 21 | currentDir, err := os.Getwd() 22 | if err != nil { 23 | return err 24 | } 25 | if d.goModDir != currentDir { 26 | return fmt.Errorf("gljdeps.edn must be in the root directory of the go module") 27 | } 28 | 29 | f, err := os.Create(filepath.Join(d.goModDir, "gljembed.go")) 30 | if err != nil { 31 | return err 32 | } 33 | defer f.Close() 34 | 35 | b := bytes.NewBuffer(nil) 36 | 37 | fmt.Fprintf(b, "// Code generated by glj. DO NOT EDIT.\n\n") 38 | fmt.Fprintf(b, "package %s\n\n", filepath.Base(d.goModName)) 39 | 40 | fmt.Fprintf(b, `import ( 41 | "embed" 42 | "io/fs" 43 | 44 | "github.com/glojurelang/glojure/pkg/runtime" 45 | ) 46 | 47 | `) 48 | 49 | fmt.Fprintf(b, "var (\n") 50 | for _, pkg := range d.Pkgs { 51 | fsName := mungePath(pkg) + "FS" 52 | fmt.Fprintf(b, "\t//go:embed %s/*\n", pkg) 53 | fmt.Fprintf(b, "\t%s embed.FS\n", fsName) 54 | } 55 | fmt.Fprintf(b, ")\n\n") 56 | 57 | fmt.Fprintf(b, `func subfs(efs embed.FS, dir string) fs.FS { 58 | d, err := fs.Sub(efs, dir) 59 | if err != nil { 60 | panic(err) 61 | } 62 | return d 63 | } 64 | 65 | `) 66 | 67 | fmt.Fprintf(b, "func init() {\n") 68 | for _, pkg := range d.Pkgs { 69 | fsName := mungePath(pkg) + "FS" 70 | fmt.Fprintf(b, "\truntime.AddLoadPath(subfs(%s, %q))\n", fsName, pkg) 71 | } 72 | fmt.Fprintf(b, "}\n") 73 | 74 | src, err := format.Source(b.Bytes()) 75 | if err != nil { 76 | return err 77 | } 78 | f.Write(src) 79 | 80 | return nil 81 | } 82 | 83 | func mungePath(path string) string { 84 | return strings.Replace(path, "/", "__", -1) 85 | } 86 | -------------------------------------------------------------------------------- /pkg/runtime/readeval.go: -------------------------------------------------------------------------------- 1 | package runtime 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/glojurelang/glojure/pkg/lang" 8 | "github.com/glojurelang/glojure/pkg/reader" 9 | ) 10 | 11 | type ( 12 | // ReadEvalOption is an option for ReadEval. 13 | ReadEvalOption func(*readEvalOptions) 14 | 15 | readEvalOptions struct { 16 | // env is the environment to use for evaluation. If not set, the 17 | // global environment is used. 18 | env lang.Environment 19 | // filename is the name of the file being read. 20 | filename string 21 | } 22 | ) 23 | 24 | // WithEnv sets the environment to use for evaluation. 25 | func WithEnv(env lang.Environment) ReadEvalOption { 26 | return func(o *readEvalOptions) { 27 | o.env = env 28 | } 29 | } 30 | 31 | // WithFilename sets the filename to use for evaluation. 32 | func WithFilename(filename string) ReadEvalOption { 33 | return func(o *readEvalOptions) { 34 | o.filename = filename 35 | } 36 | } 37 | 38 | // ReadEval reads and evaluates a string that may contain one or more 39 | // forms in the global environment. 40 | func ReadEval(code string, options ...ReadEvalOption) interface{} { 41 | var opts readEvalOptions 42 | for _, opt := range options { 43 | opt(&opts) 44 | } 45 | env := opts.env 46 | if env == nil { 47 | env = lang.GlobalEnv 48 | } 49 | readerOpts := []reader.Option{ 50 | reader.WithGetCurrentNS(func() *lang.Namespace { 51 | return env.CurrentNamespace() 52 | }), 53 | } 54 | if opts.filename != "" { 55 | readerOpts = append(readerOpts, reader.WithFilename(opts.filename)) 56 | } 57 | 58 | r := reader.New(strings.NewReader(string(code)), readerOpts...) 59 | 60 | var lastValue interface{} 61 | for { 62 | expr, err := r.ReadOne() 63 | if err == reader.ErrEOF { 64 | break 65 | } 66 | if err != nil { 67 | panic(fmt.Sprintf("error reading %v: %v", opts.filename, err)) 68 | } 69 | lastValue, err = env.Eval(expr) 70 | if err != nil { 71 | panic(fmt.Sprintf("error evaluating %v: %v", opts.filename, err)) 72 | } 73 | } 74 | return lastValue 75 | } 76 | -------------------------------------------------------------------------------- /pkg/lang/seq.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | "github.com/glojurelang/glojure/internal/seq" 8 | ) 9 | 10 | func First(x interface{}) interface{} { 11 | if x == nil { 12 | return nil 13 | } 14 | s := Seq(x) 15 | if s == nil { 16 | return nil 17 | } 18 | return s.First() 19 | } 20 | 21 | func Rest(x interface{}) interface{} { 22 | s := Seq(x) 23 | if s == nil { 24 | return emptyList 25 | } 26 | return s.More() 27 | } 28 | 29 | func Next(x interface{}) ISeq { 30 | if s, ok := x.(ISeq); ok { 31 | return s.Next() 32 | } 33 | 34 | s := Seq(x) 35 | if IsNil(s) { 36 | return nil 37 | } 38 | return s.Next() 39 | } 40 | 41 | func IsSeq(x interface{}) bool { 42 | _, ok := x.(ISeq) 43 | return ok 44 | } 45 | 46 | func Seq(x interface{}) ISeq { 47 | switch x := x.(type) { 48 | case *EmptyList: 49 | return nil 50 | case *LazySeq: 51 | return x.Seq() 52 | case ISeq: 53 | return x 54 | case Seqable: 55 | return x.Seq() 56 | case string: 57 | return NewStringSeq(x, 0) 58 | case nil: 59 | return nil 60 | // TODO: define an Iterable interface, and use it here. 61 | } 62 | 63 | // use the reflect package to handle slices and arrays 64 | t := reflect.TypeOf(x) 65 | switch t.Kind() { 66 | case reflect.Slice, reflect.Array: 67 | return NewSliceSeq(x) 68 | case reflect.Map: 69 | return NewGoMapSeq(x) 70 | } 71 | 72 | panic(fmt.Errorf("can't convert %T to ISeq", x)) 73 | } 74 | 75 | func seqToSlice(s ISeq) []interface{} { 76 | var res []interface{} 77 | for seq := Seq(s); seq != nil; seq = seq.Next() { 78 | res = append(res, seq.First()) 79 | } 80 | return res 81 | } 82 | 83 | type seqSeq struct { 84 | ISeq 85 | } 86 | 87 | func (s seqSeq) Next() seq.Seq { 88 | n := s.ISeq.Next() 89 | if n == nil { 90 | return nil 91 | } 92 | return seqSeq{ISeq: n} 93 | } 94 | 95 | func seqToInternalSeq(s ISeq) seq.Seq { 96 | if s == nil { 97 | return nil 98 | } 99 | return seqSeq{ISeq: s} 100 | } 101 | -------------------------------------------------------------------------------- /pkg/stdlib/clojure/template.glj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | ;;; template.clj - anonymous functions that pre-evaluate sub-expressions 10 | 11 | ;; By Stuart Sierra 12 | ;; June 23, 2009 13 | 14 | ;; CHANGE LOG 15 | ;; 16 | ;; June 23, 2009: complete rewrite, eliminated _1,_2,... argument 17 | ;; syntax 18 | ;; 19 | ;; January 20, 2009: added "template?" and checks for valid template 20 | ;; expressions. 21 | ;; 22 | ;; December 15, 2008: first version 23 | 24 | 25 | (ns ^{:doc "Macros that expand to repeated copies of a template expression." 26 | :author "Stuart Sierra"} 27 | clojure.template 28 | (:require [clojure.walk :as walk])) 29 | 30 | (defn apply-template 31 | "For use in macros. argv is an argument list, as in defn. expr is 32 | a quoted expression using the symbols in argv. values is a sequence 33 | of values to be used for the arguments. 34 | 35 | apply-template will recursively replace argument symbols in expr 36 | with their corresponding values, returning a modified expr. 37 | 38 | Example: (apply-template '[x] '(+ x x) '[2]) 39 | ;=> (+ 2 2)" 40 | [argv expr values] 41 | (assert (vector? argv)) 42 | (assert (every? symbol? argv)) 43 | (walk/postwalk-replace (zipmap argv values) expr)) 44 | 45 | (defmacro do-template 46 | "Repeatedly copies expr (in a do block) for each group of arguments 47 | in values. values are automatically partitioned by the number of 48 | arguments in argv, an argument vector as in defn. 49 | 50 | Example: (macroexpand '(do-template [x y] (+ y x) 2 4 3 5)) 51 | ;=> (do (+ 4 2) (+ 5 3))" 52 | [argv expr & values] 53 | (let [c (count argv)] 54 | `(do ~@(map (fn [a] (apply-template argv expr a)) 55 | (partition c values))))) 56 | -------------------------------------------------------------------------------- /scripts/rewrite-core/originals/template.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | ;;; template.clj - anonymous functions that pre-evaluate sub-expressions 10 | 11 | ;; By Stuart Sierra 12 | ;; June 23, 2009 13 | 14 | ;; CHANGE LOG 15 | ;; 16 | ;; June 23, 2009: complete rewrite, eliminated _1,_2,... argument 17 | ;; syntax 18 | ;; 19 | ;; January 20, 2009: added "template?" and checks for valid template 20 | ;; expressions. 21 | ;; 22 | ;; December 15, 2008: first version 23 | 24 | 25 | (ns ^{:doc "Macros that expand to repeated copies of a template expression." 26 | :author "Stuart Sierra"} 27 | clojure.template 28 | (:require [clojure.walk :as walk])) 29 | 30 | (defn apply-template 31 | "For use in macros. argv is an argument list, as in defn. expr is 32 | a quoted expression using the symbols in argv. values is a sequence 33 | of values to be used for the arguments. 34 | 35 | apply-template will recursively replace argument symbols in expr 36 | with their corresponding values, returning a modified expr. 37 | 38 | Example: (apply-template '[x] '(+ x x) '[2]) 39 | ;=> (+ 2 2)" 40 | [argv expr values] 41 | (assert (vector? argv)) 42 | (assert (every? symbol? argv)) 43 | (walk/postwalk-replace (zipmap argv values) expr)) 44 | 45 | (defmacro do-template 46 | "Repeatedly copies expr (in a do block) for each group of arguments 47 | in values. values are automatically partitioned by the number of 48 | arguments in argv, an argument vector as in defn. 49 | 50 | Example: (macroexpand '(do-template [x y] (+ y x) 2 4 3 5)) 51 | ;=> (do (+ 4 2) (+ 5 3))" 52 | [argv expr & values] 53 | (let [c (count argv)] 54 | `(do ~@(map (fn [a] (apply-template argv expr a)) 55 | (partition c values))))) 56 | -------------------------------------------------------------------------------- /test/glojure/test_glojure/cli.glj: -------------------------------------------------------------------------------- 1 | (ns glojure.test-glojure.cli 2 | (:use clojure.test) 3 | (:require [clojure.string :as str])) 4 | 5 | (defmacro #^{:private true} test-that 6 | "Provides a useful way for specifying the purpose of tests. If the first-level 7 | forms are lists that make a call to a glojure.test function, it supplies the 8 | purpose as the msg argument to those functions. Otherwise, the purpose just 9 | acts like a comment and the forms are run unchanged." 10 | [purpose & test-forms] 11 | (let [tests (map 12 | #(if (= (:ns (meta (resolve (first %)))) 13 | (the-ns 'clojure.test)) 14 | (concat % (list purpose)) 15 | %) 16 | test-forms)] 17 | `(do ~@tests))) 18 | 19 | (defn run-cli-cmd [& args] 20 | (let [bytes-to-string (fn [bytes] 21 | (if (nil? bytes) 22 | "" 23 | (apply str (map char (seq bytes))))) 24 | cmd (apply os:exec.Command args) 25 | [output err] (.CombinedOutput cmd)] 26 | [(bytes-to-string output) (bytes-to-string (and err (.Error err)))])) 27 | 28 | (def glj (first os.Args)) 29 | 30 | (deftest e-flag-test 31 | (test-that 32 | "glj -e flag works correctly" 33 | (let [[out err] (run-cli-cmd glj "-e" "(* 6 7)")] 34 | (is (= out "42\n") "Command should output 42") 35 | (is (empty? err) "Command should not return an error")))) 36 | 37 | (deftest version-flag-test 38 | (test-that 39 | "glj --version flag works correctly" 40 | (let [[out err] (run-cli-cmd glj "--version")] 41 | (is (re-matches #"glojure v\d+\.\d+\.\d+\n" out) 42 | "Command should output version") 43 | (is (empty? err) "Command should not return an error")))) 44 | 45 | (deftest help-flag-test 46 | (test-that 47 | "glj --help flag works correctly" 48 | (let [[out err] (run-cli-cmd glj "--help")] 49 | (is (empty? err) "Command should not return an error")))) 50 | 51 | (deftest short-help-flag-test 52 | (test-that 53 | "glj -h flag works correctly" 54 | (let [[out err] (run-cli-cmd glj "-h")] 55 | (is (empty? err) "Command should not return an error")))) 56 | 57 | (run-tests) 58 | -------------------------------------------------------------------------------- /pkg/lang/char.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "unicode/utf8" 7 | ) 8 | 9 | // Char is a character value. 10 | type Char rune 11 | 12 | // NewChar creates a new character value. 13 | func NewChar(value rune) Char { 14 | return Char(value) 15 | } 16 | 17 | func (c Char) Equals(v interface{}) bool { 18 | switch v := v.(type) { 19 | case Char: 20 | return rune(c) == rune(v) 21 | default: 22 | return false 23 | } 24 | } 25 | 26 | func (c Char) Hash() uint32 { 27 | return uint32(rune(c)) 28 | } 29 | 30 | // RuneFromCharLiteral returns the rune value from a character 31 | // literal. 32 | func RuneFromCharLiteral(lit string) (rune, error) { 33 | if len(lit) < 2 || lit[0] != '\\' { 34 | return 0, fmt.Errorf("too short or not a char literal: %s", lit) 35 | } 36 | 37 | char := lit[1:] 38 | 39 | // Handle special characters 40 | // \newline, \space, \tab, \formfeed, \backspace, and \return 41 | switch char { 42 | case "newline": 43 | char = "\n" 44 | case "space": 45 | char = " " 46 | case "tab": 47 | char = "\t" 48 | case "formfeed": 49 | char = "\f" 50 | case "backspace": 51 | char = "\b" 52 | case "return": 53 | char = "\r" 54 | } 55 | // Handle unicode characters 56 | if len(char) > 1 && char[0] == 'u' { 57 | unquoted, _, _, err := strconv.UnquoteChar("\\"+char, 0) 58 | if err != nil { 59 | return 0, fmt.Errorf("invalid unicode character: %s", char) 60 | } 61 | char = string(unquoted) 62 | } 63 | 64 | // if the character is more than one rune, it's invalid 65 | if utf8.RuneCountInString(char) != 1 { 66 | return 0, fmt.Errorf("unexpected rune count") 67 | } 68 | 69 | rn, _ := utf8.DecodeRuneInString(char) 70 | return rn, nil 71 | } 72 | 73 | // CharLiteralFromRune returns a character literal from a rune. 74 | func CharLiteralFromRune(rn rune) string { 75 | switch rn { 76 | case '\n': 77 | return `\newline` 78 | case ' ': 79 | return `\space` 80 | case '\t': 81 | return `\tab` 82 | case '\f': 83 | return `\formfeed` 84 | case '\b': 85 | return `\backspace` 86 | case '\r': 87 | return `\return` 88 | } 89 | 90 | return fmt.Sprintf("\\%c", rn) 91 | } 92 | 93 | func CharAt(s string, idx int) Char { 94 | return NewChar([]rune(s)[idx]) 95 | } 96 | -------------------------------------------------------------------------------- /pkg/lang/struct.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "unicode" 7 | ) 8 | 9 | // FieldOrMethod returns the field or method of the given name on the 10 | // given value or pointer to a value, and a boolean indicating whether 11 | // the field or method was found. If the given value is a pointer, it 12 | // is dereferenced. If the value or pointer target is not a struct, or 13 | // if no such field or method exists, nil and false are returned. The 14 | // first letter of the name will be capitalized if it is not 15 | // already. This is because Go exports fields and methods that start 16 | // with a capital letter. 17 | func FieldOrMethod(v interface{}, name string) (interface{}, bool) { 18 | if unicode.IsLower(rune(name[0])) { 19 | name = string(unicode.ToUpper(rune(name[0]))) + string([]rune(name)[1:]) 20 | } 21 | 22 | target := reflect.ValueOf(v) 23 | 24 | if !target.IsValid() { 25 | panic(fmt.Errorf("FieldOrMethod on nil value. field: %v", name)) 26 | } 27 | 28 | val := target.MethodByName(name) 29 | if val.IsValid() { 30 | return val.Interface(), true 31 | } 32 | 33 | // dereference the value if it's a pointer 34 | for target.Kind() == reflect.Ptr { 35 | target = target.Elem() 36 | } 37 | 38 | if target.Kind() != reflect.Struct { 39 | return nil, false 40 | } 41 | 42 | val = target.FieldByName(name) 43 | if val.IsValid() { 44 | return val.Interface(), true 45 | } 46 | 47 | return nil, false 48 | } 49 | 50 | func SetField(target interface{}, name string, val interface{}) error { 51 | targetVal := reflect.ValueOf(target) 52 | 53 | // dereference the value if it's a pointer 54 | for targetVal.Kind() == reflect.Ptr { 55 | targetVal = targetVal.Elem() 56 | } 57 | 58 | if targetVal.Kind() != reflect.Struct { 59 | return fmt.Errorf("cannot set field on non-struct") 60 | } 61 | 62 | field := targetVal.FieldByName(name) 63 | if field.IsValid() { 64 | if !field.CanSet() { 65 | return fmt.Errorf("cannot set field %s", name) 66 | } 67 | goVal := reflect.ValueOf(val) 68 | if !goVal.Type().AssignableTo(field.Type()) { 69 | return fmt.Errorf("cannot assign %s to %s", goVal.Type(), field.Type()) 70 | } 71 | field.Set(goVal) 72 | return nil 73 | } 74 | 75 | return fmt.Errorf("no such field %s", name) 76 | } 77 | -------------------------------------------------------------------------------- /pkg/lang/aseq.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/glojurelang/glojure/internal/murmur3" 7 | ) 8 | 9 | type ( 10 | ASeq interface { 11 | IObj 12 | Hasher 13 | IHashEq 14 | Equiver 15 | Equalser 16 | Counter 17 | Seqable 18 | ISeq 19 | Sequential 20 | 21 | String() string 22 | Empty() IPersistentCollection 23 | 24 | // Clojure includes Java List ops. We omit these. 25 | } 26 | ) 27 | 28 | func aseqMore(a ASeq) ISeq { 29 | s := a.Next() 30 | if s == nil { 31 | return emptyList 32 | } 33 | return s 34 | } 35 | 36 | func aseqCount(a ASeq) int { 37 | i := 1 38 | for s := a.Next(); s != nil; s, i = s.Next(), i+1 { 39 | if sc, ok := s.(Counted); ok { 40 | return i + sc.Count() 41 | } 42 | } 43 | return i 44 | } 45 | 46 | func aseqCons(a ASeq, o any) Conser { 47 | return NewCons(o, a) 48 | } 49 | 50 | func aseqEmpty() IPersistentCollection { 51 | return emptyList 52 | } 53 | 54 | func aseqEquiv(a ASeq, obj any) bool { 55 | if a == obj { 56 | return true 57 | } 58 | 59 | _, isSequential := obj.(Sequential) 60 | objV := reflect.ValueOf(obj) 61 | if !isSequential && objV.Kind() != reflect.Slice { 62 | return false 63 | } 64 | 65 | if ac, ok := a.(Counted); ok { 66 | if bc, ok := obj.(Counted); ok { 67 | if ac.Count() != bc.Count() { 68 | return false 69 | } 70 | } 71 | } 72 | 73 | ms := Seq(obj) 74 | for s := Seq(a); s != nil; s, ms = s.Next(), ms.Next() { 75 | if ms == nil || !Equiv(s.First(), ms.First()) { 76 | return false 77 | } 78 | } 79 | 80 | return ms == nil 81 | } 82 | 83 | func aseqEquals(a ASeq, obj any) bool { 84 | return aseqEquiv(a, obj) 85 | } 86 | 87 | func aseqHash(hc *uint32, a ASeq) uint32 { 88 | if *hc != 0 { 89 | return *hc 90 | } 91 | 92 | hash := uint32(1) 93 | for s := a.Seq(); s != nil; s = s.Next() { 94 | var h uint32 95 | first := s.First() 96 | if first != nil { 97 | h = Hash(first) 98 | } 99 | hash = 31*hash + h 100 | } 101 | *hc = hash 102 | return hash 103 | } 104 | 105 | func aseqHashEq(hc *uint32, a ASeq) uint32 { 106 | if *hc != 0 { 107 | return *hc 108 | } 109 | hash := murmur3.HashOrdered(seqToInternalSeq(a), HashEq) 110 | *hc = hash 111 | return hash 112 | } 113 | 114 | func aseqString(a ASeq) string { 115 | return PrintString(a) 116 | } 117 | -------------------------------------------------------------------------------- /pkg/lang/chunkedcons.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | type ( 4 | ChunkedCons struct { 5 | meta IPersistentMap 6 | hash uint32 7 | hashEq uint32 8 | 9 | chunk IChunk 10 | more ISeq 11 | } 12 | ) 13 | 14 | var ( 15 | _ IChunkedSeq = (*ChunkedCons)(nil) 16 | _ ASeq = (*ChunkedCons)(nil) 17 | ) 18 | 19 | func NewChunkedCons(chunk IChunk, more ISeq) *ChunkedCons { 20 | return &ChunkedCons{ 21 | chunk: chunk, 22 | more: more, 23 | } 24 | } 25 | 26 | func (c *ChunkedCons) ChunkedFirst() IChunk { 27 | return c.chunk 28 | } 29 | 30 | func (c *ChunkedCons) ChunkedNext() ISeq { 31 | return c.ChunkedMore().Seq() 32 | } 33 | 34 | func (c *ChunkedCons) ChunkedMore() ISeq { 35 | if c.more == nil { 36 | return emptyList 37 | } 38 | return c.more 39 | } 40 | 41 | func (c *ChunkedCons) First() any { 42 | return c.chunk.Nth(0) 43 | } 44 | 45 | func (c *ChunkedCons) Next() ISeq { 46 | if c.chunk.Count() > 1 { 47 | return NewChunkedCons(c.chunk.DropFirst(), c.more) 48 | } 49 | return c.ChunkedNext() 50 | } 51 | 52 | func (c *ChunkedCons) More() ISeq { 53 | if c.chunk.Count() > 1 { 54 | return NewChunkedCons(c.chunk.DropFirst(), c.more) 55 | } 56 | if c.more == nil { 57 | return emptyList 58 | } 59 | return c.more 60 | } 61 | 62 | func (c *ChunkedCons) xxx_sequential() {} 63 | 64 | func (c *ChunkedCons) Seq() ISeq { 65 | return c 66 | } 67 | 68 | func (c *ChunkedCons) Meta() IPersistentMap { 69 | return c.meta 70 | } 71 | 72 | func (c *ChunkedCons) WithMeta(meta IPersistentMap) any { 73 | if c.meta == meta { 74 | return c 75 | } 76 | cpy := *c 77 | cpy.meta = meta 78 | return &cpy 79 | } 80 | 81 | func (c *ChunkedCons) Count() int { 82 | return aseqCount(c) 83 | } 84 | 85 | func (c *ChunkedCons) Cons(o any) Conser { 86 | return aseqCons(c, o) 87 | } 88 | 89 | func (c *ChunkedCons) Empty() IPersistentCollection { 90 | return aseqEmpty() 91 | } 92 | 93 | func (c *ChunkedCons) Equiv(o any) bool { 94 | return aseqEquiv(c, o) 95 | } 96 | 97 | func (c *ChunkedCons) Equals(o any) bool { 98 | return aseqEquals(c, o) 99 | } 100 | 101 | func (c *ChunkedCons) Hash() uint32 { 102 | return aseqHash(&c.hash, c) 103 | } 104 | 105 | func (c *ChunkedCons) HashEq() uint32 { 106 | return aseqHashEq(&c.hashEq, c) 107 | } 108 | 109 | func (c *ChunkedCons) String() string { 110 | return aseqString(c) 111 | } 112 | -------------------------------------------------------------------------------- /pkg/lang/atom.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import "sync/atomic" 4 | 5 | type ( 6 | Atom struct { 7 | state atomic.Value 8 | watches IPersistentMap 9 | 10 | meta IPersistentMap 11 | } 12 | ) 13 | 14 | var ( 15 | _ IAtom2 = (*Atom)(nil) 16 | _ IRef = (*Atom)(nil) 17 | ) 18 | 19 | func NewAtom(val any) *Atom { 20 | a := &Atom{} 21 | a.state.Store(Box{val}) 22 | a.watches = emptyMap 23 | return a 24 | } 25 | 26 | func NewAtomWithMeta(val any, meta IPersistentMap) *Atom { 27 | a := NewAtom(val) 28 | if meta != nil { 29 | a.meta = meta 30 | } 31 | return a 32 | } 33 | 34 | func (a *Atom) Deref() interface{} { 35 | return a.state.Load().(Box).val 36 | } 37 | 38 | func (a *Atom) SetValidator(vf IFn) { panic("not implemented") } 39 | func (a *Atom) Validator() IFn { panic("not implemented") } 40 | func (a *Atom) Watches() IPersistentMap { 41 | return a.watches 42 | } 43 | 44 | func (a *Atom) AddWatch(key interface{}, fn IFn) IRef { 45 | a.watches = a.watches.Assoc(key, fn).(IPersistentMap) 46 | return a 47 | } 48 | 49 | func (a *Atom) RemoveWatch(key interface{}) { 50 | a.watches = a.watches.Without(key) 51 | } 52 | 53 | func (a *Atom) notifyWatches(oldVal, newVal interface{}) { 54 | watches := a.watches 55 | if watches == nil || watches.Count() == 0 { 56 | return 57 | } 58 | 59 | for seq := watches.Seq(); seq != nil; seq = seq.Next() { 60 | entry := seq.First().(IMapEntry) 61 | key := entry.Key() 62 | fn := entry.Val().(IFn) 63 | // Call watch function with key, ref, old-state, new-state 64 | fn.Invoke(key, a, oldVal, newVal) 65 | } 66 | } 67 | 68 | func (a *Atom) Swap(f IFn, args ISeq) interface{} { 69 | for { 70 | old := a.state.Load().(Box) 71 | nw := f.ApplyTo(NewCons(old.val, args)) 72 | if a.CompareAndSet(old.val, nw) { 73 | return nw 74 | } 75 | } 76 | } 77 | 78 | func (a *Atom) CompareAndSet(oldv, newv interface{}) bool { 79 | // TODO: validate 80 | swapped := a.state.CompareAndSwap(Box{val: oldv}, Box{val: newv}) 81 | if swapped { 82 | a.notifyWatches(oldv, newv) 83 | } 84 | return swapped 85 | } 86 | 87 | func (a *Atom) Reset(newVal interface{}) interface{} { 88 | old := a.state.Load().(Box) 89 | // TODO: validate 90 | 91 | a.state.Store(Box{newVal}) 92 | a.notifyWatches(old.val, newVal) 93 | return newVal 94 | } 95 | 96 | func (a *Atom) Meta() IPersistentMap { 97 | if a.meta == nil { 98 | return nil 99 | } 100 | return a.meta 101 | } 102 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/const_keyword/load.go.out: -------------------------------------------------------------------------------- 1 | // Code generated by glojure codegen. DO NOT EDIT. 2 | 3 | package const_DASH_keyword 4 | 5 | import ( 6 | fmt "fmt" 7 | lang "github.com/glojurelang/glojure/pkg/lang" 8 | runtime "github.com/glojurelang/glojure/pkg/runtime" 9 | reflect "reflect" 10 | ) 11 | 12 | func init() { 13 | runtime.RegisterNSLoader("codegen/test/const_keyword", LoadNS) 14 | } 15 | 16 | func checkDerefVar(v *lang.Var) any { 17 | if v.IsMacro() { 18 | panic(lang.NewIllegalArgumentError(fmt.Sprintf("can't take value of macro: %v", v))) 19 | } 20 | return v.Get() 21 | } 22 | 23 | func checkArity(args []any, expected int) { 24 | if len(args) != expected { 25 | panic(lang.NewIllegalArgumentError("wrong number of arguments (" + fmt.Sprint(len(args)) + ")")) 26 | } 27 | } 28 | 29 | func checkArityGTE(args []any, min int) { 30 | if len(args) < min { 31 | panic(lang.NewIllegalArgumentError("wrong number of arguments (" + fmt.Sprint(len(args)) + ")")) 32 | } 33 | } 34 | 35 | // LoadNS initializes the namespace "codegen.test.const-keyword" 36 | func LoadNS() { 37 | sym_codegen_DOT_test_DOT_const_DASH_keyword := lang.NewSymbol("codegen.test.const-keyword") 38 | sym_const_DASH_keyword := lang.NewSymbol("const-keyword") 39 | kw_column := lang.NewKeyword("column") 40 | kw_end_DASH_column := lang.NewKeyword("end-column") 41 | kw_end_DASH_line := lang.NewKeyword("end-line") 42 | kw_file := lang.NewKeyword("file") 43 | kw_foo := lang.NewKeyword("foo") 44 | kw_line := lang.NewKeyword("line") 45 | kw_ns := lang.NewKeyword("ns") 46 | // var codegen.test.const-keyword/const-keyword 47 | var_codegen_DOT_test_DOT_const_DASH_keyword_const_DASH_keyword := lang.InternVarName(sym_codegen_DOT_test_DOT_const_DASH_keyword, sym_const_DASH_keyword) 48 | // reference fmt to avoid unused import error 49 | _ = fmt.Printf 50 | // reference reflect to avoid unused import error 51 | _ = reflect.TypeOf 52 | ns := lang.FindOrCreateNamespace(sym_codegen_DOT_test_DOT_const_DASH_keyword) 53 | _ = ns 54 | // const-keyword 55 | { 56 | tmp0 := sym_const_DASH_keyword.WithMeta(lang.NewMap(kw_file, "codegen/test/const_keyword.glj", kw_line, int(3), kw_column, int(6), kw_end_DASH_line, int(3), kw_end_DASH_column, int(18), kw_ns, lang.FindOrCreateNamespace(sym_codegen_DOT_test_DOT_const_DASH_keyword))).(*lang.Symbol) 57 | var_codegen_DOT_test_DOT_const_DASH_keyword_const_DASH_keyword = ns.InternWithValue(tmp0, kw_foo, true) 58 | if tmp0.Meta() != nil { 59 | var_codegen_DOT_test_DOT_const_DASH_keyword_const_DASH_keyword.SetMeta(tmp0.Meta().(lang.IPersistentMap)) 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/const_string/load.go.out: -------------------------------------------------------------------------------- 1 | // Code generated by glojure codegen. DO NOT EDIT. 2 | 3 | package const_DASH_string 4 | 5 | import ( 6 | fmt "fmt" 7 | lang "github.com/glojurelang/glojure/pkg/lang" 8 | runtime "github.com/glojurelang/glojure/pkg/runtime" 9 | reflect "reflect" 10 | ) 11 | 12 | func init() { 13 | runtime.RegisterNSLoader("codegen/test/const_string", LoadNS) 14 | } 15 | 16 | func checkDerefVar(v *lang.Var) any { 17 | if v.IsMacro() { 18 | panic(lang.NewIllegalArgumentError(fmt.Sprintf("can't take value of macro: %v", v))) 19 | } 20 | return v.Get() 21 | } 22 | 23 | func checkArity(args []any, expected int) { 24 | if len(args) != expected { 25 | panic(lang.NewIllegalArgumentError("wrong number of arguments (" + fmt.Sprint(len(args)) + ")")) 26 | } 27 | } 28 | 29 | func checkArityGTE(args []any, min int) { 30 | if len(args) < min { 31 | panic(lang.NewIllegalArgumentError("wrong number of arguments (" + fmt.Sprint(len(args)) + ")")) 32 | } 33 | } 34 | 35 | // LoadNS initializes the namespace "codegen.test.const-string" 36 | func LoadNS() { 37 | sym_codegen_DOT_test_DOT_const_DASH_string := lang.NewSymbol("codegen.test.const-string") 38 | sym_const_DASH_string := lang.NewSymbol("const-string") 39 | kw_column := lang.NewKeyword("column") 40 | kw_end_DASH_column := lang.NewKeyword("end-column") 41 | kw_end_DASH_line := lang.NewKeyword("end-line") 42 | kw_file := lang.NewKeyword("file") 43 | kw_hello := lang.NewKeyword("hello") 44 | kw_line := lang.NewKeyword("line") 45 | kw_ns := lang.NewKeyword("ns") 46 | // var codegen.test.const-string/const-string 47 | var_codegen_DOT_test_DOT_const_DASH_string_const_DASH_string := lang.InternVarName(sym_codegen_DOT_test_DOT_const_DASH_string, sym_const_DASH_string) 48 | // reference fmt to avoid unused import error 49 | _ = fmt.Printf 50 | // reference reflect to avoid unused import error 51 | _ = reflect.TypeOf 52 | ns := lang.FindOrCreateNamespace(sym_codegen_DOT_test_DOT_const_DASH_string) 53 | _ = ns 54 | // const-string 55 | { 56 | tmp0 := sym_const_DASH_string.WithMeta(lang.NewMap(kw_hello, true, kw_file, "codegen/test/const_string.glj", kw_line, int(3), kw_column, int(6), kw_end_DASH_line, int(3), kw_end_DASH_column, int(25), kw_ns, lang.FindOrCreateNamespace(sym_codegen_DOT_test_DOT_const_DASH_string))).(*lang.Symbol) 57 | var_codegen_DOT_test_DOT_const_DASH_string_const_DASH_string = ns.InternWithValue(tmp0, "Hello, World!", true) 58 | if tmp0.Meta() != nil { 59 | var_codegen_DOT_test_DOT_const_DASH_string_const_DASH_string.SetMeta(tmp0.Meta().(lang.IPersistentMap)) 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/const_number/load.go.out: -------------------------------------------------------------------------------- 1 | // Code generated by glojure codegen. DO NOT EDIT. 2 | 3 | package const_DASH_number 4 | 5 | import ( 6 | fmt "fmt" 7 | lang "github.com/glojurelang/glojure/pkg/lang" 8 | runtime "github.com/glojurelang/glojure/pkg/runtime" 9 | reflect "reflect" 10 | ) 11 | 12 | func init() { 13 | runtime.RegisterNSLoader("codegen/test/const_number", LoadNS) 14 | } 15 | 16 | func checkDerefVar(v *lang.Var) any { 17 | if v.IsMacro() { 18 | panic(lang.NewIllegalArgumentError(fmt.Sprintf("can't take value of macro: %v", v))) 19 | } 20 | return v.Get() 21 | } 22 | 23 | func checkArity(args []any, expected int) { 24 | if len(args) != expected { 25 | panic(lang.NewIllegalArgumentError("wrong number of arguments (" + fmt.Sprint(len(args)) + ")")) 26 | } 27 | } 28 | 29 | func checkArityGTE(args []any, min int) { 30 | if len(args) < min { 31 | panic(lang.NewIllegalArgumentError("wrong number of arguments (" + fmt.Sprint(len(args)) + ")")) 32 | } 33 | } 34 | 35 | // LoadNS initializes the namespace "codegen.test.const-number" 36 | func LoadNS() { 37 | sym_codegen_DOT_test_DOT_const_DASH_number := lang.NewSymbol("codegen.test.const-number") 38 | sym_const_DASH_number := lang.NewSymbol("const-number") 39 | kw_column := lang.NewKeyword("column") 40 | kw_doc := lang.NewKeyword("doc") 41 | kw_end_DASH_column := lang.NewKeyword("end-column") 42 | kw_end_DASH_line := lang.NewKeyword("end-line") 43 | kw_file := lang.NewKeyword("file") 44 | kw_line := lang.NewKeyword("line") 45 | kw_ns := lang.NewKeyword("ns") 46 | // var codegen.test.const-number/const-number 47 | var_codegen_DOT_test_DOT_const_DASH_number_const_DASH_number := lang.InternVarName(sym_codegen_DOT_test_DOT_const_DASH_number, sym_const_DASH_number) 48 | // reference fmt to avoid unused import error 49 | _ = fmt.Printf 50 | // reference reflect to avoid unused import error 51 | _ = reflect.TypeOf 52 | ns := lang.FindOrCreateNamespace(sym_codegen_DOT_test_DOT_const_DASH_number) 53 | _ = ns 54 | // const-number 55 | { 56 | tmp0 := sym_const_DASH_number.WithMeta(lang.NewMap(kw_file, "codegen/test/const_number.glj", kw_line, int(3), kw_column, int(6), kw_end_DASH_line, int(3), kw_end_DASH_column, int(17), kw_doc, "A constant number.", kw_ns, lang.FindOrCreateNamespace(sym_codegen_DOT_test_DOT_const_DASH_number))).(*lang.Symbol) 57 | var_codegen_DOT_test_DOT_const_DASH_number_const_DASH_number = ns.InternWithValue(tmp0, int64(42), true) 58 | if tmp0.Meta() != nil { 59 | var_codegen_DOT_test_DOT_const_DASH_number_const_DASH_number.SetMeta(tmp0.Meta().(lang.IPersistentMap)) 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /pkg/lang/slices.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | func SliceSet(slc any, idx int, val any) { 9 | slcVal := reflect.ValueOf(slc) 10 | valVal := reflect.ValueOf(val) 11 | // coerce valVal to the element type of slcVal 12 | if valVal.Type() != slcVal.Type().Elem() { 13 | if valVal.Type().ConvertibleTo(slcVal.Type().Elem()) { 14 | valVal = valVal.Convert(slcVal.Type().Elem()) 15 | } else { 16 | panic(NewIllegalArgumentError(fmt.Sprintf("Cannot convert %T to %s", val, slcVal.Type().Elem().String()))) 17 | } 18 | } 19 | slcVal.Index(idx).Set(valVal) 20 | } 21 | 22 | func ToSlice(x any) []any { 23 | // Handle nil - Clojure returns empty array for nil 24 | if IsNil(x) { 25 | return []any{} 26 | } 27 | 28 | // Handle []any - return as-is 29 | if slice, ok := x.([]any); ok { 30 | return slice 31 | } 32 | 33 | // Handle IPersistentVector 34 | if vec, ok := x.(IPersistentVector); ok { 35 | count := vec.Count() 36 | res := make([]any, count) 37 | for i := 0; i < count; i++ { 38 | res[i] = vec.Nth(i) 39 | } 40 | return res 41 | } 42 | 43 | // Handle IPersistentMap - convert to array of MapEntry objects 44 | if m, ok := x.(IPersistentMap); ok { 45 | seq := m.Seq() 46 | res := make([]any, 0, m.Count()) 47 | for seq != nil { 48 | res = append(res, seq.First()) // Each element is a MapEntry 49 | seq = seq.Next() 50 | } 51 | return res 52 | } 53 | 54 | // Handle Set - convert to array of values 55 | if s, ok := x.(*Set); ok { 56 | seq := s.Seq() 57 | res := make([]any, 0, s.Count()) 58 | for seq != nil { 59 | res = append(res, seq.First()) 60 | seq = seq.Next() 61 | } 62 | return res 63 | } 64 | 65 | // Handle string - convert to character array 66 | if s, ok := x.(string); ok { 67 | runes := []rune(s) // Important: use runes for proper Unicode handling 68 | res := make([]any, len(runes)) 69 | for i, ch := range runes { 70 | res[i] = NewChar(ch) // Convert each rune to Char 71 | } 72 | return res 73 | } 74 | 75 | // Handle ISeq 76 | if s, ok := x.(ISeq); ok { 77 | res := make([]interface{}, 0, Count(x)) 78 | for s := Seq(s); s != nil; s = s.Next() { 79 | res = append(res, s.First()) 80 | } 81 | return res 82 | } 83 | 84 | // Handle reflection-based slice/array 85 | xVal := reflect.ValueOf(x) 86 | if xVal.Kind() == reflect.Slice || xVal.Kind() == reflect.Array { 87 | res := make([]interface{}, xVal.Len()) 88 | for i := 0; i < xVal.Len(); i++ { 89 | res[i] = xVal.Index(i).Interface() 90 | } 91 | return res 92 | } 93 | 94 | // Error with Clojure-style message 95 | panic(NewIllegalArgumentError(fmt.Sprintf("Unable to convert: %T to Object[]", x))) 96 | } 97 | -------------------------------------------------------------------------------- /pkg/lang/agent.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type ( 8 | Agent struct { 9 | meta IPersistentMap 10 | 11 | watches IPersistentMap 12 | } 13 | 14 | future struct { 15 | done chan struct{} 16 | res interface{} 17 | } 18 | ) 19 | 20 | var ( 21 | // _ ARef = (*Agent)(nil) 22 | 23 | _ IBlockingDeref = (*future)(nil) 24 | _ IDeref = (*future)(nil) 25 | _ IPending = (*future)(nil) 26 | _ Future = (*future)(nil) 27 | ) 28 | 29 | func (f *future) Deref() interface{} { 30 | <-f.done 31 | return f.res 32 | } 33 | 34 | func (f *future) DerefWithTimeout(timeoutMS int64, timeoutVal interface{}) interface{} { 35 | select { 36 | case <-f.done: 37 | return f.res 38 | case <-time.After(time.Duration(timeoutMS) * time.Millisecond): 39 | return timeoutVal 40 | } 41 | } 42 | 43 | func (f *future) Get() interface{} { 44 | return f.Deref() 45 | } 46 | 47 | func (f *future) GetWithTimeout(timeout int64, timeUnit time.Duration) interface{} { 48 | select { 49 | case <-f.done: 50 | return f.res 51 | case <-time.After(time.Duration(timeout) * time.Millisecond): 52 | panic(NewTimeoutError("future timeout")) 53 | } 54 | } 55 | 56 | func (f *future) IsRealized() bool { 57 | select { 58 | case <-f.done: 59 | return true 60 | default: 61 | return false 62 | } 63 | } 64 | 65 | //////////////////////////////////////////////////////////////////////////////// 66 | // Agent 67 | 68 | func (a *Agent) Deref() any { 69 | panic("not implemented") 70 | } 71 | 72 | func (a *Agent) Watches() IPersistentMap { 73 | return a.watches 74 | } 75 | 76 | // func (a *Agent) AddWatch(key interface{}, fn IFn) IRef { 77 | // a.watches = a.watches.Assoc(key, fn).(IPersistentMap) 78 | // return a 79 | // } 80 | 81 | func (a *Agent) RemoveWatch(key interface{}) { 82 | a.watches = a.watches.Without(key) 83 | } 84 | 85 | func (a *Agent) notifyWatches(oldVal, newVal interface{}) { 86 | watches := a.watches 87 | if watches == nil || watches.Count() == 0 { 88 | return 89 | } 90 | 91 | for seq := watches.Seq(); seq != nil; seq = seq.Next() { 92 | entry := seq.First().(IMapEntry) 93 | key := entry.Key() 94 | fn := entry.Val().(IFn) 95 | // Call watch function with key, ref, old-state, new-state 96 | fn.Invoke(key, a, oldVal, newVal) 97 | } 98 | } 99 | 100 | //////////////////////////////////////////////////////////////////////////////// 101 | 102 | func ShutdownAgents() { 103 | // TODO 104 | } 105 | 106 | func AgentSubmit(fn IFn) IBlockingDeref { 107 | fut := &future{ 108 | done: make(chan struct{}), 109 | } 110 | go func() { 111 | fut.res = fn.Invoke() 112 | close(fut.done) 113 | }() 114 | return fut 115 | } 116 | -------------------------------------------------------------------------------- /pkg/lang/stringseq.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | type ( 4 | StringSeq struct { 5 | meta IPersistentMap 6 | hash, hasheq uint32 7 | 8 | str []rune 9 | i int 10 | } 11 | ) 12 | 13 | var ( 14 | _ ASeq = (*StringSeq)(nil) 15 | _ IndexedSeq = (*StringSeq)(nil) 16 | _ IDrop = (*StringSeq)(nil) 17 | _ IReduceInit = (*StringSeq)(nil) 18 | ) 19 | 20 | func NewStringSeq(s string, i int) *StringSeq { 21 | if len(s) == 0 { 22 | return nil 23 | } 24 | runes := []rune(s) 25 | if i >= len(runes) { 26 | return nil 27 | } 28 | return &StringSeq{str: runes, i: i} 29 | } 30 | 31 | func newStringSeq(s []rune, i int) *StringSeq { 32 | if len(s) == 0 || i >= len(s) { 33 | return nil 34 | } 35 | return &StringSeq{str: s, i: i} 36 | } 37 | 38 | func (s *StringSeq) xxx_sequential() {} 39 | 40 | func (s *StringSeq) Meta() IPersistentMap { 41 | return s.meta 42 | } 43 | 44 | func (s *StringSeq) WithMeta(meta IPersistentMap) any { 45 | if meta == s.meta { 46 | return s 47 | } 48 | cpy := *s 49 | cpy.meta = meta 50 | return &cpy 51 | } 52 | 53 | func (s *StringSeq) String() string { 54 | return aseqString(s) 55 | } 56 | 57 | func (s *StringSeq) Seq() ISeq { 58 | return s 59 | } 60 | 61 | func (s *StringSeq) Cons(o any) Conser { 62 | return aseqCons(s, o) 63 | } 64 | 65 | func (s *StringSeq) First() any { 66 | return NewChar(s.str[s.i]) 67 | } 68 | 69 | func (s *StringSeq) Next() ISeq { 70 | if s.i+1 >= len(s.str) { 71 | return nil 72 | } 73 | res := newStringSeq(s.str, s.i+1) 74 | res.meta = s.meta 75 | return res 76 | } 77 | 78 | func (s *StringSeq) More() ISeq { 79 | return aseqMore(s) 80 | } 81 | 82 | func (s *StringSeq) Count() int { 83 | return len(s.str) - s.i 84 | } 85 | 86 | func (s *StringSeq) xxx_counted() {} 87 | 88 | func (s *StringSeq) Empty() IPersistentCollection { 89 | return aseqEmpty() 90 | } 91 | 92 | func (s *StringSeq) Equals(o any) bool { 93 | return aseqEquals(s, o) 94 | } 95 | 96 | func (s *StringSeq) Equiv(o any) bool { 97 | return aseqEquiv(s, o) 98 | } 99 | 100 | func (s *StringSeq) Hash() uint32 { 101 | return aseqHash(&s.hash, s) 102 | } 103 | 104 | func (s *StringSeq) HashEq() uint32 { 105 | return aseqHashEq(&s.hasheq, s) 106 | } 107 | 108 | func (s *StringSeq) Index() int { 109 | return s.i 110 | } 111 | 112 | func (s *StringSeq) Drop(n int) Sequential { 113 | ii := s.i + n 114 | if ii >= len(s.str) { 115 | return nil 116 | } 117 | return newStringSeq(s.str, ii).WithMeta(s.meta).(Sequential) 118 | } 119 | 120 | func (s *StringSeq) ReduceInit(f IFn, init any) any { 121 | acc := init 122 | for i := s.i; i < len(s.str); i++ { 123 | acc = f.Invoke(acc, NewChar(s.str[i])) 124 | if IsReduced(acc) { 125 | return acc.(IDeref).Deref() 126 | } 127 | } 128 | return acc 129 | } 130 | -------------------------------------------------------------------------------- /pkg/lang/persistenthashmap_test.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestPersistentHashMap(t *testing.T) { 13 | var m Associative = NewPersistentHashMap() 14 | m = m.Assoc(nil, 1) 15 | assert.Equal(t, 1, m.ValAt(nil)) 16 | 17 | assert.NotNil(t, m.Seq()) 18 | 19 | m = NewPersistentHashMap() 20 | for i := 0; i < 1000; i++ { 21 | m = m.Assoc(i, i) 22 | } 23 | for i := 0; i < 1000; i++ { 24 | assert.Equal(t, i, m.ValAt(i)) 25 | } 26 | } 27 | 28 | func FuzzPersistentHashMap(f *testing.F) { 29 | f.Add([]byte(`[ 30 | 42, 31 | "a", 32 | ["symbol", "foo"], 33 | ["symbol", "foo/bar"], 34 | ["symbol", "fn"] 35 | ]`)) 36 | f.Fuzz(func(t *testing.T, data []byte) { 37 | var jsVals []interface{} 38 | if err := json.Unmarshal(data, &jsVals); err != nil { 39 | t.Skip() 40 | } 41 | var vals []any 42 | for _, jsVal := range jsVals { 43 | v, err := jsonValToVal(jsVal) 44 | if err != nil { 45 | assert.Nil(t, err) 46 | } 47 | vals = append(vals, v) 48 | } 49 | 50 | var m Associative = NewPersistentHashMap() 51 | 52 | for _, val := range vals { 53 | m = m.Assoc(val, val) 54 | } 55 | 56 | // Test that all values are present. 57 | 58 | for _, val := range vals { 59 | assert.Equal(t, val, m.ValAt(val), "%v (%T) not in %v", val, val, m) 60 | } 61 | }) 62 | } 63 | 64 | func jsonValToVal(v any) (res any, err error) { 65 | defer func() { 66 | if r := recover(); r != nil { 67 | err = fmt.Errorf("panic: %v", r) 68 | } 69 | }() 70 | 71 | switch v := v.(type) { 72 | case float64: 73 | if v == float64(int64(v)) { 74 | return int64(v), nil 75 | } 76 | return v, nil 77 | case string: 78 | return v, nil 79 | case []interface{}: 80 | if len(v) == 0 { 81 | return NewList(), nil 82 | } 83 | if v[0] == "symbol" && len(v) <= 3 { 84 | strs := make([]string, len(v)-1) 85 | for i := 1; i < len(v); i++ { 86 | strs[i-1] = v[i].(string) 87 | } 88 | return NewSymbol(strings.Join(strs, "/")), nil 89 | } 90 | switch v[0] { 91 | case "list": 92 | return NewList(mapJSONVals(v[1:])...), nil 93 | case "vector": 94 | return NewVector(mapJSONVals(v[1:])...), nil 95 | case "map": 96 | if len(v)%2 != 1 { 97 | return nil, fmt.Errorf("map must have odd number of elements") 98 | } 99 | return NewMap(mapJSONVals(v[1:])...), nil 100 | } 101 | return NewList(mapJSONVals(v)...), nil 102 | } 103 | panic(fmt.Errorf("unknown type %T", v)) 104 | } 105 | 106 | func mapJSONVals(arr []any) []any { 107 | var els []any 108 | for _, el := range arr { 109 | el, err := jsonValToVal(el) 110 | if err != nil { 111 | panic(err) 112 | } 113 | els = append(els, el) 114 | } 115 | return els 116 | } 117 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/throw_simple/load.go.out: -------------------------------------------------------------------------------- 1 | // Code generated by glojure codegen. DO NOT EDIT. 2 | 3 | package throw_DASH_simple 4 | 5 | import ( 6 | fmt "fmt" 7 | lang "github.com/glojurelang/glojure/pkg/lang" 8 | runtime "github.com/glojurelang/glojure/pkg/runtime" 9 | reflect "reflect" 10 | ) 11 | 12 | func init() { 13 | runtime.RegisterNSLoader("codegen/test/throw_simple", LoadNS) 14 | } 15 | 16 | func checkDerefVar(v *lang.Var) any { 17 | if v.IsMacro() { 18 | panic(lang.NewIllegalArgumentError(fmt.Sprintf("can't take value of macro: %v", v))) 19 | } 20 | return v.Get() 21 | } 22 | 23 | func checkArity(args []any, expected int) { 24 | if len(args) != expected { 25 | panic(lang.NewIllegalArgumentError("wrong number of arguments (" + fmt.Sprint(len(args)) + ")")) 26 | } 27 | } 28 | 29 | func checkArityGTE(args []any, min int) { 30 | if len(args) < min { 31 | panic(lang.NewIllegalArgumentError("wrong number of arguments (" + fmt.Sprint(len(args)) + ")")) 32 | } 33 | } 34 | 35 | // LoadNS initializes the namespace "codegen.test.throw-simple" 36 | func LoadNS() { 37 | sym__DASH_main := lang.NewSymbol("-main") 38 | sym_codegen_DOT_test_DOT_throw_DASH_simple := lang.NewSymbol("codegen.test.throw-simple") 39 | kw_arglists := lang.NewKeyword("arglists") 40 | kw_column := lang.NewKeyword("column") 41 | kw_end_DASH_column := lang.NewKeyword("end-column") 42 | kw_end_DASH_line := lang.NewKeyword("end-line") 43 | kw_expected_DASH_throw := lang.NewKeyword("expected-throw") 44 | kw_file := lang.NewKeyword("file") 45 | kw_line := lang.NewKeyword("line") 46 | kw_ns := lang.NewKeyword("ns") 47 | kw_rettag := lang.NewKeyword("rettag") 48 | // var codegen.test.throw-simple/-main 49 | var_codegen_DOT_test_DOT_throw_DASH_simple__DASH_main := lang.InternVarName(sym_codegen_DOT_test_DOT_throw_DASH_simple, sym__DASH_main) 50 | // reference fmt to avoid unused import error 51 | _ = fmt.Printf 52 | // reference reflect to avoid unused import error 53 | _ = reflect.TypeOf 54 | ns := lang.FindOrCreateNamespace(sym_codegen_DOT_test_DOT_throw_DASH_simple) 55 | _ = ns 56 | // -main 57 | { 58 | tmp0 := sym__DASH_main.WithMeta(lang.NewMap(kw_expected_DASH_throw, "uncaught error", kw_file, "codegen/test/throw_simple.glj", kw_line, int(3), kw_column, int(7), kw_end_DASH_line, int(3), kw_end_DASH_column, int(47), kw_arglists, lang.NewList(lang.NewVector()), kw_ns, lang.FindOrCreateNamespace(sym_codegen_DOT_test_DOT_throw_DASH_simple))).(*lang.Symbol) 59 | var tmp1 lang.FnFunc 60 | tmp1 = lang.NewFnFunc(func(args ...any) any { 61 | checkArity(args, 0) 62 | panic("uncaught error") 63 | }) 64 | tmp1 = tmp1.WithMeta(lang.NewMap(kw_rettag, nil)).(lang.FnFunc) 65 | var_codegen_DOT_test_DOT_throw_DASH_simple__DASH_main = ns.InternWithValue(tmp0, tmp1, true) 66 | if tmp0.Meta() != nil { 67 | var_codegen_DOT_test_DOT_throw_DASH_simple__DASH_main.SetMeta(tmp0.Meta().(lang.IPersistentMap)) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /pkg/lang/apersistentmap.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/glojurelang/glojure/internal/murmur3" 7 | ) 8 | 9 | type ( 10 | APersistentMap interface { 11 | AFn 12 | IPersistentMap 13 | IHashEq 14 | Hasher 15 | } 16 | ) 17 | 18 | func apersistentmapString(a APersistentMap) string { 19 | return PrintString(a) 20 | } 21 | 22 | func apersistentmapAssocEx(a APersistentMap, k, v any) IPersistentMap { 23 | if a.ContainsKey(k) { 24 | panic(errors.New("key already present")) 25 | } 26 | return a.Assoc(k, v).(IPersistentMap) 27 | } 28 | 29 | func apersistentmapCons(a APersistentMap, x any) Conser { 30 | switch x := x.(type) { 31 | case IMapEntry: 32 | return a.Assoc(x.Key(), x.Val()).(Conser) 33 | case IPersistentVector: 34 | if x.Count() != 2 { 35 | panic("vector arg to map conj must be a pair") 36 | } 37 | return a.Assoc(MustNth(x, 0), MustNth(x, 1)).(Conser) 38 | } 39 | 40 | var ret Conser = a 41 | for seq := Seq(x); seq != nil; seq = seq.Next() { 42 | ret = ret.Cons(seq.First().(IMapEntry)) 43 | } 44 | return ret 45 | } 46 | 47 | func apersistentmapContainsKey(a APersistentMap, key any) bool { 48 | return a.EntryAt(key) != nil 49 | } 50 | 51 | func apersistentmapEquiv(a APersistentMap, obj any) bool { 52 | if a == obj { 53 | return true 54 | } 55 | 56 | if c, ok := obj.(Counted); ok { 57 | if a.Count() != c.Count() { 58 | return false 59 | } 60 | } 61 | assoc, ok := obj.(Associative) 62 | if !ok { 63 | return false 64 | } 65 | 66 | for s := a.Seq(); s != nil; s = s.Next() { 67 | entry := s.First().(IMapEntry) 68 | if !assoc.ContainsKey(entry.Key()) { 69 | return false 70 | } 71 | if !Equiv(entry.Val(), assoc.EntryAt(entry.Key()).Val()) { 72 | return false 73 | } 74 | } 75 | 76 | return true 77 | } 78 | 79 | func apersistentmapHash(hc *uint32, a APersistentMap) uint32 { 80 | if *hc != 0 { 81 | return *hc 82 | } 83 | // Following Clojure's APersistentMap.mapHash logic: 84 | // Sum of (key.hashCode() XOR value.hashCode()) for each entry 85 | var hash uint32 = 0 86 | for seq := a.Seq(); seq != nil; seq = seq.Next() { 87 | entry := seq.First().(IMapEntry) 88 | keyHash := Hash(entry.Key()) 89 | valHash := Hash(entry.Val()) 90 | hash += keyHash ^ valHash 91 | } 92 | *hc = hash 93 | return hash 94 | } 95 | 96 | func apersistentmapHashEq(hc *uint32, a APersistentMap) uint32 { 97 | if *hc != 0 { 98 | return *hc 99 | } 100 | hash := murmur3.HashUnordered(seqToInternalSeq(a.Seq()), HashEq) 101 | *hc = hash 102 | return hash 103 | } 104 | 105 | func apersistentmapInvoke(a APersistentMap, args ...any) any { 106 | if len(args) == 1 { 107 | return a.ValAt(args[0]) 108 | } 109 | if len(args) == 2 { 110 | return a.ValAtDefault(args[0], args[1]) 111 | } 112 | panic(NewIllegalArgumentError("map expects either 1 or 2 arguments")) 113 | } 114 | -------------------------------------------------------------------------------- /pkg/lang/sort.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | "strings" 7 | ) 8 | 9 | // SortSlice performs an in-place stable sort on the given array using the provided comparator. 10 | // This matches java.util.Arrays.sort semantics: 11 | // - Stable sort (equal elements maintain their relative order) 12 | // - In-place modification of the array 13 | // - Comparator returns -1 for less than, 0 for equal, 1 for greater than 14 | func SortSlice(slice []any, comp any) { 15 | // comp is a Clojure function that acts as a comparator 16 | compFn, ok := comp.(IFn) 17 | if !ok { 18 | panic(NewIllegalArgumentError("Comparator must be a function")) 19 | } 20 | 21 | // Use sort.SliceStable for stable sorting (maintains relative order of equal elements) 22 | sort.SliceStable(slice, func(i, j int) bool { 23 | // Call the comparator function with the two elements 24 | result := compFn.Invoke(slice[i], slice[j]) 25 | 26 | // Handle both boolean and numeric comparators 27 | // Boolean comparator: returns true if i < j 28 | // Numeric comparator: returns negative if i < j 29 | if boolResult, ok := result.(bool); ok { 30 | return boolResult 31 | } 32 | 33 | // Numeric comparator returns: 34 | // -1 if first arg is less than second 35 | // 0 if args are equal 36 | // 1 if first arg is greater than second 37 | // We return true for "less than" case 38 | resultInt, ok := AsInt(result) 39 | if !ok { 40 | panic(NewIllegalArgumentError(fmt.Sprintf("Comparator must return a boolean or number, got %T", result))) 41 | } 42 | return resultInt < 0 43 | }) 44 | } 45 | 46 | // Compare implements Clojure's compare function. 47 | // Returns a negative number, zero, or a positive number when x is logically 48 | // 'less than', 'equal to', or 'greater than' y. 49 | // Handles nil values (nil is less than everything except nil). 50 | func Compare(x, y any) int { 51 | // Identity check 52 | if x == y { 53 | return 0 54 | } 55 | 56 | // Handle nil cases 57 | if IsNil(x) { 58 | if IsNil(y) { 59 | return 0 60 | } 61 | return -1 62 | } 63 | if IsNil(y) { 64 | return 1 65 | } 66 | 67 | // Handle numbers using the Numbers.Compare method 68 | if xNum, xIsNum := AsNumber(x); xIsNum { 69 | return Numbers.Compare(xNum, y) 70 | } 71 | 72 | // Check if x implements Comparer interface 73 | if xComp, ok := x.(Comparer); ok { 74 | return xComp.Compare(y) 75 | } 76 | 77 | // Handle strings (built-in type, doesn't implement Comparer) 78 | if xStr, xOk := x.(string); xOk { 79 | if yStr, yOk := y.(string); yOk { 80 | return strings.Compare(xStr, yStr) 81 | } 82 | } 83 | 84 | // Handle characters 85 | if xChar, xOk := x.(Char); xOk { 86 | if yChar, yOk := y.(Char); yOk { 87 | if xChar < yChar { 88 | return -1 89 | } else if xChar > yChar { 90 | return 1 91 | } 92 | return 0 93 | } 94 | } 95 | 96 | // Default error - cannot compare 97 | panic(NewIllegalArgumentError(fmt.Sprintf("%T cannot be cast to Comparable", x))) 98 | } 99 | -------------------------------------------------------------------------------- /pkg/runtime/testdata/codegen/test/quote_simple/load.go.out: -------------------------------------------------------------------------------- 1 | // Code generated by glojure codegen. DO NOT EDIT. 2 | 3 | package quote_DASH_simple 4 | 5 | import ( 6 | fmt "fmt" 7 | lang "github.com/glojurelang/glojure/pkg/lang" 8 | runtime "github.com/glojurelang/glojure/pkg/runtime" 9 | reflect "reflect" 10 | ) 11 | 12 | func init() { 13 | runtime.RegisterNSLoader("codegen/test/quote_simple", LoadNS) 14 | } 15 | 16 | func checkDerefVar(v *lang.Var) any { 17 | if v.IsMacro() { 18 | panic(lang.NewIllegalArgumentError(fmt.Sprintf("can't take value of macro: %v", v))) 19 | } 20 | return v.Get() 21 | } 22 | 23 | func checkArity(args []any, expected int) { 24 | if len(args) != expected { 25 | panic(lang.NewIllegalArgumentError("wrong number of arguments (" + fmt.Sprint(len(args)) + ")")) 26 | } 27 | } 28 | 29 | func checkArityGTE(args []any, min int) { 30 | if len(args) < min { 31 | panic(lang.NewIllegalArgumentError("wrong number of arguments (" + fmt.Sprint(len(args)) + ")")) 32 | } 33 | } 34 | 35 | // LoadNS initializes the namespace "codegen.test.quote-simple" 36 | func LoadNS() { 37 | sym__DASH_main := lang.NewSymbol("-main") 38 | sym_codegen_DOT_test_DOT_quote_DASH_simple := lang.NewSymbol("codegen.test.quote-simple") 39 | kw_arglists := lang.NewKeyword("arglists") 40 | kw_column := lang.NewKeyword("column") 41 | kw_end_DASH_column := lang.NewKeyword("end-column") 42 | kw_end_DASH_line := lang.NewKeyword("end-line") 43 | kw_expected_DASH_output := lang.NewKeyword("expected-output") 44 | kw_file := lang.NewKeyword("file") 45 | kw_line := lang.NewKeyword("line") 46 | kw_ns := lang.NewKeyword("ns") 47 | kw_rettag := lang.NewKeyword("rettag") 48 | // var codegen.test.quote-simple/-main 49 | var_codegen_DOT_test_DOT_quote_DASH_simple__DASH_main := lang.InternVarName(sym_codegen_DOT_test_DOT_quote_DASH_simple, sym__DASH_main) 50 | // reference fmt to avoid unused import error 51 | _ = fmt.Printf 52 | // reference reflect to avoid unused import error 53 | _ = reflect.TypeOf 54 | ns := lang.FindOrCreateNamespace(sym_codegen_DOT_test_DOT_quote_DASH_simple) 55 | _ = ns 56 | // -main 57 | { 58 | tmp0 := sym__DASH_main.WithMeta(lang.NewMap(kw_expected_DASH_output, lang.NewList(int64(1), int64(2), int64(3)), kw_file, "codegen/test/quote_simple.glj", kw_line, int(3), kw_column, int(7), kw_end_DASH_line, int(3), kw_end_DASH_column, int(44), kw_arglists, lang.NewList(lang.NewVector()), kw_ns, lang.FindOrCreateNamespace(sym_codegen_DOT_test_DOT_quote_DASH_simple))).(*lang.Symbol) 59 | var tmp1 lang.FnFunc 60 | tmp1 = lang.NewFnFunc(func(args ...any) any { 61 | checkArity(args, 0) 62 | return lang.NewList(int64(1), int64(2), int64(3)) 63 | }) 64 | tmp1 = tmp1.WithMeta(lang.NewMap(kw_rettag, nil)).(lang.FnFunc) 65 | var_codegen_DOT_test_DOT_quote_DASH_simple__DASH_main = ns.InternWithValue(tmp0, tmp1, true) 66 | if tmp0.Meta() != nil { 67 | var_codegen_DOT_test_DOT_quote_DASH_simple__DASH_main.SetMeta(tmp0.Meta().(lang.IPersistentMap)) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /pkg/lang/sliceseq.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | type ( 9 | // SliceSeq is an implementation of ISeq for slices. 10 | SliceSeq struct { 11 | meta IPersistentMap 12 | hash, hasheq uint32 13 | 14 | v reflect.Value 15 | i int 16 | } 17 | ) 18 | 19 | var ( 20 | _ ASeq = (*SliceSeq)(nil) 21 | _ IReduce = (*SliceSeq)(nil) 22 | _ IReduceInit = (*SliceSeq)(nil) 23 | ) 24 | 25 | func NewSliceSeq(x any) ISeq { 26 | reflectVal := reflect.ValueOf(x) 27 | switch reflectVal.Kind() { 28 | case reflect.Array, reflect.Slice: 29 | if reflectVal.Len() == 0 { 30 | return nil 31 | } 32 | return &SliceSeq{v: reflectVal, i: 0} 33 | } 34 | panic(NewIllegalArgumentError(fmt.Sprintf("not a slice: %T", x))) 35 | } 36 | 37 | func (s *SliceSeq) xxx_sequential() {} 38 | 39 | func (s *SliceSeq) Meta() IPersistentMap { 40 | return s.meta 41 | } 42 | 43 | func (s *SliceSeq) WithMeta(meta IPersistentMap) any { 44 | if meta == s.meta { 45 | return s 46 | } 47 | 48 | cpy := *s 49 | cpy.meta = meta 50 | return &cpy 51 | } 52 | 53 | func (s *SliceSeq) First() any { 54 | return s.v.Index(s.i).Interface() 55 | } 56 | 57 | func (s *SliceSeq) Next() ISeq { 58 | nxt := s.i + 1 59 | if nxt >= s.v.Len() { 60 | return nil 61 | } 62 | return &SliceSeq{ 63 | v: s.v, 64 | i: nxt, 65 | } 66 | } 67 | 68 | func (s *SliceSeq) More() ISeq { 69 | return aseqMore(s) 70 | } 71 | 72 | func (s *SliceSeq) Cons(o any) Conser { 73 | return aseqCons(s, o) 74 | } 75 | 76 | func (s *SliceSeq) Count() int { 77 | return s.v.Len() - s.i 78 | } 79 | 80 | func (s *SliceSeq) Empty() IPersistentCollection { 81 | return aseqEmpty() 82 | } 83 | 84 | func (s *SliceSeq) Equals(o any) bool { 85 | return aseqEquals(s, o) 86 | } 87 | 88 | func (s *SliceSeq) Equiv(o any) bool { 89 | return aseqEquiv(s, o) 90 | } 91 | 92 | func (s *SliceSeq) Hash() uint32 { 93 | return aseqHash(&s.hash, s) 94 | } 95 | 96 | func (s *SliceSeq) HashEq() uint32 { 97 | return aseqHashEq(&s.hasheq, s) 98 | } 99 | 100 | func (s *SliceSeq) Seq() ISeq { 101 | return s 102 | } 103 | 104 | func (s *SliceSeq) String() string { 105 | return aseqString(s) 106 | } 107 | 108 | func (s *SliceSeq) Reduce(f IFn) any { 109 | if s.v.IsZero() || s.v.IsNil() { 110 | return nil 111 | } 112 | 113 | ret := s.v.Index(s.i).Interface() 114 | for x := s.i + 1; x < s.v.Len(); x++ { 115 | ret = f.Invoke(ret, s.v.Index(x).Interface()) 116 | if IsReduced(ret) { 117 | return ret.(IDeref).Deref() 118 | } 119 | } 120 | return ret 121 | } 122 | 123 | func (s *SliceSeq) ReduceInit(f IFn, start any) any { 124 | if s.v.IsZero() || s.v.IsNil() { 125 | return start 126 | } 127 | 128 | ret := f.Invoke(start, s.v.Index(s.i).Interface()) 129 | for x := s.i + 1; x < s.v.Len(); x++ { 130 | ret = f.Invoke(ret, s.v.Index(x).Interface()) 131 | if IsReduced(ret) { 132 | return ret.(IDeref).Deref() 133 | } 134 | } 135 | if IsReduced(ret) { 136 | return ret.(IDeref).Deref() 137 | } 138 | return ret 139 | } 140 | -------------------------------------------------------------------------------- /pkg/lang/equal.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | func Equiv(a, b any) bool { 8 | return Equals(a, b) 9 | } 10 | 11 | func Equals(a, b any) bool { 12 | // check functions first, because == panics on func comparison. 13 | aVal, bVal := reflect.ValueOf(a), reflect.ValueOf(b) 14 | if aVal.Kind() == reflect.Func || bVal.Kind() == reflect.Func { 15 | if !(aVal.Kind() == reflect.Func && bVal.Kind() == reflect.Func) { 16 | return false 17 | } 18 | return aVal.Pointer() == bVal.Pointer() 19 | } 20 | if aVal.Kind() == reflect.Map || bVal.Kind() == reflect.Map || aVal.Kind() == reflect.Slice || bVal.Kind() == reflect.Slice { 21 | if aVal.Kind() != bVal.Kind() { 22 | return false 23 | } 24 | return Equals(Seq(a), Seq(b)) 25 | } 26 | 27 | if a == b { 28 | return true 29 | } 30 | 31 | aNil, bNil := IsNil(a), IsNil(b) 32 | 33 | if aNil && bNil { 34 | // both nil 35 | return true 36 | } 37 | if aNil || bNil { 38 | // one nil 39 | return false 40 | } 41 | 42 | if _, ok := AsNumber(a); ok { 43 | if _, ok := AsNumber(b); !ok { 44 | return false 45 | } 46 | return NumbersEqual(a, b) 47 | } 48 | if _, ok := a.(IPersistentCollection); ok { 49 | return pcEquiv(a, b) 50 | } 51 | if _, ok := b.(IPersistentCollection); ok { 52 | return pcEquiv(a, b) 53 | } 54 | 55 | if a, ok := a.(Equalser); ok { 56 | return a.Equals(b) 57 | } 58 | if b, ok := b.(Equalser); ok { 59 | return b.Equals(a) 60 | } 61 | 62 | if a, ok := a.(Equiver); ok { 63 | return a.Equiv(b) 64 | } 65 | if b, ok := b.(Equiver); ok { 66 | return b.Equiv(a) 67 | } 68 | 69 | // TODO: match all clojure equality rules 70 | 71 | return false 72 | } 73 | 74 | func Identical(a, b any) bool { 75 | aVal, bVal := reflect.ValueOf(a), reflect.ValueOf(b) 76 | 77 | // check if comparing functions, because == panics on func comparison. 78 | if aVal.Kind() == reflect.Func || bVal.Kind() == reflect.Func { 79 | if !(aVal.Kind() == reflect.Func && bVal.Kind() == reflect.Func) { 80 | return false 81 | } 82 | return aVal.Pointer() == bVal.Pointer() 83 | } 84 | // slices 85 | if aVal.Kind() == reflect.Slice || bVal.Kind() == reflect.Slice { 86 | if !(aVal.Kind() == reflect.Slice && bVal.Kind() == reflect.Slice) { 87 | return false 88 | } 89 | return aVal.Pointer() == bVal.Pointer() 90 | } 91 | // arrays 92 | if aVal.Kind() == reflect.Array || bVal.Kind() == reflect.Array { 93 | if !(aVal.Kind() == reflect.Array && bVal.Kind() == reflect.Array) { 94 | return false 95 | } 96 | return aVal.Pointer() == bVal.Pointer() 97 | } 98 | // maps 99 | if aVal.Kind() == reflect.Map || bVal.Kind() == reflect.Map { 100 | if !(aVal.Kind() == reflect.Map && bVal.Kind() == reflect.Map) { 101 | return false 102 | } 103 | return aVal.Pointer() == bVal.Pointer() 104 | } 105 | 106 | return a == b 107 | } 108 | 109 | func pcEquiv(a, b any) bool { 110 | if a, ok := a.(IPersistentCollection); ok { 111 | return a.Equiv(b) 112 | } 113 | return b.(IPersistentCollection).Equiv(a) 114 | } 115 | -------------------------------------------------------------------------------- /pkg/lang/repeat.go: -------------------------------------------------------------------------------- 1 | package lang 2 | 3 | type Repeat struct { 4 | meta IPersistentMap 5 | hash, hasheq uint32 6 | 7 | x interface{} 8 | count int64 9 | next ISeq 10 | } 11 | 12 | var ( 13 | _ ASeq = (*Repeat)(nil) 14 | _ ISeq = (*Repeat)(nil) 15 | _ Sequential = (*Repeat)(nil) 16 | _ IReduce = (*Repeat)(nil) 17 | _ IReduceInit = (*Repeat)(nil) 18 | ) 19 | 20 | func NewRepeat(x interface{}) *Repeat { 21 | return &Repeat{x: x, count: -1} 22 | } 23 | 24 | func NewRepeatN(count int64, x interface{}) ISeq { 25 | if count <= 0 { 26 | return emptyList 27 | } 28 | return &Repeat{x: x, count: count} 29 | } 30 | 31 | func (r *Repeat) Meta() IPersistentMap { 32 | return r.meta 33 | } 34 | 35 | func (r *Repeat) WithMeta(meta IPersistentMap) any { 36 | if meta == r.meta { 37 | return r 38 | } 39 | 40 | cpy := *r 41 | cpy.meta = meta 42 | return &cpy 43 | } 44 | 45 | func (r *Repeat) xxx_sequential() {} 46 | 47 | func (r *Repeat) First() interface{} { 48 | return r.x 49 | } 50 | 51 | func (r *Repeat) More() ISeq { 52 | s := r.Next() 53 | if s == nil { 54 | return emptyList 55 | } 56 | return s 57 | } 58 | 59 | func (r *Repeat) Next() ISeq { 60 | if r.next != nil { 61 | return r.next 62 | } 63 | if r.count > 1 { 64 | r.next = NewRepeatN(r.count-1, r.x) 65 | } else if r.count == -1 { 66 | r.next = r 67 | } 68 | return r.next 69 | } 70 | 71 | func (r *Repeat) Seq() ISeq { 72 | return r 73 | } 74 | 75 | func (r *Repeat) Cons(val any) Conser { 76 | return aseqCons(r, val) 77 | } 78 | 79 | func (r *Repeat) Count() int { 80 | return aseqCount(r) 81 | } 82 | 83 | func (r *Repeat) Empty() IPersistentCollection { 84 | return aseqEmpty() 85 | } 86 | 87 | func (r *Repeat) Equals(o any) bool { 88 | return aseqEquals(r, o) 89 | } 90 | 91 | func (r *Repeat) Equiv(o any) bool { 92 | return aseqEquiv(r, o) 93 | } 94 | 95 | func (r *Repeat) Hash() uint32 { 96 | return aseqHash(&r.hash, r) 97 | } 98 | 99 | func (r *Repeat) HashEq() uint32 { 100 | return aseqHashEq(&r.hasheq, r) 101 | } 102 | 103 | func (r *Repeat) String() string { 104 | return aseqString(r) 105 | } 106 | 107 | func (r *Repeat) Reduce(f IFn) interface{} { 108 | ret := r.x 109 | if r.count == -1 { 110 | for { 111 | ret = f.Invoke(ret, r.x) 112 | if IsReduced(ret) { 113 | return ret.(IDeref).Deref() 114 | } 115 | } 116 | } else { 117 | for i := int64(1); i < r.count; i++ { 118 | ret = f.Invoke(ret, r.x) 119 | if IsReduced(ret) { 120 | return ret.(IDeref).Deref() 121 | } 122 | } 123 | return ret 124 | } 125 | } 126 | 127 | func (r *Repeat) ReduceInit(f IFn, start interface{}) interface{} { 128 | ret := start 129 | if r.count == -1 { 130 | for { 131 | ret = f.Invoke(ret, r.x) 132 | if IsReduced(ret) { 133 | return ret.(IDeref).Deref() 134 | } 135 | } 136 | } else { 137 | for i := int64(0); i < r.count; i++ { 138 | ret = f.Invoke(ret, r.x) 139 | if IsReduced(ret) { 140 | return ret.(IDeref).Deref() 141 | } 142 | } 143 | return ret 144 | } 145 | } 146 | --------------------------------------------------------------------------------