Compare commits

...

804 Commits

Author SHA1 Message Date
greg 38eeec5718 Use lib flag to get debug 2018-04-28 20:44:42 -07:00
greg 431c0fdf8c Starting to try and annotate real compiler passes 2018-04-28 15:46:11 -07:00
greg 9015cbee21 More experimentation 2018-04-28 15:35:04 -07:00
greg bf7533cdbe Some experimentation 2018-04-28 03:31:53 -07:00
greg 2c79984678 Starting codegen work 2018-04-28 00:08:16 -07:00
greg 82cfd3f03d Adding proc macro for codegen
This should hopefully make the compiler pass thing I want to do possible
2018-04-27 02:19:09 -07:00
greg 9547275355 Backtick operators supported in tokenizing 2018-04-25 03:01:41 -07:00
greg 2dae8d6629 Show artifacts on failure 2018-04-24 23:11:04 -07:00
greg 056ca1c162 TODO note 2018-04-24 20:31:17 -07:00
greg 00ace0b682 Put this stuff back
More complicated to separate out repl than I thought
2018-04-24 20:31:17 -07:00
greg 5fcf0815c0 Start refactoring how interpreter options are split up 2018-04-24 20:31:17 -07:00
greg 748250d383 Swap sigil from . to : 2018-04-24 20:31:17 -07:00
greg 87cc14356d Want to change 'trait' to 'interface' 2018-04-24 20:31:17 -07:00
greg 7004960434 Fix history adding 2018-04-24 20:31:17 -07:00
greg f5aff0b276 Trait -> Interface 2018-04-24 16:30:17 -07:00
greg 1d6f104b12 Kill old advanced_slice_patterns 2018-04-03 23:24:21 -07:00
greg c11ae3b50d Debug stages from command line 2018-03-27 00:50:31 -07:00
greg 7f21b70e3f Make REPL interpreter return a value 2018-03-25 00:11:18 -07:00
greg 0b2e2cf68b Kill unused items in schala-repl 2018-03-24 23:28:00 -07:00
greg 1129d97603 Start killing old code in language 2018-03-24 19:02:16 -07:00
greg e9c538da49 Fix interspersing of newlines in tokenizer infra 2018-03-24 18:38:28 -07:00
greg 3295242115 Show err output when evaluating non-interactively 2018-03-24 17:27:54 -07:00
greg 4bea717e72 Colored repl command output 2018-03-24 15:14:24 -07:00
greg 20f879d68d Hook schala function up to debug booleans
Not sure if I like this API, but eh, it's what I've got
2018-03-24 13:41:54 -07:00
greg 20cf877d21 Add back color 2018-03-24 13:20:10 -07:00
greg df51a1e04a rename schala_main -> repl_main 2018-03-23 19:04:32 -07:00
greg 1c7574150e Add version string 2018-03-23 18:56:09 -07:00
greg 36b3f58f77 Get rid of all top-level dependencies 2018-03-23 18:48:15 -07:00
greg f181e2f284 move schala into separate crate 2018-03-23 18:43:43 -07:00
greg 34086b3b2b Color in terminal error output 2018-03-22 03:39:42 -07:00
greg fd4f5e17df Move robo to separate crate 2018-03-21 01:46:11 -07:00
greg 8ca5a77174 Move rukka to crate 2018-03-21 01:43:43 -07:00
greg 78a250bcba Move maaru into separate crate 2018-03-20 23:29:56 -07:00
greg 31fc751799 Rename schala-lib -> schala-repl 2018-03-20 21:17:46 -07:00
greg e99479ffcc Removed (for now) LLVMCodeString 2018-03-20 21:13:34 -07:00
greg 6fcc7ded59 new thing compiles 2018-03-20 20:29:57 -07:00
greg de073a6d9b Switch over schala to new system 2018-03-19 22:57:54 -07:00
greg 638afd47cc Update schala example code 2018-03-17 23:38:37 -07:00
greg 5ab7c2d254 Nested comments 2018-03-17 22:25:43 -07:00
greg 7a606980cb Changing comments to use //, /* 2018-03-17 19:12:58 -07:00
greg ad8d2b22cd Starting to improve infrastrucutre for lang output
To make repl vs non-repl output better
2018-03-11 14:53:08 -07:00
greg 57f56168c5 Hacky fix for displaying error output non-interactively 2018-03-11 12:56:51 -07:00
greg 64a3705e35 Some changes necessary to handle non-interactive code 2018-03-09 00:50:24 -08:00
greg 7e23e40a2f Eval list literals 2018-03-08 12:42:05 -08:00
greg 4c88a7ada6 Parse list literals 2018-03-08 12:01:24 -08:00
greg 367719d408 Tighten some code 2018-03-08 00:32:19 -08:00
greg 2e80045750 Rename ReplOutput -> LanguageOutput 2018-03-07 22:07:13 -08:00
greg 35c67f73c3 Make directory for schala source files 2018-03-07 21:53:55 -08:00
greg da9aa1e29d Index evaluation 2018-03-07 21:46:21 -08:00
greg ca67f9b4fe Proper index exprs 2018-03-06 02:51:45 -08:00
greg 60cce3fe9c Some macro simplifications 2018-03-06 01:31:31 -08:00
greg 4eb22f94d0 Trying to make tests less verbose 2018-03-06 01:06:36 -08:00
greg 355c8170a4 Added test for lambda call 2018-03-06 00:56:19 -08:00
greg e3671a579d Changed BNF grammar of call statements
To allow calling lambdas
2018-03-06 00:38:33 -08:00
greg 08ca48b2ba lambdas 2018-03-04 02:11:22 -08:00
greg fea9b9575b Print output of tuples 2018-03-03 13:26:22 -08:00
greg 276dad56d7 Handle tuple literals in type system 2018-03-03 11:55:20 -08:00
greg 695e733584 Sum types in type schema 2018-03-03 11:52:07 -08:00
greg 9bfd751db6 Kill unused import 2018-03-03 11:32:38 -08:00
greg b058e47d79 Kill some compiler warnings 2018-03-03 00:28:52 -08:00
greg 5be53dc847 Evaluator now only prints when a builtin print is called 2018-03-03 00:25:08 -08:00
greg a0bea0d55a Kill comments 2018-03-02 23:33:01 -08:00
greg 9747374e8a Fix bug in delimited macro
Had to do with bad strictness testing.
2018-03-02 22:44:17 -08:00
greg 9ab1ca28f8 Improve tokenizer debug output 2018-03-02 22:11:25 -08:00
greg 66cd51a355 Cleanup 2018-03-02 21:59:14 -08:00
greg 1056be12e7 Include line count in token debug 2018-03-02 15:21:48 -08:00
greg 48e7c0be03 Munged types to make tokenizer compile 2018-03-02 15:15:12 -08:00
greg 6e82d1207e SOme work
WIP
2018-03-02 02:57:04 -08:00
greg f0e7c9906e Fixed bug w/ lines in functions
Also improved debugging
2018-03-02 00:42:52 -08:00
greg 57c7858c87 Frame-aware lookups 2018-03-01 23:13:32 -08:00
greg a105c84943 Kill debug 2018-03-01 22:54:03 -08:00
greg 2b8d63d9cc Better debugging for types 2018-03-01 22:32:38 -08:00
greg c807c20292 Use UVars in type signatures of functions 2018-03-01 03:35:09 -08:00
greg a643c8a792 Add history saving 2018-03-01 02:49:14 -08:00
greg 69200048fa Switch to rustyline library 2018-03-01 02:43:11 -08:00
greg 55e372a670 Introduced fresh type variable method 2018-02-28 05:45:20 -08:00
greg c50626241e Continuing work 2018-02-27 03:01:05 -08:00
greg 232bec97a7 Re-added symbol table infra 2018-02-26 21:43:53 -08:00
greg ce1d967f08 Some logic for function call inferring 2018-02-26 21:28:11 -08:00
greg daa0062108 Starting on function application typechecking 2018-02-26 21:00:36 -08:00
greg 3e7c7a50b4 Move some code around 2018-02-26 19:57:46 -08:00
greg 2574a1b9c0 Function calls work 2018-02-26 19:55:27 -08:00
greg c285ee182e Temporarily disable type-erroring
and tighten some code
2018-02-26 19:16:49 -08:00
greg f7659a5598 Handle variable lookups 2018-02-26 18:23:10 -08:00
greg 1064d9993a Evaluate binding declarations 2018-02-26 18:18:42 -08:00
greg 0e3320e183 Separate Value and NamedStruct syntactic categories 2018-02-26 18:12:37 -08:00
greg 89a2be19f4 Fixed | 2018-02-26 02:27:36 -08:00
greg d9e96398a4 More operator stuff 2018-02-26 02:21:21 -08:00
greg a564ffa1ce Operator changes 2018-02-26 02:11:56 -08:00
greg b3fff100d2 Fixed tests w/ respect to binop
There's a few unnecessary conversions of &str 's to Rc<String> and back
2018-02-24 17:50:57 -08:00
greg cfd6df7ba5 Centralize data for prefix ops too 2018-02-24 17:43:26 -08:00
greg bb2e1ae27a Added type information to binop definitions
Also started centralizing precedence there too
2018-02-24 17:37:23 -08:00
greg 4333563d03 Make sigil field private 2018-02-24 14:39:45 -08:00
greg e7cabb2a79 Function evaluation work 2018-02-24 14:31:04 -08:00
greg 5da7c809b2 Give State a pointer to its parent
For function call lookups
2018-02-24 13:56:04 -08:00
greg d229a57837 Finished initial BinOp/PrefixOp 2018-02-23 19:06:37 -08:00
greg 0dd8861f83 Starting to munge BinOp types
Incomplete, doesn't yet compile
2018-02-23 04:10:00 -08:00
greg 4ab900d601 ReplState -> State
Not everythign is a repl
2018-02-23 03:07:58 -08:00
greg 501b975fb6 Move bx! macro up to mod.rs
And make use of it in parser
2018-02-23 03:04:19 -08:00
greg 83315e97ac Move anno-to-type to a method on TypeName 2018-02-23 02:30:34 -08:00
greg 6259a0808c Fix tests too 2018-02-23 01:59:53 -08:00
greg 0c69476fd0 Separate tokenizing module
Parsing was getting too long
2018-02-23 01:58:06 -08:00
greg 1caccc6ae2 Some work on binops 2018-02-23 01:49:37 -08:00
greg 23af2b1455 Some more type-checking work 2018-02-22 19:59:53 -08:00
greg 61795b0331 More work on evaluating applications
for later testing + to kill a compiler warning
2018-02-22 03:34:36 -08:00
greg a1b874c891 Fix traits, silence warnings 2018-02-22 03:26:32 -08:00
greg e7103b925b type of a declaration should be Void, not Unit
I think this makes sense

Also kill some compiler warnings
2018-02-22 03:25:05 -08:00
greg c35401da65 Types in bindings 2018-02-22 03:21:58 -08:00
greg d51a9a73d7 Simplified match 2018-02-22 00:31:13 -08:00
greg fddd43b86e Added trait declaration 2018-02-21 22:06:56 -08:00
greg 12f55fa844 More static type work 2018-02-21 18:12:46 -08:00
greg bb0fb716e4 Finished basic constant type inference 2018-02-21 14:14:24 -08:00
greg 687d482853 More type implementing - WIP
This has a borrowing bug currently
2018-02-21 04:32:30 -08:00
greg 628eb28deb Fix some integer overflows with binary and hex 2018-02-21 03:52:16 -08:00
greg 4c8b4c8c71 Starting basic type stuff 2018-02-21 03:39:40 -08:00
greg c674148772 Starting over with types 2018-02-21 02:35:09 -08:00
greg 7b4f69dce5 Additional TODO 2018-02-21 02:35:09 -08:00
greg 98caf1cac3 Add todo note 2018-02-20 17:56:13 -08:00
greg 457799e0f7 More type things 2018-02-12 01:45:36 -08:00
greg 681d767855 Type singletons test work 2018-02-12 00:51:53 -08:00
greg ef4620e90a TypeSingletonName broken out 2018-02-12 00:25:48 -08:00
greg 1f2a4c706f Fix struct literals in if expressions
With special case-ing, sigh :( Also will need to do this for match
expressions but I'll cross that bridge when I come to it
2018-02-11 22:10:21 -08:00
greg a452bccd1c Don't need clone() here 2018-02-11 16:45:26 -08:00
greg eca2218f6a Kill separate is_digit method
I care about 10 vs 16 distinction
2018-02-11 16:43:51 -08:00
greg 83aedb0efb Hex parsing done 2018-02-11 16:35:38 -08:00
greg 76841de784 Save settings on ctrl-D 2018-02-11 16:28:17 -08:00
greg c0574ff1ef Added a bunch of notes 2018-02-11 02:37:52 -08:00
greg faa5c6ab42 Fix parse level calculation 2018-02-10 17:45:00 -08:00
greg 9c2d2190b0 Proper indentation of parser debug 2018-02-10 15:10:06 -08:00
greg 21511f5120 Move some code around 2018-02-08 01:15:27 -08:00
greg 8bd399f97a Better hex literals 2018-01-08 06:12:45 -08:00
greg 30a6d0929a Starting hex parsing 2018-01-08 05:57:36 -08:00
greg bec8aedc22 Simplify some code 2018-01-08 05:21:04 -08:00
greg 1b642c6321 Assign a specific rocket version 2017-12-31 15:46:08 -08:00
greg 559306ffc8 unified BoolAtom 2017-12-30 23:19:42 -08:00
greg 540ffde4bc Rukka source file 2017-12-30 01:18:48 -08:00
greg fa8d46e3d7 Print operation 2017-12-29 05:10:03 -08:00
greg 4598802999 Refactoring 2017-12-29 05:03:30 -08:00
greg 5ea83e2da6 Delete some unneeded code 2017-12-29 04:55:03 -08:00
greg 23c0f54042 Forgot to change name here 2017-12-29 04:52:47 -08:00
greg 8618de313b Name change
builtin -> primitive
2017-12-29 04:51:14 -08:00
greg cd23b23a91 Get rid of some printlns 2017-12-29 04:46:19 -08:00
greg e6475a1262 Implement lambda application 2017-12-29 03:57:27 -08:00
greg e6f81b28f9 Plus and multiply 2017-12-25 23:10:16 -08:00
greg c20d75faf1 Builtins - + 2017-12-24 23:48:13 -08:00
greg 85aabed344 Framework for multiple environments 2017-12-21 03:11:56 -08:00
greg aa821e720a Apply wokr 2017-12-21 01:14:05 -08:00
greg 0e25720927 Fixing quote 2017-12-21 01:12:05 -08:00
greg c101610cde Starting builtins 2017-12-20 22:56:24 -08:00
greg 1217f6e143 Lambda abstraction 2017-12-20 18:23:44 -08:00
greg 6f41167402 Kill this linker thing 2017-12-18 01:30:33 -08:00
greg cf0af7e0c9 Flesh out TODO, README 2017-12-13 00:52:54 -08:00
greg a7fd515e7b Add Rukka to README 2017-12-13 00:25:59 -08:00
greg c6509338d8 Kill unused code 2017-12-12 02:57:19 -08:00
greg 192a6bf6e1 Some lambda work 2017-12-12 02:56:10 -08:00
greg 7e8c4267c2 Remove a unimplemented 2017-12-11 01:55:08 -08:00
greg 25527bbdf0 Add fn literal variant 2017-12-11 01:53:27 -08:00
greg 5e7aef1040 Even more concise 2017-12-10 19:01:44 -08:00
greg 3386fcc505 Refactoring 2017-12-10 18:44:21 -08:00
greg 18a839bb91 Starting lambdas 2017-12-10 03:58:44 -08:00
greg 92d641fca0 Make var methods better 2017-12-10 03:35:51 -08:00
greg 9b4499c5ac If expressions 2017-12-10 02:58:07 -08:00
greg 07e19cbfa2 Rukka - Variables 2017-12-09 19:08:08 -08:00
greg 2016fcab41 Add schala idea 2017-12-09 18:56:34 -08:00
greg 0e7b6f25b3 Can specify language name with -l in any case 2017-12-09 18:19:07 -08:00
greg 7e7aa55d6e Go directly to langauge by name 2017-12-09 13:38:55 -08:00
greg cc79565fb3 Define half-working 2017-12-07 20:28:09 -08:00
greg 5659bab684 Language name in prompt 2017-12-07 20:08:31 -08:00
greg 405f91a770 Get rid of old import 2017-12-07 19:55:18 -08:00
greg faed1d6f25 eq? 2017-12-07 19:54:53 -08:00
greg f6d047e3b8 True and False primitives 2017-12-07 19:51:34 -08:00
greg fcd980f148 Some primitive implementations 2017-12-07 19:48:48 -08:00
greg c3919daa66 Fix pointer alias problem 2017-12-07 11:22:59 -08:00
greg f8152f68ad Still tryign to make the pointer-munging work 2017-12-07 08:15:28 -08:00
greg 9273773bf4 This has broken sexp parsing 2017-12-04 04:56:29 -08:00
greg 844cef36c7 Fix print bug 2017-12-04 03:26:38 -08:00
greg 0e17e45f3e Convert to more lispish Cons 2017-12-04 03:23:55 -08:00
greg 18d8ca7bd5 Special forms list 2017-12-04 03:06:54 -08:00
greg 2ee14bf740 Unwraps 2017-12-04 03:02:38 -08:00
greg 502497687a Handle top-level empty list 2017-12-04 03:01:47 -08:00
greg 107897ec97 print list 2017-12-04 02:44:09 -08:00
greg 8534fb4118 Tighten code 2017-12-04 02:00:00 -08:00
greg a1e38aba8e Some more code 2017-12-04 01:57:24 -08:00
greg 2f8ef99b08 Type simplification 2017-12-03 22:20:43 -08:00
greg 2bb55b6cca State for eval 2017-12-03 22:10:19 -08:00
greg bcd70ff538 Numbers 2017-12-03 19:21:56 -08:00
greg 728393671f Fixed string parsing 2017-12-03 17:47:17 -08:00
greg 9c3e223e51 Strings partway working 2017-12-03 17:11:17 -08:00
greg 2738119f17 Quotes 2017-12-03 06:04:53 -08:00
greg 630ead289c Change Symbol -> Word for token 2017-12-01 03:00:42 -08:00
greg 485e869c90 Fix bug 2017-12-01 02:58:09 -08:00
greg 9e8a3d1f08 Tighten code 2017-12-01 02:39:17 -08:00
greg b1da524a8f Intersperse 2017-12-01 02:36:52 -08:00
greg 787b6d51a4 Parsing correctly yay 2017-12-01 02:16:28 -08:00
greg 1dae4443cd Tokens 2017-11-30 22:37:49 -08:00
greg 210a45c92e Sexp parsing 2017-11-29 02:08:30 -08:00
greg 815e0401f2 Parses ( 2017-11-29 01:45:29 -08:00
greg 753247ee83 Some halfwritten stuff 2017-11-28 03:37:16 -08:00
greg 6223fc20f3 List datatype 2017-11-27 00:57:26 -08:00
greg da928db351 Add a new language - Rukka
This is a (simple) lisp, partially for fun, partially for testing the
generic interfaces
2017-11-26 21:17:17 -08:00
greg 93d0cfe5b8 Make schala-lib::language private and reexport 2017-11-02 02:45:26 -07:00
greg 687b28d1d1 Take TokenError type out of schala-lib 2017-11-01 22:41:34 -07:00
greg b62f618256 I don't need this syntax 2017-11-01 01:25:26 -07:00
greg f25b76ea11 Kill some packages from schala bin 2017-11-01 01:23:54 -07:00
greg 6b2736348d Get rid of unused imports 2017-10-31 00:45:15 -07:00
greg 69d5f38ea1 Refactor into libs part II
woo it compiles
2017-10-30 22:18:02 -07:00
greg a6f8616839 Halfway done to library-ifying schala 2017-10-30 20:06:20 -07:00
greg cdcb55e3b8 PLIGenerators can be authoritative, not the instances themselves 2017-10-29 13:45:55 -07:00
greg 74ac26841f Some simplification 2017-10-29 12:27:24 -07:00
greg 8fd29b5090 Passing things along as generators 2017-10-29 04:09:10 -07:00
greg 5ebc96daa7 Don't need mutex, kill it 2017-10-29 04:04:54 -07:00
greg 277e039251 Finally removed schala dependency
Now need to clena up everything
2017-10-29 03:41:40 -07:00
greg 6e8f57e54f Okay this compiles
The secret (from #rust) appeared to be that Fn() needed to have + Send
explicitly annotated on it
2017-10-29 03:16:08 -07:00
greg ae02391270 Working on solution to Rocket state problem 2017-10-27 00:30:28 -07:00
greg 9379485713 Some linker bullshit
I don't know why I needed to do this
2017-10-26 02:03:47 -07:00
greg 910522537c Splitting up some code
In preparation for splitting schala into crates
2017-10-23 20:51:08 -07:00
greg 98e1a5235a Some more structure in evaluator 2017-10-23 01:54:35 -07:00
greg e054c4b27f Revert "Starting to split project into multiple crates"
This reverts commit e3b0f4a51e.
Bah, this was a bad idea, wrong way to do it
2017-10-23 00:45:01 -07:00
greg e3b0f4a51e Starting to split project into multiple crates 2017-10-23 00:43:43 -07:00
greg 911f26e9c6 Halfway done with evaluating tuples 2017-10-23 00:22:25 -07:00
greg 677e3ae0a9 Add module keyword 2017-10-22 03:58:09 -07:00
greg 9611770bb3 Switch from request to superagent
For doing HTTP requests. Makes the js bundle a lot smaller.

Also I should do something about the fact that I now have to change the
js and also rebuild the rust binary to change code
2017-10-15 02:37:02 -07:00
greg 4c256cb5f7 Literal non-primitive values 2017-10-14 13:54:17 -07:00
greg 688e1c7f5d Starting work on literal non-primitve values 2017-10-13 18:56:02 -07:00
greg 26c9c72bcc Can eval custom data constructors now 2017-10-13 03:05:18 -07:00
greg 2d614aa17a Float literals, kill old code 2017-10-13 00:01:43 -07:00
greg ecb2eb0f87 Some more primitive types + binop-checking 2017-10-12 23:59:52 -07:00
greg 4c4004d3ac Add required imports 2017-10-12 21:46:12 -07:00
greg 9b4a23c4f2 Some partial work on refactoring type infer fn 2017-10-12 20:14:33 -07:00
greg 936c168cef Add colored output to non-interactive 2017-10-12 10:43:54 -07:00
greg db835f42aa Convert webapp to using included files 2017-10-12 02:13:55 -07:00
greg cd5fc36c37 Fix type check macro to add symbol table 2017-10-11 16:43:04 -07:00
greg d7a33c974e More work with unification 2017-10-11 02:33:46 -07:00
greg b2288206d2 the evar table
TODO find a better way to represent this
2017-10-11 02:11:12 -07:00
greg d962e2c27a Unify work 2017-10-11 02:03:50 -07:00
greg 4534c1d3d6 Move type-level func up 2017-10-11 01:55:45 -07:00
greg f79dc0b1e3 Okay I am figuring things out about hindley-milner again 2017-10-11 01:50:04 -07:00
greg 4928fc0019 rename type_var to ty 2017-10-10 22:14:55 -07:00
greg d735e45688 String and () types 2017-10-10 21:51:45 -07:00
greg b4208b696d Change around some stuff 2017-10-10 21:23:24 -07:00
greg ff3dbbcbc6 Change Variable to Value 2017-10-10 21:02:32 -07:00
greg e3261be8a0 Partial handling of user defined types 2017-10-10 17:29:28 -07:00
greg f131105b50 Starting to make unify actually work 2017-10-10 04:38:59 -07:00
greg 1089a33634 Convert unify to are types
b/c Type implements Clone
Maybe wanna kill this later for efficiency
2017-10-10 04:26:40 -07:00
greg 6c60794485 Have + do something different with strings
Needed to introduce polymorphism soon
2017-10-10 02:45:25 -07:00
greg 2f18529bcc Operator typing a little bit 2017-10-10 02:41:17 -07:00
greg c68e09d89d Slight refactoring 2017-10-10 02:32:02 -07:00
greg d9e8178a90 Renamed all the type-related types 2017-10-10 02:17:07 -07:00
greg 57536e6399 Move some type checking code around 2017-10-10 01:11:24 -07:00
greg 32e077c407 Merge branch 'master' of github.com:neunenak/schala 2017-10-10 01:09:27 -07:00
greg 83752a1c74 Some more type work 2017-10-10 01:04:19 -07:00
greg 33d0d49d30 Basic typing test 2017-10-09 12:26:25 -07:00
greg 76a9367284 String types 2017-10-09 11:42:53 -07:00
greg 66c7bbeb07 Floats, pathspec changes 2017-10-09 04:02:50 -07:00
greg ed8359bcd7 Store constant state, func/binding as value
on symbol table, instead of key
2017-10-09 02:38:33 -07:00
greg 996f75e15c A lot more type work 2017-10-09 02:26:59 -07:00
greg 30a54d997c Simplify symbol table code 2017-10-09 00:59:52 -07:00
greg 4bcbf1854a Use universal/existential type data structures 2017-10-09 00:36:54 -07:00
greg f2c6556c2a Use name TypeVariable 2017-10-09 00:22:42 -07:00
greg 9161e2751f (Janky) type inference for explicitly-type-annotated values 2017-10-08 23:45:38 -07:00
greg 60fc9fd7e1 Super-basic type inference working
with a bunch of assumptions and hard-coded values
2017-10-08 23:33:53 -07:00
greg 3b249045aa Call needs to accept a general argument 2017-10-08 23:02:03 -07:00
greg ff0e14d9a9 Rename params -> args in Call Expr 2017-10-08 22:52:05 -07:00
greg 8fe535597e Starting to actually do Hindley-Milner!! 2017-10-08 22:48:10 -07:00
greg 4bb8f82579 Make AST output red 2017-10-08 22:17:29 -07:00
greg 5cb8423ecc Beginning for expressions 2017-10-08 22:07:18 -07:00
greg 4032707dc9 Kill some comments 2017-10-08 21:26:47 -07:00
greg 1a8423535a Add test for function decl 2017-10-08 21:25:51 -07:00
greg 338981febe Changed function signatures around slightly 2017-10-08 21:21:02 -07:00
greg 6dff8b029e Function definitions expanded 2017-10-08 20:55:05 -07:00
greg df877830d3 Fixed tests 2017-10-08 19:39:41 -07:00
greg 40696b3cbd Rename TypeAnno to TypeName everywhere 2017-10-08 19:30:52 -07:00
greg 40a82d7e25 Tests for new type stuff
+ some renaming
2017-10-08 19:15:08 -07:00
greg c605f76059 More type work II 2017-10-08 19:03:02 -07:00
greg a6d71821b9 More type work I 2017-10-08 18:47:57 -07:00
greg c4f0331d1a Symbol table addition should be separate stage 2017-10-08 16:24:44 -07:00
greg b4054d7581 Impl blocks 2017-10-08 14:24:02 -07:00
greg 74d3828c71 Symbol table debug needs to happen before type check 2017-10-08 13:59:55 -07:00
greg bb57da564d Infrastructure to debug symbol table 2017-10-08 13:57:43 -07:00
greg 3f9ae5fac3 Symbol table accepts variables 2017-10-08 13:51:56 -07:00
greg 62edc7c996 type checking / symbol table stuff 2017-10-08 12:22:04 -07:00
greg e412fb9a89 Convert type-checking function type 2017-10-07 22:08:48 -07:00
greg 3a97401f61 Add symbol table insertion method skeleton 2017-10-07 21:57:51 -07:00
greg 87cfe854ac Tuple literals 2017-10-06 20:28:07 -07:00
greg 184a2ae03a Change syntax for type alias 2017-10-04 22:02:31 -07:00
greg 50ceb92d6b Move type-checking into a module 2017-10-04 02:07:30 -07:00
greg dd7736e32d Add some resources 2017-10-03 21:28:17 -07:00
greg 3025af3ded Starting on impls 2017-10-03 03:49:07 -07:00
greg 24608362a5 Added a non-interactive schala source file 2017-10-02 23:42:34 -07:00
greg c83df6fd84 refactor main code 2017-10-02 23:33:07 -07:00
greg 65f64ebcc2 Add source file suffix to trait 2017-10-02 23:07:05 -07:00
greg 00ee802fbd Clear up clutter in code from using std::process:: 2017-10-02 23:00:11 -07:00
greg c88d59401c Making main.rs more concise 2017-10-02 22:58:03 -07:00
greg 1aa4e3b942 Get rid of virtual machine code
Gonna implement this differently
2017-10-02 20:34:51 -07:00
greg abbbb34901 Some very basic evaluation stuff 2017-10-02 20:11:27 -07:00
greg 3ff4a34aeb kill some non-used variable warnings 2017-10-02 01:52:46 -07:00
greg 7430aebe63 Webapp actually does something 2017-10-02 00:15:39 -07:00
greg 9071846df3 Flex on web input 2017-10-02 00:04:33 -07:00
greg 29d307ff53 Made web app a bit more useful 2017-10-01 23:25:36 -07:00
greg 89482e5b5a Use a Result 2017-10-01 19:31:43 -07:00
greg 6435d5e958 Make eval output nicer 2017-10-01 19:29:05 -07:00
greg f6536e7ebd Evaluation stuff 2017-10-01 19:09:55 -07:00
greg c5feea4597 Add basics of compiler design to readme 2017-10-01 18:37:10 -07:00
greg c5cb223168 Super-minimal type-checking with just ints 2017-10-01 17:50:26 -07:00
greg d16a0c9380 Evaluation of literals 2017-10-01 12:55:28 -07:00
greg daf9878020 Kill some unused code 2017-10-01 00:50:13 -07:00
greg f825c87397 Type checking beginnings 2017-10-01 00:48:08 -07:00
greg 8d2a65b44e Starting eval framework 2017-09-30 23:30:02 -07:00
greg 6b9fee1aed Made handling parse errors nicer 2017-09-30 14:41:37 -07:00
greg d05f173dd3 Using delmited! macro for more things 2017-09-30 14:11:38 -07:00
greg e88a0f59b5 Made macro less complicatd 2017-09-30 13:46:50 -07:00
greg 90cf7db609 Use the delimiter macro in a few places
Made it capable of handling the strict <item> <delim> behavior necessary
for non-blocks, as well as teh loose behavior necessary for blocks,
added a test for a parse error.
2017-09-30 13:04:56 -07:00
greg 1ae9dbcba7 Tests for tuple type annotations 2017-09-30 01:14:49 -07:00
greg 9214f36c04 Tests for type annotations 2017-09-29 19:10:37 -07:00
greg 98169bd352 update test for new type anno format 2017-09-29 14:53:16 -07:00
greg d60cf99ab5 Parsing sorta works
Need to handle double >> bug
2017-09-29 14:10:49 -07:00
greg bb93d29beb Some type anno parsing work 2017-09-28 23:55:10 -07:00
greg c20f93e18c A test for type annotation 2017-09-28 00:45:36 -07:00
greg f48adbd9bf Made the tests work again 2017-09-28 00:06:08 -07:00
greg 9ad506fc78 Handling type annotations in the AST 2017-09-27 22:27:50 -07:00
greg 4c81c36d67 Parse type annotations
Not using them yet
2017-09-26 22:10:13 -07:00
greg 230f2dd7ff More match expr work 2017-09-21 16:00:14 -07:00
greg 1615269a7b Browser stuff 2017-09-21 09:25:39 -07:00
greg 1a8ba8d8a2 Requests! 2017-09-21 00:01:54 -07:00
greg d4aec19c71 web: CodeArea 2017-09-20 23:46:46 -07:00
greg 10b1864680 Version-control yarn.lock 2017-09-20 23:23:17 -07:00
greg 4831a24853 yarn build script + rocket passthrough route 2017-09-20 23:21:45 -07:00
greg 67ff21d408 Basic yarn + browserify + babeljs infrastructure
For webapp
2017-09-20 23:15:29 -07:00
greg 8b83d982c0 More work on match expression 2017-09-20 21:05:08 -07:00
greg 6bff7aac0d Match expressions
not done yet
2017-09-20 20:30:30 -07:00
greg 18fa160fed Move source files into directory 2017-09-19 22:36:48 -07:00
greg 7ac5846282 A tiny bit more work on the webapp 2017-09-19 22:11:05 -07:00
greg 88209d370d Add .schala_repl to gitignore 2017-09-19 22:11:05 -07:00
greg 0f9d2d76c4 Serve an actual file
Convert this to stdweb soon?
2017-09-19 22:11:05 -07:00
greg 006fd7d411 Trying webapp
Note this doesn't work yet
2017-09-19 22:11:05 -07:00
greg e3b236a15d If expressions 2017-09-19 22:11:04 -07:00
greg 68bbd62ab6 Make token debug nicer 2017-09-19 22:11:04 -07:00
greg e47a2c7241 Save REPL config to file 2017-09-19 22:11:04 -07:00
greg a8b77848b4 kill old EBNF in comments 2017-09-19 22:11:04 -07:00
greg 839731f2d1 Make op! macro part of binexp!/prefexp! macros
For tests
2017-09-19 22:11:04 -07:00
greg f51e1a3c47 make Operation a tuple-style struct 2017-09-19 22:11:04 -07:00
greg fc350cd03e Prefix operators 2017-09-19 22:11:04 -07:00
greg 8fe7c85b00 return keyword + idea for how to use it in for 2017-09-19 22:11:04 -07:00
greg b920fae93b variable bindings 2017-09-19 22:11:04 -07:00
greg 1981b74d89 Nicen up precedence-testing 2017-09-19 22:11:04 -07:00
greg 077ab8ddb8 Add trace to binexp parser 2017-09-19 22:11:04 -07:00
greg 9775bfc342 bool literals 2017-09-19 22:11:04 -07:00
greg 505d23a327 Parse . operator 2017-09-19 22:11:04 -07:00
greg 81c4566c2b Improved operator parsing 2017-09-19 22:11:04 -07:00
greg 8be757beca Some changes to EBNF grammar 2017-09-19 22:11:04 -07:00
greg 20c74953b5 Get rid of unimplemented! and panic! 2017-09-19 22:11:04 -07:00
greg d5c3227966 Parse string literals 2017-09-19 22:11:04 -07:00
greg fbeb101e7f make parse trace have newlines 2017-09-19 22:11:04 -07:00
greg 18c761a5b5 Wrap all parse methods in record-printing macro 2017-09-19 22:11:04 -07:00
greg 89cf101362 Refactoring prace trace infra 2017-09-19 22:11:04 -07:00
greg 66d10604ba make parse_method! macro more naturalistic 2017-09-19 22:11:04 -07:00
greg 565461e1db Show more useful information in parse tracing 2017-09-19 22:11:04 -07:00
greg 5ecd28d057 Print parse record in REPL as TraceArtifact 2017-09-19 22:11:04 -07:00
greg 6c5dbac406 Starting to add logic to track recursive descent calls 2017-09-19 22:11:04 -07:00
greg 5dd1cd79ff Parsing call expressions 2017-09-19 22:11:03 -07:00
greg dfc89e5060 Index expressions
This only partially works
2017-09-19 22:11:03 -07:00
greg 5871bf68de test for function parsing 2017-09-19 22:11:03 -07:00
greg 34b569eb5f Starting to parse formal params 2017-09-19 22:11:03 -07:00
greg 3220f0aec7 A random idea 2017-09-19 22:11:03 -07:00
greg 5810fb7961 type alias test 2017-09-19 22:11:03 -07:00
greg 1a3076a949 Change syntax of rc macro 2017-09-19 22:11:03 -07:00
greg 1d9e5edfba Rudimentary type stuff 2017-09-19 22:11:03 -07:00
greg 555d2a7ba5 Identifier tests 2017-09-19 22:11:03 -07:00
greg 291fb61c8d Parse identifiers
Some more complicted types of expression
2017-09-19 22:11:03 -07:00
greg 685b579fdd paren exprs 2017-09-19 22:11:03 -07:00
greg f72e77cbb6 Remove printlns 2017-09-19 22:11:03 -07:00
greg 4b5afef17e Added one more test 2017-09-19 22:11:03 -07:00
greg 5889998126 Precedence parsing
Using the Pratt parser algorithm (probably with some bugs as of yet).
Need to clean up the code some as well
2017-09-19 22:11:03 -07:00
greg c19946bb6d Additional test 2017-09-19 22:11:03 -07:00
greg d1301b30e6 Added infra for operators 2017-09-19 22:11:03 -07:00
greg f8287e42ce Binary literal test case 2017-09-19 22:11:03 -07:00
greg bd6bf2f4bb Parse binary literal 2017-09-19 22:11:03 -07:00
greg a6b336d84c type anno EBNF 2017-09-19 22:11:03 -07:00
greg deab74b992 Kill extraneous lines 2017-09-19 22:11:03 -07:00
greg 0755d42112 More parsing work 2017-09-19 22:11:03 -07:00
greg 117e0e38a8 Starting types 2017-09-19 22:11:03 -07:00
greg 3f1de5f60d Kill unused struct 2017-09-19 22:11:03 -07:00
greg c52fd4c73d Parse test 2017-09-19 22:11:03 -07:00
greg cac3ea86cf Import TokenType and Kw everywhere 2017-09-19 22:11:03 -07:00
greg 92ece39d5e Only IntLiteral
Signed/unsigned is via - operator
2017-09-19 22:11:03 -07:00
greg 14c09bb40c Float literals too 2017-09-19 22:11:03 -07:00
greg 0dabbc700b Concise-ify code 2017-09-19 22:11:03 -07:00
greg 741e5f7f9b Parsing basic numbers! 2017-09-19 22:11:03 -07:00
greg cfefceabf9 More infra
Don't want EOF after all
2017-09-19 22:11:02 -07:00
greg ea08f8cab8 More parse infra 2017-09-19 22:11:02 -07:00
greg 7d1c07c481 Parsing infrastructure 2017-09-19 22:11:02 -07:00
greg 7831cb8d8a Start parsing 2017-09-19 22:11:02 -07:00
greg 16d9e3eb60 Colored text for artifacts 2017-09-19 22:11:02 -07:00
greg 737dad6438 Added some tests
And commented out old tests for Maaru that don't compile
2017-09-19 22:11:02 -07:00
greg 74f8c16599 Fix bug with _ 2017-09-19 22:11:02 -07:00
greg a82f24a158 Kill import 2017-09-19 22:11:02 -07:00
greg 6459ad28e8 A few more keywords 2017-09-19 22:11:02 -07:00
greg 5e0c7e5a95 Add some things to test.schala
Still playing with syntax
2017-09-19 22:11:02 -07:00
greg 57d4222746 Operators, keywords largely working 2017-09-19 22:11:02 -07:00
greg 88d1896281 Identifiers and keywords 2017-09-19 22:11:02 -07:00
greg 7fe0a6589e Unclosed string 2017-09-19 22:11:02 -07:00
greg ac5bdd7bcb Change some func signatures around tokenizing and errors 2017-09-19 22:11:02 -07:00
greg 8bf5f40a2a Some string tokenizing - not done 2017-09-19 22:11:02 -07:00
greg 7e505dd88e Stuff 2017-09-19 22:11:02 -07:00
greg f15427e5d9 A bunch of token stuff 2017-09-19 22:11:02 -07:00
greg 8230b115de Add test.schala file
With some syntax ideas
2017-09-19 22:11:02 -07:00
greg a53135a897 More elaborate tokens 2017-09-19 22:11:02 -07:00
greg f3c8474c93 Add help entry
Would like to make this generalizeable
2017-09-19 22:11:02 -07:00
greg 8dc8d15437 Cleaned up Repl struct 2017-09-19 22:11:02 -07:00
greg b5a6c5903e Switch to contentful output types 2017-09-19 22:11:02 -07:00
greg c97e58c2aa Cleared out all remaining linter warnings 2017-09-19 22:11:02 -07:00
greg cb9b56f000 Added back compilation 2017-09-19 22:11:02 -07:00
greg 55e1600b97 Kill old trait 2017-09-19 22:11:02 -07:00
greg fb009497a4 Still more cleanup 2017-09-19 22:11:02 -07:00
greg 4b13fef734 More cleanup 2017-09-19 22:11:02 -07:00
greg 14ccf9f1be Converted Robo to new style trait 2017-09-19 22:11:02 -07:00
greg 7a6dfbbd0e Deleting old code 2017-09-19 22:11:02 -07:00
greg bb3f85dd16 Getting rid of old code for maaru 2017-09-19 22:11:02 -07:00
greg 3e66568ddd Converted over Maaru to new schema
-partially...
2017-09-19 22:11:02 -07:00
greg 3abe299361 More work on new trait structure 2017-09-19 22:11:02 -07:00
greg 626b17cbd2 Idea for trait redesign 2017-09-19 22:11:02 -07:00
greg 192a7e611f Parsing BNF 2017-09-19 22:11:01 -07:00
greg d3febb201b More parsing 2017-09-19 22:11:01 -07:00
greg f9fe81993f Beginning parsing code 2017-09-19 22:11:01 -07:00
greg ff01d4b798 Initial Schala (for real) commit 2017-09-19 22:11:01 -07:00
greg dd22ca0291 Grand renaming of things 2017-09-19 22:11:01 -07:00
greg 801896bcc6 Starting to add code for vm 2017-09-19 22:11:01 -07:00
greg 6f8c89af37 Change wording 2017-09-19 22:11:01 -07:00
greg f82e1e699c Rust, not Haskell 2017-09-19 22:11:01 -07:00
greg a0f3583ab1 Forgot newline 2017-09-19 22:11:01 -07:00
greg e81d5e108b Improved README 2017-09-19 22:11:01 -07:00
greg 5d15d60ab6 Structs implemented
albeit very inefficiently
2017-09-19 22:11:01 -07:00
greg f0de3c3d12 Fix this source file 2017-09-19 22:11:01 -07:00
greg 9dd8f90e3c Only print last evaluated result 2017-09-19 22:11:01 -07:00
greg e0f5f01e69 Kill error messges for not using Result 2017-09-19 22:11:01 -07:00
greg 424998c128 Lists work! 2017-09-19 22:11:01 -07:00
greg b93625819c Update ReducedValue to handle lists 2017-09-19 22:11:01 -07:00
greg f90bfb88ca Fix display of list 2017-09-19 22:11:01 -07:00
greg 850b77541b Display of lists sorta works 2017-09-19 22:11:01 -07:00
greg dbf5886aad List evaluation technically working 2017-09-19 22:11:01 -07:00
greg dd93adf5b7 try!() -> ? 2017-09-19 22:11:01 -07:00
greg d8df98ba01 Beginnings of list literals 2017-09-19 22:11:01 -07:00
greg 4da771036a Part of evaluation path for indexing done 2017-09-19 22:11:01 -07:00
greg 3911c45dde Introduced index notation 2017-09-19 22:11:01 -07:00
greg f3c3d4595e Immediate lambda call 2017-09-19 22:11:01 -07:00
greg e4a42e7691 Add back eval printing 2017-09-19 22:11:01 -07:00
greg cc537f292d Starting Maaru AST 2017-09-19 22:11:01 -07:00
greg 840e093bc4 Maaru - token work 2017-09-19 22:11:01 -07:00
greg 815f2b8242 Starting work on Maaru tokens 2017-09-19 22:11:01 -07:00
greg 34dba9cc4d Schala - fix bug with comments 2017-09-19 22:11:01 -07:00
greg 6e28ae68a0 Add options 2017-09-19 22:11:01 -07:00
greg 48b0b8d053 Add logic for picking language with command line flags 2017-09-19 22:11:01 -07:00
greg e0c49abe56 Change show-llvm opt to -v 2017-09-19 22:11:01 -07:00
greg 65dc362a1d Killed some warnings, cleaned up some code 2017-09-19 22:11:01 -07:00
greg 8ff1c632c2 Make REPL friendlier 2017-09-19 22:11:01 -07:00
greg 039022bfc5 Get rid of println 2017-09-19 22:11:01 -07:00
greg 387ec25cda Fix bugs in interpreter argument parsing 2017-09-19 22:11:01 -07:00
greg ecf60198fa Can now switch between languages in the interpreter 2017-09-19 22:11:01 -07:00
greg f83cece2b4 Import Maaru into main 2017-09-19 22:11:00 -07:00
greg 8fd5fb5a0b Added language name functionality 2017-09-19 22:11:00 -07:00
greg 455fe2abe2 Get rid of stand alone evaluator 2017-09-19 22:11:00 -07:00
greg 902c85ccd7 Fully implemented state
If I make the LanguageInterface trait over a pair of language and
evaluator, then it works :)
2017-09-19 22:11:00 -07:00
greg 4ea600d55c Abstracted most work into LanguageInterface trait
Still need to handle state
2017-09-19 22:11:00 -07:00
greg 6dec35d460 Think I've nearly gotten it traitified correctly... 2017-09-19 22:11:00 -07:00
greg cc855affbf Make Maaru structs public 2017-09-19 22:11:00 -07:00
greg a303aa2a5b Add first new language - Maaru
Maaru is intended to be a haskell-ish functional language.
Here's enough of a skeleton to print a thing
2017-09-19 22:11:00 -07:00
greg 421a9a7e9b Abstract evaluation into EvaluationMachine trait 2017-09-19 22:11:00 -07:00
greg f37ab80163 Fix tests 2017-09-19 22:11:00 -07:00
greg 178434171e Cleaning up some types 2017-09-19 22:11:00 -07:00
greg fd4610e175 Make newtype for LLVM code strings 2017-09-19 22:11:00 -07:00
greg 5103f03fa5 Forgot to add mod.rs file 2017-09-19 22:11:00 -07:00
greg 1a4bf24ab1 Move schala-specific stuff into its own module 2017-09-19 22:11:00 -07:00
greg 9d6bdf22da More conversions to trait version 2017-09-19 22:11:00 -07:00
greg 8326a12c9c (Largely) trait-ify Schala
The idea is to provide a trait `ProgrammingLanguage` that actually does
all the work, and then main.rs will just be the infra for storing its
own state
2017-09-19 22:11:00 -07:00
greg 5e474231da ProgrammingLanguage types need Debug 2017-09-19 22:11:00 -07:00
greg 1ac440c8df Implement trait parse 2017-09-19 22:11:00 -07:00
greg f5022a771c Starting work to trait-ify language 2017-09-19 22:11:00 -07:00
greg eaf86ea908 Add support for +, - in num literals 2017-09-19 22:11:00 -07:00
greg eb6354e55a Only print errors if the programs failed 2017-09-19 22:11:00 -07:00
greg 751c6f65bd Deleted some code in compilation 2017-09-19 22:11:00 -07:00
greg 3e231b4715 Use native rust to write source file 2017-09-19 22:11:00 -07:00
greg e103ba221c Conditionals work! 2017-09-19 22:11:00 -07:00
greg d5f01a7b1f Continuing work on phi nodes 2017-09-19 22:11:00 -07:00
greg 1702163478 Add flag for llvm 2017-09-19 22:11:00 -07:00
greg bdd6f75cf6 Show/hide LLVM IR in REPL 2017-09-19 22:11:00 -07:00
greg 2681dbc4f2 Add test for "a+4" being conterintuitive
Also fix Op -> BinOp in tests
2017-09-19 22:11:00 -07:00
greg 9454fc6194 Add return statements to generated functions 2017-09-19 22:11:00 -07:00
greg c8feaa9b57 Add logging of supplimental commands 2017-09-19 22:11:00 -07:00
greg 518414ffd5 I was doing a wrong thing with creating vecs
The old vector was getting dropped and thus free-ing the old
underlying slice. I want to use set_len() on Vec to do
this
2017-09-19 22:11:00 -07:00
greg 06a5de6e32 Trying to debug this segfault 2017-09-19 22:11:00 -07:00
greg dd4816624c Change name of project to Schala 2017-09-19 22:11:00 -07:00
greg 748a85db02 Compiling functions half-works 2017-09-19 22:11:00 -07:00
greg 8f2d9b900b Function codegen sorta works 2017-09-19 22:11:00 -07:00
greg b9d1140264 Refactored op compilation code
+ moved to separate function
2017-09-19 22:11:00 -07:00
greg 7188a7d33e Clarified that we hardcode a "main" function
in compiler data structure
2017-09-19 22:10:59 -07:00
greg 0c7099771f Comparison operator working 2017-09-19 22:10:59 -07:00
greg afec7e829c There's some segfault happening in LLVMBuildUIToFP 2017-09-19 22:10:59 -07:00
greg a6773d59bd Refactoring in compiling binops 2017-09-19 22:10:59 -07:00
greg d804efdc5e Use BinOp type instead of strings 2017-09-19 22:10:59 -07:00
greg 0ace370fc2 Tightened up REPL loop 2017-09-19 22:10:59 -07:00
greg 1f50fcc620 Improvments to interpreter directives parsing 2017-09-19 22:10:59 -07:00
greg d7181afa91 Few more linefeed-related changes 2017-09-19 22:10:59 -07:00
greg 4eb7683f47 Move linefeed reader to struct 2017-09-19 22:10:59 -07:00
greg b04a8f0092 Add back interpreter directives 2017-09-19 22:10:59 -07:00
greg c50be58cd2 Moved from simplerepl to lineread crate 2017-09-19 22:10:59 -07:00
greg 5911a07f4f Inline parsing of lambdas like half-works 2017-09-19 22:10:59 -07:00
greg 26bc6e90f3 Lamba calls partially work 2017-09-19 22:10:59 -07:00
greg b0655d7cab need to flush stdout for printing 2017-09-19 22:10:59 -07:00
greg a46ede9395 Made evaluation-printing more sophisticated 2017-09-19 22:10:59 -07:00
greg d9ab5a58cf Add some methods to llvm_wrap 2017-09-19 22:10:59 -07:00
greg 77297c7e06 Add lambdas 2017-09-19 22:10:59 -07:00
greg d93b5c0a2e Still cranking away at conditional compilation 2017-09-19 22:10:59 -07:00
greg 0b9dc113d1 CLoser to working now 2017-09-19 22:10:59 -07:00
greg d6fc13f08d Fix a couple of problems 2017-09-19 22:10:59 -07:00
greg 825c271b17 More work on codegen for conditionals
Still doesn't compile
2017-09-19 22:10:59 -07:00
greg 8c4f7e141a Compiling if statements like half done 2017-09-19 22:10:59 -07:00
greg 12fbc51da1 Compile multi-expression source programs 2017-09-19 22:10:59 -07:00
greg db108ee434 Unicode should work 2017-09-19 22:10:59 -07:00
greg 7ddb421ced Exit cleanly on opt parse fail 2017-09-19 22:10:59 -07:00
greg 1631bb0a04 Fix tests for conditionals 2017-09-19 22:10:59 -07:00
greg 5923cc2317 Kill then, else keywords 2017-09-19 22:10:59 -07:00
greg 1fa56800c5 Convert parsing while, if, fn exprs to use { } 2017-09-19 22:10:59 -07:00
greg 2b4d3e8516 Add support for curly braces and brackets
Gonna make this a curly-brace language, I like those better. Shoulda
done that to begin with.
2017-09-19 22:10:59 -07:00
greg 9b74527618 Control printing eval steps with flags 2017-09-19 22:10:59 -07:00
greg d23e5bff35 Add an Op type for binop operators
Soon this will get swapped in as the way that BinOps are evaluated
2017-09-19 22:10:59 -07:00
greg 3a4f5ae840 Change name Op -> OpTok
So that I can make an Op type for the ASTNode
2017-09-19 22:10:59 -07:00
greg 298194c42d Finish support for assignment operators 2017-09-19 22:10:59 -07:00
greg 23d2209d8b Implementing a few more operators
WIP - not done
2017-09-19 22:10:59 -07:00
greg 4cf165b408 Use buffered reader for stdout
Not sure ifthis is actually helping
2017-09-19 22:10:59 -07:00
greg 154839979b Add nicer handle_builtin method 2017-09-19 22:10:59 -07:00
greg 6741787852 Update references in README 2017-09-19 22:10:59 -07:00
greg 538f0b18f4 Evaluate while loop 2017-09-19 22:10:59 -07:00
greg dc81d237c5 Reduce re-allocations in eval 2017-09-19 22:10:58 -07:00
greg 8651839a66 Getting rid of some newlines - concision 2017-09-19 22:10:58 -07:00
greg f6e5ea250d Convert while_expr to delimiter_block! too 2017-09-19 22:10:58 -07:00
greg 9801f53a17 Moved conditionals to delimiter_block! syntax 2017-09-19 22:10:58 -07:00
greg db92292569 Fixed all tests 2017-09-19 22:10:58 -07:00
greg e1ce54aece Add delimiter_block macro 2017-09-19 22:10:58 -07:00
greg c227ad656f Parser simplifications, renames, etc. 2017-09-19 22:10:58 -07:00
greg b45d09e81a Don't need this reference 2017-09-19 22:10:58 -07:00
greg 761500b9d6 Some cleanups in Parser
-get rid of some use statements
-mkae error messages better
2017-09-19 22:10:58 -07:00
greg e888e82404 Remove some unnecessary destructurings of Rc<String> 2017-09-19 22:10:58 -07:00
greg 328ec4ba87 Convertd like half the Strings to RC
-still need to eliminate some clones in eval, parse
+ fix all the tests
2017-09-19 22:10:58 -07:00
greg 4a7b570603 Parser changes - add precedences, move definitions
Move impls of Display for AST subtypes closer to where they are defined
2017-09-19 22:10:58 -07:00
greg 7eb48fb4ef Working on compilation again 2017-09-19 22:10:58 -07:00
greg 8ebf1b3056 Add parser support for while statements 2017-09-19 22:10:58 -07:00
greg 905431b33c Change name: ASTNode -> Statement 2017-09-19 22:10:58 -07:00
greg 2996198eff lookup_binding only needs &str 2017-09-19 22:10:58 -07:00
greg 06771979df Function bodies can contain statements now 2017-09-19 22:10:58 -07:00
greg f158b6c712 Converted to multiple-evaluator logic
Now I have (basically) full single-step evaluation and it works fine
2017-09-19 22:10:58 -07:00
greg ba8f67441f Conditionals - handle delimiters correctly 2017-09-19 22:10:58 -07:00
greg 872e9ce7ee Make function binding a SideEffect 2017-09-19 22:10:58 -07:00
greg 2722533efd Prove recursion works 2017-09-19 22:10:58 -07:00
greg edf342e65a Add == operator 2017-09-19 22:10:58 -07:00
greg 27d4c2ccbd No references in pattern-matching 2017-09-19 22:10:58 -07:00
greg 6794d22f1d Run rustfmt on eval.rs, parser.rs 2017-09-19 22:10:58 -07:00
greg 1858d26638 Add comparison operators
+  make operator evaluation more concise
2017-09-19 22:10:58 -07:00
greg 84fbe73cf6 Add Lambda type
And change name FuncNode -> FuncDefNode

Now function definition nodes reduce to a Lambda, which is not
reducible.
2017-09-19 22:10:58 -07:00
greg ad994c38ae Test simplification 2017-09-19 22:10:58 -07:00
greg 48343d3fad Tightened tokenization tests 2017-09-19 22:10:58 -07:00
greg 4f8ff35d0f fixed bug with ends_identifier 2017-09-19 22:10:58 -07:00
greg b210ad5e19 Added links to README 2017-09-19 22:10:58 -07:00
greg 7311d0311f Simplify pattern a little bit 2017-09-19 22:10:58 -07:00
greg 1b59c264b4 Use itertools peeking_take_while
Cuts down on lines in the tokenizer
2017-09-19 22:10:58 -07:00
greg b2e453a9de Rewrite of tokenizer 2017-09-19 22:10:58 -07:00
greg 59226eb731 Ran rustfmt on parser.rs 2017-09-19 22:10:58 -07:00
greg 297003c0b0 Operator only needs to be a tuple struct 2017-09-19 22:10:58 -07:00
greg e5ee072b00 Convert tokenizer to large match statement
In the hopes of making it shorter
2017-09-19 22:10:58 -07:00
greg 9b62efc830 Fix conditional parsing
Needed to account for semicolons/newlines. Maybe need to generalize
delimiter-separated list of things
2017-09-19 22:10:58 -07:00
greg f626ca1427 Add test for conditional parsing 2017-09-19 22:10:57 -07:00
greg 82c52ede48 Finish evaluating conditionals 2017-09-19 22:10:57 -07:00
greg 7f52b20d97 Conditional eval partway implemented
Need to change the AST representation to support compound statements I
think
2017-09-19 22:10:57 -07:00
greg 12fee6158c Vector'd expressions don't need to be boxed 2017-09-19 22:10:57 -07:00
greg 2d21de7cc3 Added support for conditionals to parser
Not to eval yet
2017-09-19 22:10:57 -07:00
greg 9cc9c5977d Fixed evaluation of function calls
This bit still isn't quite small-step but maybe that's okay for
functions
2017-09-19 22:10:57 -07:00
greg ed9d1312d1 Change representation of variables 2017-09-19 22:10:57 -07:00
greg 29d9e50311 All environment changes represented explicitly
Start the work of rewriting the evluator to be a true small-step
evaluator - that is, all state changes are represented explicitly as
SideEffect types, and not as methods called on the evaluator, except at
the very top of the evaluation loop
2017-09-19 22:10:57 -07:00
greg e84550f3ec Kill two compilation warnings 2017-09-19 22:10:57 -07:00
greg 3063de1242 Run rustfmt on the rest of them 2017-09-19 22:10:57 -07:00
greg e1d07b4e66 Rustfmt on llvm_wrap 2017-09-19 22:10:57 -07:00
greg af45004afa Run rustfmt on some files 2017-09-19 22:10:57 -07:00
greg d4d61ce5ad Use process::exit in main 2017-09-19 22:10:57 -07:00
greg 743311d18a Convert Tokenize to Result 2017-09-19 22:10:57 -07:00
greg 17f9846bb9 Make tests pass w/ new match syntax 2017-09-19 22:10:57 -07:00
greg fe8418edbe Kill some extraneous spaces 2017-09-19 22:10:57 -07:00
greg 1d8102b9fa Make compilation controllable 2017-09-19 22:10:57 -07:00
greg aac3ca40fe Add getopts 2017-09-19 22:10:57 -07:00
greg c389b44bf8 Compile statements with variables 2017-09-19 22:10:57 -07:00
greg db52f9b716 Package compilation context into one struct 2017-09-19 22:10:57 -07:00
greg d3a743442b = should have high precedence 2017-09-19 22:10:57 -07:00
greg e46d840d96 Move more code to llvm_wrap
And silence a few compiler warnings
2017-09-19 22:10:57 -07:00
greg 3d406f1dd2 Move llmv wrapper into separate file 2017-09-19 22:10:57 -07:00
greg fca307d3ab Package more functions in LLVMWrapper 2017-09-19 22:10:57 -07:00
greg a65544356c Fix bug in delete routine 2017-09-19 22:10:57 -07:00
greg 10d51cc29c Compile binexps 2017-09-19 22:10:57 -07:00
greg 95b773de7f Compilation sequence
-move all steps of the llvm IR compilation process into the binary
2017-09-19 22:10:57 -07:00
greg c032da712f Successfully compiling a main function 2017-09-19 22:10:57 -07:00
greg cfb93d9dea Compilation that returns an output code works 2017-09-19 22:10:57 -07:00
greg 08798fb690 Move all LLVM functions into LLVMWrap
so I can kill the global unsafe
2017-09-19 22:10:57 -07:00
greg e0eef5e58f More moving functions around 2017-09-19 22:10:57 -07:00
greg 2107e6344e More function wrapping 2017-09-19 22:10:57 -07:00
greg 2ad65a1c5d Some in-progress work 2017-09-19 22:10:57 -07:00
greg a01b6c874e Small-step function arg evaluation 2017-09-19 22:10:57 -07:00
greg 907af38f44 Evaluate printing 2017-09-19 22:10:57 -07:00
greg 923566c4e9 Get rid of println's for token/ast debugging
Instead just explicitly stick them into the returned string.

This is necessary 'cause I'm gonna convert simplerepl to use ncurses
soon, so I can't have any side effects
2017-09-19 22:10:57 -07:00
greg 96c51a9b88 Move some more unsafe code into block 2017-09-19 22:10:57 -07:00
greg 8528c912bd Added side effect framework 2017-09-19 22:10:57 -07:00
greg 47f4c25020 Doing some wrapping 2017-09-19 22:10:56 -07:00
greg 6ed67cd325 Add test .schala file 2017-09-19 22:10:56 -07:00
greg c18c1a639a compilation does something usefl
Following along with:
http://blog.ulysse.io/2016/07/03/llvm-getting-started.html
2017-09-19 22:10:56 -07:00
greg d3f6bbabaa Starting to add actual LLVM generating code 2017-09-19 22:10:56 -07:00
greg 5d0648b9c0 Ignore Cargo.lock 2017-09-19 22:10:56 -07:00
greg ba7a2e658f Don't track Cargo.lock 2017-09-19 22:10:56 -07:00
greg ee12e10ac6 Fix slice syntax 2017-09-19 22:10:56 -07:00
greg 6a0b50f278 Kill hashmap import for now 2017-09-19 22:10:56 -07:00
greg 6c44d295db Framework for compilation 2017-09-19 22:10:56 -07:00
greg cd69ebaa9d Split main() into subfunctions 2017-09-19 22:10:56 -07:00
greg 3fe9ec95d5 Fix repl error message 2017-09-19 22:10:56 -07:00
greg 8b5d1ecd15 Kill compilation for now
Don't wanna deal with this right this second
2017-09-19 22:10:56 -07:00
greg 272fa92052 More fleshing out 2017-09-19 22:10:56 -07:00
greg 0c717c721e Import Function type 2017-09-19 22:10:56 -07:00
greg 64d560a1fc Still more work 2017-09-19 22:10:56 -07:00
greg 8a92d5ffa8 Partial LLVM work 2017-09-19 22:10:56 -07:00
greg 5ed0ed727b Starting to add LLVM code 2017-09-19 22:10:56 -07:00
greg 5428810d2c Add llvm dependencies 2017-09-19 22:10:56 -07:00
greg b48c007bca Get rid of old evaluator file
No longer need it
2017-09-19 22:10:56 -07:00
greg 5aa4c404a5 Added conditionals to grammar 2017-09-19 22:10:56 -07:00
greg 0e3aaa8b08 Add conditional expression support 2017-09-19 22:10:56 -07:00
greg f33cfdadfe Fix variable lookup order
Evaluator uses frames as a stack, so to find the most-recent variable
binding we have to iterate through it the reverse way. This fixes a bug
manifested by:

fn a(x)
 x + 20
end

fn b(x)
 a(x) + 20
end
2017-09-19 22:10:56 -07:00
greg 231de69084 Added infrastructure for reading files 2017-09-19 22:10:56 -07:00
greg f014c1a9d9 Kill compiler warnings 2017-09-19 22:10:56 -07:00
greg c36bc3377e Fix lookup_binding in function call case 2017-09-19 22:10:56 -07:00
greg 5eba222679 Variable binding in fucntion call work 2017-09-19 22:10:56 -07:00
greg dcf89aa429 Move lookup_function back onto Evaluator
THe problem was that we were borrowing the output of the inner HashMap,
if we clone it we don't borrow anything
2017-09-19 22:10:56 -07:00
greg 1ffbeb6472 Return last value out of function 2017-09-19 22:10:56 -07:00
greg f8a521fc9b Start making function calls work
This commit isn't fully done yet
2017-09-19 22:10:56 -07:00
greg 77f72806be Add support for multiple frames 2017-09-19 22:10:56 -07:00
greg dcde5d6018 Start function call reduction
Move the varmap and funcmap functions to the Evaluator, to mkae it
easier to facilitate separate frames
2017-09-19 22:10:56 -07:00
greg 6bb227d052 Rename methods
make reduce() the entry point to evaluation
2017-09-19 22:10:56 -07:00
greg 29d4cb53a4 Add infrastructure to do function evaluation
Right now you can successfully evaluate a function definition (returning
Null), but cannot call a function
2017-09-19 22:10:56 -07:00
greg 3915c1f035 Remove println from reduction 2017-09-19 22:10:56 -07:00
greg b400796e4d Get rid of test variable input 2017-09-19 22:10:56 -07:00
greg 96e6a87f64 Add string concat 2017-09-19 22:10:56 -07:00
greg b1f9e5cefc Fix variable lookup 2017-09-19 22:10:56 -07:00
greg be36d4697d Pretty-print evaluated AST nodes 2017-09-19 22:10:56 -07:00
greg ce8c511929 Evaluate additional operators 2017-09-19 22:10:55 -07:00
greg a8cafa8c64 Move Evaluator state into interpreter state 2017-09-19 22:10:55 -07:00
greg 229e6ae733 More expression parsing work 2017-09-19 22:10:55 -07:00
greg e9dd0d9ae8 Add concept of Null expression
Finally, the null-only behavior is starting to manifest itself!
2017-09-19 22:10:55 -07:00
greg 15d4317191 Add null expression 2017-09-19 22:10:55 -07:00
greg 7114e446a4 Adding, subtracting works 2017-09-19 22:10:55 -07:00
greg 044f534ac5 Start implementing variable lookup 2017-09-19 22:10:55 -07:00
greg d3207ad890 Move evaluation logic back into methods
They need to be able to access the environment which is stored in the
Evalator struct
2017-09-19 22:10:55 -07:00
greg 19fffd5063 Variable binding infrastructure 2017-09-19 22:10:55 -07:00
greg 785c916ece Start reducing ASTs
Start writing code to reduce AST nodes
q
2017-09-19 22:10:55 -07:00
greg 5a9ebb188d Make Evaluable trait 2017-09-19 22:10:55 -07:00
greg 16e8d969be Add basic evaluation 2017-09-19 22:10:55 -07:00
greg 70bf68d9bd More concision in parser 2017-09-19 22:10:55 -07:00
greg f53c14535b Made error! macro more programtic
TODO implement Display on Token so we're not just displaying the debug
name of the token enum variants
2017-09-19 22:10:55 -07:00
greg 4f96abd7d9 Changes to make the code more concise 2017-09-19 22:10:55 -07:00
greg fdaf4c302c Fix all compiler warnings 2017-09-19 22:10:55 -07:00
greg 8ce53d7c72 Fix bind error 2017-09-19 22:10:55 -07:00
greg 428d560e2a Add tests for call expr parsing 2017-09-19 22:10:55 -07:00
greg 80bc7ec089 Proper call expression parsing 2017-09-19 22:10:55 -07:00
greg e6591b80d9 Add paren test 2017-09-19 22:10:55 -07:00
greg e099f713ad Add binop parsing test 2017-09-19 22:10:55 -07:00
greg 9b54256521 Import types for breivty
and rename function to be explicit
2017-09-19 22:10:55 -07:00
greg 087402ece6 Add more tests
Need to use box patterns
2017-09-19 22:10:55 -07:00
greg 252b6e8bd9 Okay, this strategy makes the test work 2017-09-19 22:10:55 -07:00
greg b1163e2ae4 Operator-precedence parsing + tests
The tests are crippled now, because it's hard to write a test macro that
can also match on Strings
2017-09-19 22:10:55 -07:00
greg 032d01c9f5 Fix tokenization bug 2017-09-19 22:10:55 -07:00
greg c4ab1ed105 Fix tokenizer tests 2017-09-19 22:10:55 -07:00
greg 9a257f08d7 Introduce Op type
For operator parsing
2017-09-19 22:10:55 -07:00
greg 47d56a7b44 fix operator parsing 2017-09-19 22:10:55 -07:00
greg 1f7ae2e30f Paren expression 2017-09-19 22:10:55 -07:00
greg e3c8753a4d Expression parsing 2017-09-19 22:10:55 -07:00
greg e1aa7ecb17 Finish tokenizing Op separately 2017-09-19 22:10:55 -07:00
greg f09a6e14ba Tokenizer work to support operators
work in progress but committing to transfer
2017-09-19 22:10:55 -07:00
greg 31da25a66e Expression parsing work 2017-09-19 22:10:55 -07:00
greg fff9cb7d25 Fix functin parsing 2017-09-19 22:10:55 -07:00
greg db1e188fdb Move grammar to top of file 2017-09-19 22:10:55 -07:00
greg 935185ed92 more parsing 2017-09-19 22:10:55 -07:00
greg 0999cbe28e More parsing work 2017-09-19 22:10:55 -07:00
greg 674f70a428 Convert parsing to method-based 2017-09-19 22:10:54 -07:00
greg b1b6672399 Implement function parsing
With a lot of dummy code, especially around expression parsing
2017-09-19 22:10:54 -07:00
greg bd1c455dc8 Basic infrastructure parses
Also got rid of EOF, don't need it
2017-09-19 22:10:54 -07:00
greg b62ef43f07 Add basic BNF grammar 2017-09-19 22:10:54 -07:00
greg 09b67dc3f7 Change error message 2017-09-19 22:10:54 -07:00
greg a613fa73e5 Basic parsing framework 2017-09-19 22:10:54 -07:00
greg 8c473c554e Fix bug 2017-09-19 22:10:54 -07:00
greg 3e04cbfa29 Add comma tokenization 2017-09-19 22:10:54 -07:00
greg 570650cbfa Finish keyword tokenization 2017-09-19 22:10:54 -07:00
greg 49be163181 Add test to ignore
For better handing of user-defined operators, which I will do in the
future
2017-09-19 22:10:54 -07:00
greg b4f93acbd8 Couple more tests 2017-09-19 22:10:54 -07:00
greg 8c65ae3214 Macro-ize token tests 2017-09-19 22:10:54 -07:00
greg e436533638 Passing test 2017-09-19 22:10:54 -07:00
greg 4601a56867 Start working on tokenization tests 2017-09-19 22:10:54 -07:00
greg 2f7a1850db Finish tokenizing 2017-09-19 22:10:54 -07:00
greg 71aef379d3 Tokenize number literals
TODO: expand this bit of code to handle 0x12, etc. syntax
2017-09-19 22:10:54 -07:00
greg 8662a3ba0e Make tokenize error-able 2017-09-19 22:10:54 -07:00
greg 5ca98c7d77 Print tokeniziation 2017-09-19 22:10:54 -07:00
greg 13cde3106c Start making tokenizer changes
Hopefully this time iron out all the bugs from the last implementation
2017-09-19 22:10:54 -07:00
greg 09d524c74a Changing how parsing works again
Someone wrote a port of the LLVM kaleidoscope tutorial to rust, namely
https://github.com/jauhien/iron-kaleidoscope

I'm just gonna follow this along
2017-09-19 22:10:54 -07:00
greg 61c36c4def Fix assign parsing
= is a keyword not an identifier
2017-09-19 22:10:54 -07:00
greg bc4fbe4276 Start implementing definition
WIP, doesn't work
2017-09-19 22:10:54 -07:00
greg fc11ee753d Add block parsing
Right now evaluating a block reduces it to just the last AST in it, will
fix later with environments
2017-09-19 22:10:54 -07:00
greg dd2b4893a4 Get rid of Separator token
Have separate newline and semicolon tokens
2017-09-19 22:10:54 -07:00
greg 51745fd800 Make convenience macro for parse errors 2017-09-19 22:10:54 -07:00
greg 8c0ac19fa8 Add full test
Test evaluate, tokenize, parser all at once
2017-09-19 22:10:54 -07:00
greg 971ab9ba21 Implement paren expressions 2017-09-19 22:10:54 -07:00
greg 819fb3f58f Basic evaluator functionality
Interpreter now works for simple arithmetic expressions
2017-09-19 22:10:54 -07:00
greg 1c23329656 Add boilerplate for evaluation
Just wires everything up, doesn't actually evaluate yet
2017-09-19 22:10:54 -07:00
greg 6da20cbfaf Address compiler warnings 2017-09-19 22:10:54 -07:00
greg f48451125e Parsing arithmetic expressions works
At the expense of an unnecessary move in lookahead()
2017-09-19 22:10:54 -07:00
greg 06c3999430 Add expect_identifier function
For utility
2017-09-19 22:10:54 -07:00
greg 1af1589550 Make Parser class internally use IntoIterator
And wrap the next() and peek() methods
2017-09-19 22:10:54 -07:00
greg 32a90b8103 write expect
and also make the ParseResult type more general so it can be used for
more things.
2017-09-19 22:10:54 -07:00
greg 186c900920 Implement expect macro
Seems like there's no way around passing in self manually
2017-09-19 22:10:54 -07:00
greg 509ab80b9c I can now parse one thing 2017-09-19 22:10:54 -07:00
greg 247638c4db Get parsing REPL working 2017-09-19 22:10:54 -07:00
greg be98f8387e Add another test 2017-09-19 22:10:54 -07:00
greg 841b38d5b1 Add tokenization test 2017-09-19 22:10:53 -07:00
greg 16dfbb27d5 Use state features from simplerepl 2017-09-19 22:10:53 -07:00
greg 3af7e6a409 Refactoring Schala
Gonna work on Schala in earnest now! Using the simplerepl crate instead
of a build-in REPL, temporarily dropping parsing and evaluation code.
2017-09-19 22:10:53 -07:00
greg 123f388711 Rename language to "Schala" 2017-09-19 22:10:53 -07:00
greg bb349cda5f Fix newline tokenizing bug
Still need to fix <statements> parsing because of the final newline
2017-09-19 22:10:53 -07:00
greg caa331ecdc Read from file as well as repl 2017-09-19 22:10:53 -07:00
greg ae3a030ad8 Use iterative semantics for reduction
Becuase if you try to make reduce() recursive you blow out the stack.

Incidentally, "let a = 0; while a < 999999; let a = a + 1 end" is a neat
thing to try at this stage of the game
2017-09-19 22:10:53 -07:00
greg ddb09b453d Implement booleans 2017-09-19 22:10:53 -07:00
greg 9a4760d44f Implement while-reduction 2017-09-19 22:10:53 -07:00
greg eb5ce2ef9e Don't print newline for empty REPL result 2017-09-19 22:10:53 -07:00
greg b080ea7c81 Add if-statement evaluation 2017-09-19 22:10:53 -07:00
greg dffab8ae94 Fixed arg list parsing 2017-09-19 22:10:53 -07:00
greg 48ee6c9a75 Add function name to parse 2017-09-19 22:10:53 -07:00
greg 58d399dace More function parsing 2017-09-19 22:10:53 -07:00
greg 57ea1bae30 Preliminary addition of function blocks 2017-09-19 22:10:53 -07:00
greg e870d8172a Make = a keyword 2017-09-19 22:10:53 -07:00
greg 72b26755a7 Make ParseResult just a normal Result type
No reason for it to be different
2017-09-19 22:10:53 -07:00
greg 1416c9d444 Parse null as simple_expression 2017-09-19 22:10:53 -07:00
greg 5c79563e53 Reduce binop args before reducing full expr 2017-09-19 22:10:53 -07:00
greg c27c900e7f Add operators % and **
semantics for these are hard with floats handle later
2017-09-19 22:10:53 -07:00
greg 50c5dbe96d Evaluate simple binops
Woo this is kind of a neat milestone! I can now compute with this
language!
2017-09-19 22:10:53 -07:00
greg be8c8b3343 Add paren test 2017-09-19 22:10:53 -07:00
greg b856023072 Fixed paren parsing
also made error reporting a bit nicer
2017-09-19 22:10:53 -07:00
greg 868373f409 use match syntax in simple_parse 2017-09-19 22:10:53 -07:00
greg 582a7fd6dc Make parse and tokens optional 2017-09-19 22:10:53 -07:00
greg a947ec3cb2 Add simple parsing test 2017-09-19 22:10:53 -07:00
greg 79619025ea Add directive to print precedence chart 2017-09-19 22:10:53 -07:00
greg 56b338a6a8 Move to global precedence table 2017-09-19 22:10:53 -07:00
greg 7d6f946e22 Fill out a few more precedences 2017-09-19 22:10:53 -07:00
greg 0da7f7e3a1 Fully fixed binop parsing 2017-09-19 22:10:53 -07:00
greg 19a344fa77 Fixed precedent-less binop parsing 2017-09-19 22:10:53 -07:00
greg 626a7f3861 Working on binop parsing 2017-09-19 22:10:53 -07:00
greg 8e3a571d67 .env dirctive to display environment 2017-09-19 22:10:53 -07:00
greg f88f115567 Environment persistent across repl loop 2017-09-19 22:10:53 -07:00
greg 08f1092b69 Variable lookup works
Note this introduces a panic - if the AST node inserted into the
environment is not reduced, it throws an error when trying to look it up
2017-09-19 22:10:53 -07:00
greg 6ddea790c0 Beginning of variable lookup
everything is null
2017-09-19 22:10:53 -07:00
greg 6897eb1283 Implemented variable binding 2017-09-19 22:10:53 -07:00
greg 34fdf2be00 Add machinery for evaluation environments 2017-09-19 22:10:53 -07:00
greg 4ef93fafb5 Cause tokenize error for unclosed strings 2017-09-19 22:10:53 -07:00
greg d2108f0f97 First pass at evaluation
Very incomplete
2017-09-19 22:10:52 -07:00
greg 2989ac338c Implemented binop parsing
Uses Operator-precedence parsing algorithm, which I don't fully
understand.
2017-09-19 22:10:52 -07:00
greg 8b6d54aec2 Fix let clause parsing
let a = x, x should be expression not just simple expression
2017-09-19 22:10:52 -07:00
greg cdb47bb3b9 Add paren parsing 2017-09-19 22:10:52 -07:00
greg 9d6dc5a5f2 Tokenize periods separately 2017-09-19 22:10:52 -07:00
greg 71d2428e57 Update Grammar 2017-09-19 22:10:52 -07:00
greg 8f9bfbc5bd Rename rhs to simple_expression 2017-09-19 22:10:52 -07:00
greg c9fdd5e83c Simplified statements-parsing
Still a little wonky wrt extraneous Separators, need to adjust grammar
to fix I think
2017-09-19 22:10:52 -07:00
greg 30eddf7737 while statements 2017-09-19 22:10:52 -07:00
greg 42719dc2f2 Change 'input' to 'tokens'
just to be consistent
2017-09-19 22:10:52 -07:00
greg 8fcc850d77 Added else clause to if parsing 2017-09-19 22:10:52 -07:00
greg f421918945 basic if expression 2017-09-19 22:10:52 -07:00
greg 0e4469fa58 Filled out keyword tokenizing 2017-09-19 22:10:52 -07:00
greg edf100b583 Starting to do if statement parsing 2017-09-19 22:10:52 -07:00
greg 169e662049 Collapse Separator tokens
only ever gonna be one in a row
2017-09-19 22:10:52 -07:00
greg 46999beabf Added skeleton of expression() parser 2017-09-19 22:10:52 -07:00
greg 1342a76786 Added support for interpreter directives 2017-09-19 22:10:52 -07:00
greg 05238bced3 rhs production 2017-09-19 22:10:52 -07:00
greg a97cce184c Empty program is valid too 2017-09-19 22:10:52 -07:00
greg 1ae61287c1 Editing Grammar
to indicate what portion of it is now parsed
2017-09-19 22:10:52 -07:00
greg 329c521964 Parsing statement blocks works 2017-09-19 22:10:52 -07:00
greg bfa16fd6fb Added Keyword lexical class 2017-09-19 22:10:52 -07:00
greg 25f5188d8c Move definition around 2017-09-19 22:10:52 -07:00
greg 5213dd327f Change type to peekable 2017-09-19 22:10:52 -07:00
greg cea29094cd type alias for Tokens 2017-09-19 22:10:52 -07:00
greg 67eafba97a Put expect into early return macro 2017-09-19 22:10:52 -07:00
greg 1059a88ee6 Separate parsing into module 2017-09-19 22:10:52 -07:00
greg 429ace73bd Move tokenizing into separate module 2017-09-19 22:10:52 -07:00
greg 044e7a6a26 Rename ASTNode -> AST
saves typing
2017-09-19 22:10:52 -07:00
greg dbdae42c1b Add string to AST 2017-09-19 22:10:52 -07:00
greg fc3dcf792d Start writing recursive descent parser
I think I get the idea now
2017-09-19 22:10:52 -07:00
greg 02b34ca105 Wrote expect()
Hopefully correctly?
2017-09-19 22:10:52 -07:00
greg 1e9cd551a6 Add grammar of language to repo
Preliminary, work in progress
2017-09-19 22:10:52 -07:00
greg 9f4330889a Starting parsing work 2017-09-19 22:10:52 -07:00
greg 3058af4f05 Break on ctrl-D 2017-09-19 22:10:52 -07:00
greg 4f17d5a0dc Add number tokenizing 2017-09-19 22:10:52 -07:00
greg 8e3774ffca Comma as separate token 2017-09-19 22:10:52 -07:00
greg c6059ada7d Separators and parens
Separator = ; or \n, they are equivalent
2017-09-19 22:10:52 -07:00
greg b5ee45f639 null is also a keyword 2017-09-19 22:10:51 -07:00
greg 04f53b6beb Added some grammar sketch ideas to the readme 2017-09-19 22:10:51 -07:00
greg 2aaa600d53 More tokenizer stuff 2017-09-19 22:10:51 -07:00
greg c6a92728ee Scaffolding for evaluation function 2017-09-19 22:10:51 -07:00
greg b2e23bed86 Print tokens and parse 2017-09-19 22:10:51 -07:00
greg 3fdacf018e Basic repl 2017-09-19 22:10:51 -07:00
49 changed files with 8563 additions and 10 deletions

3
.gitignore vendored
View File

@ -1 +1,4 @@
Cargo.lock
target
.schala_repl
.schala_history

View File

@ -1,12 +1,18 @@
[package]
name = "null_only_language"
name = "schala"
version = "0.1.0"
authors = ["greg <greg.shuflin@protonmail.com>"]
[dependencies]
simplerepl = { path = "../simplerepl" }
llvm-sys = "*"
schala-repl = { path = "schala-repl" }
schala-codegen = { path = "schala-codegen" }
maaru-lang = { path = "maaru" }
rukka-lang = { path = "rukka" }
robo-lang = { path = "robo" }
schala-lang = { path = "schala-lang" }
[dependencies.iron_llvm]
git = "https://github.com/jauhien/iron-llvm.git"
[build-dependencies]
includedir_codegen = "0.2.0"
[workspace]

31
Grammar Normal file
View File

@ -0,0 +1,31 @@
<program> := <statements> EOF
<statements> := <statement>
| <statement> SEP <statements>
<statement> := let <id> = <expr>
| <expr>
| <fn_block>
<fn_block> := fn <id> ( <arg_list> ) <statements> end
<arg_list> := e
| <id>
| <id> , <arg_list>
<expr> := if <expr> then <statements> end
| if <expr> then <statements> else <statements> end
| while <expr> SEP <statements> end
| ( <expr> )
| <binop>
<binop> := <simple_expr>
| <simple_expr> <id> <binop>
<simple_expr> := <id>
| <number>
| <string>

4
README
View File

@ -1,4 +0,0 @@
No-runtime-value-error-language
A language wth a largely-python-like where there are no value errors. Can call null like a function

77
README.md Normal file
View File

@ -0,0 +1,77 @@
# Schala - a programming language meta-interpreter
Schala is a Rust framework written to make it easy to
create and experiment with toy programming languages. It provides
a common REPL, and a trait `ProgrammingLanguage` with provisions
for tokenizing text, parsing tokens, evaluating an abstract syntax tree,
and other tasks that are common to all programming languages.
Schala is implemented as a Rust library `schala_lib`, which provides a
`schala_main` function. This function serves as the main loop of the REPL, if run
interactively, or otherwise reads and interprets programming language source
files. It expects as input a vector of `PLIGenerator`, which is a type representing
a closure that returns a boxed trait object that implements the `ProgrammingLanguage` trait,
and stores any persistent state relevant to that programming language. The ability
to share state between different programming languages is in the works.
## About
Schala started out life as an experiment in writing a Javascript-like
programming language that would never encounter any kind of runtime value
error, but rather always return `null` under any kind of error condition. I had
seen one too many Javascript `Uncaught TypeError: Cannot read property ___ of
undefined` messages, and I was a bit frustrated. Plus I had always wanted to
write a programming langauge from scratch, and Rust is a fun language to
program in. Over time I became interested in playing around with other sorts
of programming languages as well, and wanted to make the process as general as
possible.
The name of the project comes from Schala the Princess of Zeal from the 1995
SNES RPG *Chrono Trigger*. I like classic JRPGs and enjoyed the thought of
creating a language name confusingly close to Scala. The naming scheme for
languages implemented with the Schala meta-interpreter is Chrono Trigger
characters.
Schala is incomplete alpha software and is not ready for public release.
## Languages implemented using the meta-interpreter
* The eponymous *Schala* language is an interpreted/compiled scripting langauge,
designed to be relatively simple, but with a reasonably sophisticated type
system.
* *Maaru* was the original Schala (since renamed to free up the name *Schala*
for the above language), a very simple dynamically-typed scripting language
such that all possible runtime errors result in null rather than program
failure.
* *Robo* is an experiment in creating a lazy, functional, strongly-typed language
much like Haskell
* *Rukka* is a straightforward LISP implementation
## Reference works
Here's a partial list of resources I've made use of in the process
of learning how to write a programming language.
### Type-checking
https://skillsmatter.com/skillscasts/10868-inside-the-rust-compiler
### Evaluation
*Understanding Computation*, Tom Stuart, O'Reilly 2013
*Basics of Compiler Design*, Torben Mogensen
### Parsing
http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
[Crafting Interpreters](http://www.craftinginterpreters.com/)
### LLVM
http://blog.ulysse.io/2016/07/03/llvm-getting-started.html
###Rust resources
https://thefullsnack.com/en/rust-for-the-web.html
https://rocket.rs/guide/getting-started/

64
TODO.md Normal file
View File

@ -0,0 +1,64 @@
# TODO Items
- sketch of an idea for the REPL:
-each compiler pass should be a (procedural?) macro like
compiler_pass!("parse", dataproducts: ["ast", "parse_tree"], {
match parsing::parse(INPUT) {
Ok(
PASS.add_artifact(
}
-should have an Idris-like `cast To From` function
- REPL:
- want to be able to do things like `:doc Identifier`, and have the language load up these definitions to the REPL
* change 'trait' to 'interface'
-think about idris-related ideas of multiple implementations of a type for an interface (+ vs * impl for monoids, for preorder/inorder/postorder for Foldable)
* Share state between programming languages
* idea for Schala - scoped types - be able to define a quick enum type scoped to a function ro something, that only is meant to be used as a quick bespoke interface between two other things
* another idea, allow:
type enum {
type enum MySubVariant {
SubVariant1, SubVariant2, etc.
}
Variant1(MySubVariant),
Variant2(...),
}
* idea for Schala: both currying *and* default arguments!
ex. fn a(b: Int, c:Int, d:Int = 1) -> Int
a(1,2) : Int
a(1,2,d=2): Int
a(_,1,3) : Int -> Int
a(1,2, c=_): Int -> Int
a(_,_,_) : Int -> Int -> Int -> Int
- AST : maybe replace the Expression type with "Ascription(TypeName, Box<Expression>) nodes??
- parser: add a "debug" field to the Parser struct for all debug-related things
-scala-style html"dfasfsadf${}" string interpolations!
*Compiler passes architecture
-ProgrammingLanguageInterface defines a evaluate_in_repl() and evaluate_no_repl() functions
-these take in a vec of CompilerPasses
struct CompilerPass {
name: String,
run: fn(PrevPass) -> NextPass
}
-change "Type...." names in parser.rs to "Anno..." for non-collision with names in typechecking.rs
-get rid of code pertaining to compilation specifically, have a more generation notion of "execution type"

11
maaru/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "maaru-lang"
version = "0.1.0"
authors = ["greg <greg.shuflin@protonmail.com>"]
[dependencies]
itertools = "0.5.8"
take_mut = "0.1.3"
llvm-sys = "*"
schala-repl = { path = "../schala-repl" }

279
maaru/src/compilation.rs Normal file
View File

@ -0,0 +1,279 @@
extern crate llvm_sys;
use std::collections::HashMap;
use self::llvm_sys::prelude::*;
use self::llvm_sys::{LLVMIntPredicate};
use parser::{AST, Statement, Function, Prototype, Expression, BinOp};
use schala_repl::LLVMCodeString;
use schala_repl::llvm_wrap as LLVMWrap;
type VariableMap = HashMap<String, LLVMValueRef>;
struct CompilationData {
context: LLVMContextRef,
module: LLVMModuleRef,
builder: LLVMBuilderRef,
variables: VariableMap,
main_function: LLVMValueRef,
current_function: Option<LLVMValueRef>,
}
pub fn compile_ast(ast: AST) -> LLVMCodeString {
println!("Compiling!");
let names: VariableMap = HashMap::new();
let context = LLVMWrap::create_context();
let module = LLVMWrap::module_create_with_name("example module");
let builder = LLVMWrap::CreateBuilderInContext(context);
let program_return_type = LLVMWrap::Int64TypeInContext(context);
let main_function_type = LLVMWrap::FunctionType(program_return_type, Vec::new(), false);
let main_function: LLVMValueRef = LLVMWrap::AddFunction(module, "main", main_function_type);
let mut data = CompilationData {
context: context,
builder: builder,
module: module,
variables: names,
main_function: main_function,
current_function: None,
};
let bb = LLVMWrap::AppendBasicBlockInContext(data.context, data.main_function, "entry");
LLVMWrap::PositionBuilderAtEnd(builder, bb);
let value = ast.codegen(&mut data);
LLVMWrap::BuildRet(builder, value);
let ret = LLVMWrap::PrintModuleToString(module);
// Clean up. Values created in the context mostly get cleaned up there.
LLVMWrap::DisposeBuilder(builder);
LLVMWrap::DisposeModule(module);
LLVMWrap::ContextDispose(context);
LLVMCodeString(ret)
}
trait CodeGen {
fn codegen(&self, &mut CompilationData) -> LLVMValueRef;
}
impl CodeGen for AST {
fn codegen(&self, data: &mut CompilationData) -> LLVMValueRef {
let int_type = LLVMWrap::Int64TypeInContext(data.context);
let mut ret = LLVMWrap::ConstInt(int_type, 0, false);
for statement in self {
ret = statement.codegen(data);
}
ret
}
}
impl CodeGen for Statement {
fn codegen(&self, data: &mut CompilationData) -> LLVMValueRef {
use self::Statement::*;
match self {
&ExprNode(ref expr) => expr.codegen(data),
&FuncDefNode(ref func) => func.codegen(data),
}
}
}
impl CodeGen for Function {
fn codegen(&self, data: &mut CompilationData) -> LLVMValueRef {
/* should have a check here for function already being defined */
let function = self.prototype.codegen(data);
let ref body = self.body;
data.current_function = Some(function);
let return_type = LLVMWrap::Int64TypeInContext(data.context);
let mut ret = LLVMWrap::ConstInt(return_type, 0, false);
let block = LLVMWrap::AppendBasicBlockInContext(data.context, function, "entry");
LLVMWrap::PositionBuilderAtEnd(data.builder, block);
//insert function params into variables
for value in LLVMWrap::GetParams(function) {
let name = LLVMWrap::GetValueName(value);
data.variables.insert(name, value);
}
for expr in body {
ret = expr.codegen(data);
}
LLVMWrap::BuildRet(data.builder, ret);
// get basic block of main
let main_bb = LLVMWrap::GetBasicBlocks(data.main_function).get(0).expect("Couldn't get first block of main").clone();
LLVMWrap::PositionBuilderAtEnd(data.builder, main_bb);
data.current_function = None;
ret
}
}
impl CodeGen for Prototype {
fn codegen(&self, data: &mut CompilationData) -> LLVMValueRef {
let num_args = self.parameters.len();
let return_type = LLVMWrap::Int64TypeInContext(data.context);
let mut arguments: Vec<LLVMTypeRef> = vec![];
for _ in 0..num_args {
arguments.push(LLVMWrap::Int64TypeInContext(data.context));
}
let function_type =
LLVMWrap::FunctionType(return_type,
arguments,
false);
let function = LLVMWrap::AddFunction(data.module,
&*self.name,
function_type);
let function_params = LLVMWrap::GetParams(function);
for (index, param) in function_params.iter().enumerate() {
let name = self.parameters.get(index).expect(&format!("Failed this check at index {}", index));
let new = *param;
LLVMWrap::SetValueName(new, name);
}
function
}
}
impl CodeGen for Expression {
fn codegen(&self, data: &mut CompilationData) -> LLVMValueRef {
use self::BinOp::*;
use self::Expression::*;
let int_type = LLVMWrap::Int64TypeInContext(data.context);
let zero = LLVMWrap::ConstInt(int_type, 0, false);
match *self {
Variable(ref name) => *data.variables.get(&**name).expect(&format!("Can't find variable {}", name)),
BinExp(Assign, ref left, ref right) => {
if let Variable(ref name) = **left {
let new_value = right.codegen(data);
data.variables.insert((**name).clone(), new_value);
new_value
} else {
panic!("Bad variable assignment")
}
}
BinExp(ref op, ref left, ref right) => {
let lhs = left.codegen(data);
let rhs = right.codegen(data);
op.codegen_with_ops(data, lhs, rhs)
}
Number(ref n) => {
let native_val = *n as u64;
let int_value: LLVMValueRef = LLVMWrap::ConstInt(int_type, native_val, false);
int_value
}
Conditional(ref test, ref then_expr, ref else_expr) => {
let condition_value = test.codegen(data);
let is_nonzero =
LLVMWrap::BuildICmp(data.builder,
LLVMIntPredicate::LLVMIntNE,
condition_value,
zero,
"ifcond");
let func = LLVMWrap::GetBasicBlockParent(LLVMWrap::GetInsertBlock(data.builder));
let mut then_block =
LLVMWrap::AppendBasicBlockInContext(data.context, func, "then_block");
let mut else_block =
LLVMWrap::AppendBasicBlockInContext(data.context, func, "else_block");
let merge_block =
LLVMWrap::AppendBasicBlockInContext(data.context, func, "ifcont");
// add conditional branch to ifcond block
LLVMWrap::BuildCondBr(data.builder, is_nonzero, then_block, else_block);
// start inserting into then block
LLVMWrap::PositionBuilderAtEnd(data.builder, then_block);
// then-block codegen
let then_return = then_expr.codegen(data);
LLVMWrap::BuildBr(data.builder, merge_block);
// update then block b/c recursive codegen() call may have changed the notion of
// the current block
then_block = LLVMWrap::GetInsertBlock(data.builder);
// then do the same stuff again for the else branch
//
LLVMWrap::PositionBuilderAtEnd(data.builder, else_block);
let else_return = match *else_expr {
Some(ref e) => e.codegen(data),
None => zero,
};
LLVMWrap::BuildBr(data.builder, merge_block);
else_block = LLVMWrap::GetInsertBlock(data.builder);
LLVMWrap::PositionBuilderAtEnd(data.builder, merge_block);
let phi = LLVMWrap::BuildPhi(data.builder, int_type, "phinode");
let values = vec![then_return, else_return];
let blocks = vec![then_block, else_block];
LLVMWrap::AddIncoming(phi, values, blocks);
phi
}
Block(ref exprs) => {
let mut ret = zero;
for e in exprs.iter() {
ret = e.codegen(data);
}
ret
}
ref e => {
println!("Unimplemented {:?}", e);
unimplemented!()
}
}
}
}
impl BinOp {
fn codegen_with_ops(&self, data: &CompilationData, lhs: LLVMValueRef, rhs: LLVMValueRef) -> LLVMValueRef {
use self::BinOp::*;
macro_rules! simple_binop {
($fnname: expr, $name: expr) => {
$fnname(data.builder, lhs, rhs, $name)
}
}
let int_type = LLVMWrap::Int64TypeInContext(data.context);
match *self {
Add => simple_binop!(LLVMWrap::BuildAdd, "addtemp"),
Sub => simple_binop!(LLVMWrap::BuildSub, "subtemp"),
Mul => simple_binop!(LLVMWrap::BuildMul, "multemp"),
Div => simple_binop!(LLVMWrap::BuildUDiv, "divtemp"),
Mod => simple_binop!(LLVMWrap::BuildSRem, "remtemp"),
Less => {
let pred: LLVMValueRef =
LLVMWrap::BuildICmp(data.builder, LLVMIntPredicate::LLVMIntULT, lhs, rhs, "tmp");
LLVMWrap::BuildZExt(data.builder, pred, int_type, "temp")
}
Greater => {
let pred: LLVMValueRef =
LLVMWrap::BuildICmp(data.builder, LLVMIntPredicate::LLVMIntUGT, lhs, rhs, "tmp");
LLVMWrap::BuildZExt(data.builder, pred, int_type, "temp")
}
ref unknown => panic!("Bad operator {:?}", unknown),
}
}
}

481
maaru/src/eval.rs Normal file
View File

@ -0,0 +1,481 @@
extern crate take_mut;
use std::collections::HashMap;
use std::collections::VecDeque;
use parser::{AST, Statement, Expression, Function, Callable, BinOp};
use std::rc::Rc;
use std::io::{Write, Stdout, BufWriter};
use std::convert::From;
use parser::Expression::*;
use parser::Statement::*;
type Reduction<T> = (T, Option<SideEffect>);
#[derive(Debug, Clone)]
enum ReducedValue {
StringLiteral(Rc<String>),
ListLiteral(VecDeque<Expression>),
StructLiteral(VecDeque<(Rc<String>, Expression)>),
Number(f64),
Lambda(Function),
}
impl From<ReducedValue> for Expression {
fn from(rv: ReducedValue) -> Expression {
match rv {
ReducedValue::Number(n) => Expression::Number(n),
ReducedValue::StringLiteral(n) => Expression::StringLiteral(n),
ReducedValue::Lambda(f) => Expression::Lambda(f),
ReducedValue::ListLiteral(items) => Expression::ListLiteral(items),
ReducedValue::StructLiteral(items) => Expression::StructLiteral(items),
}
}
}
impl From<Expression> for ReducedValue {
fn from(rv: Expression) -> ReducedValue {
match rv {
Expression::Number(n) => ReducedValue::Number(n),
Expression::StringLiteral(n) => ReducedValue::StringLiteral(n),
Expression::Lambda(f) => ReducedValue::Lambda(f),
Expression::ListLiteral(items) => ReducedValue::ListLiteral(items),
Expression::StructLiteral(items) => ReducedValue::StructLiteral(items),
_ => panic!("trying to store a non-fully-reduced variable"),
}
}
}
fn get_indexer(f: f64) -> Option<usize> {
if f.fract() == 0.0 {
if f.trunc() >= 0.0 {
return Some(f.trunc() as usize);
}
}
None
}
#[derive(Debug)]
enum SideEffect {
Print(String),
AddBinding(Rc<String>, ReducedValue),
}
pub struct Evaluator<'a> {
parent: Option<&'a Evaluator<'a>>,
variables: HashMap<String, ReducedValue>,
stdout: BufWriter<Stdout>,
pub trace_evaluation: bool,
}
impl<'a> Evaluator<'a> {
pub fn new(parent: Option<&'a Evaluator>) -> Evaluator<'a> {
Evaluator {
variables: HashMap::new(),
parent: parent,
stdout: BufWriter::new(::std::io::stdout()),
trace_evaluation: parent.map_or(false, |e| e.trace_evaluation),
}
}
pub fn run(&mut self, ast: AST) -> Vec<String> {
ast.into_iter()
.map(|astnode| format!("{}", self.reduction_loop(astnode)))
.collect()
}
fn add_binding(&mut self, var: String, value: ReducedValue) {
self.variables.insert(var, value);
}
fn lookup_binding(&self, var: &str) -> Option<ReducedValue> {
match self.variables.get(var) {
Some(expr) => Some(expr.clone()),
None => match self.parent {
Some(env) => env.lookup_binding(var),
None => None
}
}
}
}
trait Evaluable {
fn is_reducible(&self) -> bool;
}
impl Evaluable for Statement {
fn is_reducible(&self) -> bool {
match self {
&ExprNode(ref expr) => expr.is_reducible(),
&FuncDefNode(_) => true,
}
}
}
impl Evaluable for Expression {
fn is_reducible(&self) -> bool {
match *self {
Null => false,
StringLiteral(_) => false,
Lambda(_) => false,
Number(_) => false,
ListLiteral(ref items) => {
items.iter().any(|x| x.is_reducible())
}
StructLiteral(ref items) => {
items.iter().any(|pair| pair.1.is_reducible())
}
_ => true,
}
}
}
impl Expression {
fn is_truthy(&self) -> bool {
match *self {
Null => false,
StringLiteral(ref s) if **s == "" => false,
Number(n) if n == 0.0 => false,
_ => true,
}
}
}
fn is_assignment(op: &BinOp) -> bool {
use self::BinOp::*;
match *op {
Assign | AddAssign | SubAssign |
MulAssign | DivAssign => true,
_ => false,
}
}
impl<'a> Evaluator<'a> {
fn reduction_loop(&mut self, mut node: Statement) -> Statement {
loop {
node = self.step(node);
if !node.is_reducible() {
break;
}
}
node
}
fn step(&mut self, node: Statement) -> Statement {
let mut trace = String::new();
if self.trace_evaluation {
trace.push_str(&format!("Step: {:?}", node));
}
let (new_node, side_effect) = self.reduce_astnode(node);
if self.trace_evaluation {
trace.push_str(&format!("{:?}", new_node));
}
if let Some(s) = side_effect {
if self.trace_evaluation {
trace.push_str(&format!(" | side-effect: {:?}", s));
}
self.perform_side_effect(s);
}
if self.trace_evaluation {
println!("{}", trace);
}
new_node
}
fn perform_side_effect(&mut self, side_effect: SideEffect) {
use self::SideEffect::*;
match side_effect {
Print(s) => {
write!(self.stdout, "{}\n", s).unwrap();
match self.stdout.flush() {
Ok(_) => (),
Err(_) => println!("Could not flush stdout"),
};
}
AddBinding(var, value) => {
self.add_binding((*var).clone(), value);
},
}
}
fn reduce_astnode(&mut self, node: Statement) -> Reduction<Statement> {
match node {
ExprNode(expr) => {
if expr.is_reducible() {
let (new_expr, side_effect) = self.reduce_expr(expr);
(ExprNode(new_expr), side_effect)
} else {
(ExprNode(expr), None)
}
}
FuncDefNode(func) => {
let name = func.prototype.name.clone();
let reduced_value = ReducedValue::Lambda(func.clone());
let binding = Some(SideEffect::AddBinding(name, reduced_value));
(ExprNode(Expression::Lambda(func)), binding)
}
}
}
//TODO I probably want another Expression variant that holds a ReducedValue
fn reduce_expr(&mut self, expression: Expression) -> Reduction<Expression> {
match expression {
Null => (Null, None),
e @ StringLiteral(_) => (e, None),
e @ Number(_) => (e, None),
e @ Lambda(_) => (e, None),
Variable(ref var) => {
match self.lookup_binding(var).map(|x| x.into()) {
None => (Null, None),
Some(expr) => (expr, None),
}
}
BinExp(op, mut left, mut right) => {
if right.is_reducible() {
let mut side_effect = None;
take_mut::take(right.as_mut(), |expr| { let (a, b) = self.reduce_expr(expr); side_effect = b; a});
return (BinExp(op, left, right), side_effect);
}
if let BinOp::Assign = op {
return match *left {
Variable(var) => {
let reduced_value: ReducedValue = ReducedValue::from(*right);
let binding = SideEffect::AddBinding(var, reduced_value);
(Null, Some(binding))
},
_ => (Null, None)
};
}
if is_assignment(&op) {
use self::BinOp::*;
let new_op = match op {
AddAssign => Add,
SubAssign => Sub,
MulAssign => Mul,
DivAssign => Div,
_ => unreachable!(),
};
let reduction =
BinExp(BinOp::Assign,
Box::new(*left.clone()),
Box::new(BinExp(new_op, left, right))
);
return (reduction, None);
}
if left.is_reducible() {
let mut side_effect = None;
take_mut::take(left.as_mut(), |expr| { let (a, b) = self.reduce_expr(expr); side_effect = b; a});
(BinExp(op, left, right), side_effect)
} else {
(self.reduce_binop(op, *left, *right), None) //can assume both arguments are maximally reduced
}
}
Call(callable, mut args) => {
let mut f = true;
for arg in args.iter_mut() {
if arg.is_reducible() {
take_mut::take(arg, |arg| self.reduce_expr(arg).0);
f = false;
break;
}
}
if f {
self.reduce_call(callable, args)
} else {
(Call(callable, args), None)
}
}
While(test, body) => {
let mut block = VecDeque::from(body.clone());
block.push_back(While(test.clone(), body.clone()));
let reduction = Conditional(test, Box::new(Block(block)), None);
(reduction, None)
}
Conditional(box test, then_block, else_block) => {
if test.is_reducible() {
let (new_test, new_effect) = self.reduce_expr(test);
(Conditional(Box::new(new_test), then_block, else_block), new_effect)
} else {
if test.is_truthy() {
(*then_block, None)
} else {
match else_block {
Some(box expr) => (expr, None),
None => (Null, None),
}
}
}
}
Block(mut exprs) => {
let first = exprs.pop_front();
match first {
None => (Null, None),
Some(expr) => {
if exprs.len() == 0 {
(expr, None)
} else {
if expr.is_reducible() {
let (new, side_effect) = self.reduce_expr(expr);
exprs.push_front(new);
(Block(exprs), side_effect)
} else {
(Block(exprs), None)
}
}
}
}
}
Index(mut expr, mut index_expr) => {
if index_expr.is_reducible() {
let mut side_effect = None;
take_mut::take(index_expr.as_mut(), |expr| { let (a, b) = self.reduce_expr(expr); side_effect = b; a});
return (Index(expr, index_expr), side_effect)
}
if expr.is_reducible() {
let mut side_effect = None;
take_mut::take(expr.as_mut(), |expr| { let (a, b) = self.reduce_expr(expr); side_effect = b; a});
return (Index(expr, index_expr), side_effect);
}
match (*expr, *index_expr) {
(ListLiteral(list_items), Number(n)) => {
let indexed_expr = get_indexer(n).and_then(|i| list_items.get(i));
if let Some(e) = indexed_expr {
(e.clone(), None)
} else {
(Null, None)
}
}
(StructLiteral(items), StringLiteral(s)) => {
for item in items {
if s == item.0 {
return (item.1.clone(), None); //TODO this is hella inefficient
}
}
(Null, None)
},
_ => (Null, None)
}
}
ListLiteral(mut exprs) => {
let mut side_effect = None;
for expr in exprs.iter_mut() {
if expr.is_reducible() {
take_mut::take(expr, |expr| {
let (a, b) = self.reduce_expr(expr);
side_effect = b;
a
});
break;
}
}
(ListLiteral(exprs), side_effect)
},
StructLiteral(mut items) => {
let mut side_effect = None;
for pair in items.iter_mut() {
if pair.1.is_reducible() {
take_mut::take(pair, |pair| {
let (name, expr) = pair;
let (a, b) = self.reduce_expr(expr);
side_effect = b;
(name, a)
});
break;
}
}
(StructLiteral(items), side_effect)
}
}
}
fn reduce_binop(&mut self, op: BinOp, left: Expression, right: Expression) -> Expression {
use self::BinOp::*;
let truthy = Number(1.0);
let falsy = Null;
match (op, left, right) {
(Add, Number(l), Number(r)) => Number(l + r),
(Add, StringLiteral(s1), StringLiteral(s2)) => StringLiteral(Rc::new(format!("{}{}", *s1, *s2))),
(Add, StringLiteral(s1), Number(r)) => StringLiteral(Rc::new(format!("{}{}", *s1, r))),
(Add, Number(l), StringLiteral(s1)) => StringLiteral(Rc::new(format!("{}{}", l, *s1))),
(Sub, Number(l), Number(r)) => Number(l - r),
(Mul, Number(l), Number(r)) => Number(l * r),
(Div, Number(l), Number(r)) if r != 0.0 => Number(l / r),
(Mod, Number(l), Number(r)) => Number(l % r),
(Less, Number(l), Number(r)) => if l < r { truthy } else { falsy },
(LessEq, Number(l), Number(r)) => if l <= r { truthy } else { falsy },
(Greater, Number(l), Number(r)) => if l > r { truthy } else { falsy },
(GreaterEq, Number(l), Number(r)) => if l >= r { truthy } else { falsy },
(Equal, Number(l), Number(r)) => if l == r { truthy } else { falsy },
(Equal, Null, Null) => truthy,
(Equal, StringLiteral(s1), StringLiteral(s2)) => if s1 == s2 { truthy } else { falsy },
(Equal, _, _) => falsy,
_ => falsy,
}
}
fn reduce_call(&mut self, callable: Callable, arguments: Vec<Expression>) -> Reduction<Expression> {
if let Some(res) = handle_builtin(&callable, &arguments) {
return res;
}
let function = match callable {
Callable::Lambda(func) => func.clone(),
Callable::NamedFunction(name) => {
match self.lookup_binding(&*name) {
Some(ReducedValue::Lambda(func)) => func,
_ => return (Null, None),
}
}
};
if function.prototype.parameters.len() != arguments.len() {
return (Null, None);
}
let mut evaluator = Evaluator::new(Some(self));
for (binding, expr) in function.prototype.parameters.iter().zip(arguments.iter()) {
evaluator.add_binding((**binding).clone(), expr.clone().into());
}
let nodes = function.body.iter().map(|node| node.clone());
let mut retval = ExprNode(Null);
for n in nodes {
retval = evaluator.reduction_loop(n);
}
match retval {
ExprNode(expr) => (expr, None),
FuncDefNode(_) => panic!("This should never happen! A maximally-reduced node\
should never be a function definition!")
}
}
}
fn handle_builtin(callable: &Callable, arguments: &Vec<Expression>) -> Option<Reduction<Expression>> {
let name: &str = match *callable {
Callable::NamedFunction(ref name) => *&name,
_ => return None,
};
match name {
"print" => {
let mut s = String::new();
for arg in arguments {
s.push_str(&format!("{} ", arg));
}
return Some((Null, Some(SideEffect::Print(s))));
},
_ => None
}
}

106
maaru/src/lib.rs Normal file
View File

@ -0,0 +1,106 @@
#![feature(box_patterns)]
extern crate schala_repl;
mod tokenizer;
mod parser;
mod eval;
mod compilation;
use schala_repl::{ProgrammingLanguageInterface, EvalOptions, LanguageOutput, TraceArtifact};
#[derive(Debug)]
pub struct TokenError {
pub msg: String,
}
impl TokenError {
pub fn new(msg: &str) -> TokenError {
TokenError { msg: msg.to_string() }
}
}
pub use self::eval::Evaluator as MaaruEvaluator;
pub struct Maaru<'a> {
evaluator: MaaruEvaluator<'a>
}
impl<'a> Maaru<'a> {
pub fn new() -> Maaru<'a> {
Maaru {
evaluator: MaaruEvaluator::new(None),
}
}
}
impl<'a> ProgrammingLanguageInterface for Maaru<'a> {
fn get_language_name(&self) -> String {
"Maaru".to_string()
}
fn get_source_file_suffix(&self) -> String {
format!("maaru")
}
fn evaluate_in_repl(&mut self, input: &str, options: &EvalOptions) -> LanguageOutput {
let mut output = LanguageOutput::default();
let tokens = match tokenizer::tokenize(input) {
Ok(tokens) => {
if options.debug.tokens {
output.add_artifact(TraceArtifact::new("tokens", format!("{:?}", tokens)));
}
tokens
},
Err(err) => {
output.add_output(format!("Tokenization error: {:?}\n", err.msg));
return output;
}
};
let ast = match parser::parse(&tokens, &[]) {
Ok(ast) => {
if options.debug.ast {
output.add_artifact(TraceArtifact::new("ast", format!("{:?}", ast)));
}
ast
},
Err(err) => {
output.add_output(format!("Parse error: {:?}\n", err.msg));
return output;
}
};
let mut evaluation_output = String::new();
for s in self.evaluator.run(ast).iter() {
evaluation_output.push_str(s);
}
output.add_output(evaluation_output);
return output;
}
/* TODO make this work with new framework */
/*
fn can_compile(&self) -> bool {
true
}
fn compile(&mut self, input: &str) -> LLVMCodeString {
let tokens = match tokenizer::tokenize(input) {
Ok(tokens) => tokens,
Err(err) => {
let msg = format!("Tokenization error: {:?}\n", err.msg);
panic!("{}", msg);
}
};
let ast = match parser::parse(&tokens, &[]) {
Ok(ast) => ast,
Err(err) => {
let msg = format!("Parse error: {:?}\n", err.msg);
panic!("{}", msg);
}
};
compilation::compile_ast(ast)
}
*/
}

755
maaru/src/parser.rs Normal file
View File

@ -0,0 +1,755 @@
use tokenizer::{Token, Kw, OpTok};
use tokenizer::Token::*;
use std::fmt;
use std::collections::VecDeque;
use std::rc::Rc;
use std::convert::From;
// Grammar
// program := (statement delimiter ?)*
// delimiter := Newline | Semicolon
// statement := declaration | expression
// declaration := FN prototype LCurlyBrace (statement)* RCurlyBrace
// prototype := identifier LParen identlist RParen
// identlist := Ident (Comma Ident)* | ε
// exprlist := Expression (Comma Expression)* | ε
// itemlist := Ident COLON Expression (Comma Ident COLON Expression)* | ε
//
// expression := postop_expression (op postop_expression)*
// postop_expression := primary_expression postop
// primary_expression := number_expr | String | identifier_expr | paren_expr | conditional_expr | while_expr | lambda_expr | list_expr | struct_expr
// number_expr := (PLUS | MINUS ) number_expr | Number
// identifier_expr := call_expression | Variable
// list_expr := LSquareBracket exprlist RSquareBracket
// struct_expr := LCurlyBrace itemlist RCurlyBrace
// call_expression := Identifier LParen exprlist RParen
// while_expr := WHILE primary_expression LCurlyBrace (expression delimiter)* RCurlyBrace
// paren_expr := LParen expression RParen
// conditional_expr := IF expression LCurlyBrace (expression delimiter)* RCurlyBrace (LCurlyBrace (expresion delimiter)* RCurlyBrace)?
// lambda_expr := FN LParen identlist RParen LCurlyBrace (expression delimiter)* RCurlyBrace
// lambda_call := | LParen exprlist RParen
// postop := ε | LParen exprlist RParen | LBracket expression RBracket
// op := '+', '-', etc.
//
pub type AST = Vec<Statement>;
#[derive(Debug, Clone)]
pub enum Statement {
ExprNode(Expression),
FuncDefNode(Function),
}
impl fmt::Display for Statement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Statement::*;
match *self {
ExprNode(ref expr) => write!(f, "{}", expr),
FuncDefNode(_) => write!(f, "UNIMPLEMENTED"),
}
}
}
#[derive(Debug, Clone)]
pub struct Function {
pub prototype: Prototype,
pub body: Vec<Statement>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Prototype {
pub name: Rc<String>,
pub parameters: Vec<Rc<String>>,
}
#[derive(Debug, Clone)]
pub enum Expression {
Null,
StringLiteral(Rc<String>),
Number(f64),
Variable(Rc<String>),
BinExp(BinOp, Box<Expression>, Box<Expression>),
Call(Callable, Vec<Expression>),
Conditional(Box<Expression>, Box<Expression>, Option<Box<Expression>>),
Lambda(Function),
Block(VecDeque<Expression>),
While(Box<Expression>, Vec<Expression>),
Index(Box<Expression>, Box<Expression>),
ListLiteral(VecDeque<Expression>),
StructLiteral(VecDeque<(Rc<String>, Expression)>),
}
#[derive(Clone, Debug)]
pub enum Callable {
NamedFunction(Rc<String>),
Lambda(Function),
}
//TODO this ought to be ReducedExpression
impl fmt::Display for Expression {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Expression::*;
match *self {
Null => write!(f, "null"),
StringLiteral(ref s) => write!(f, "\"{}\"", s),
Number(n) => write!(f, "{}", n),
Lambda(Function { prototype: Prototype { ref name, ref parameters, .. }, .. }) => {
write!(f, "«function: {}, {} arg(s)»", name, parameters.len())
}
ListLiteral(ref items) => {
write!(f, "[ ")?;
let mut iter = items.iter().peekable();
while let Some(item) = iter.next() {
write!(f, "{}", item)?;
if let Some(_) = iter.peek() {
write!(f, ", ")?;
}
}
write!(f, " ]")
}
StructLiteral(ref items) => {
write!(f, "{} ", "{")?;
let mut iter = items.iter().peekable();
while let Some(pair) = iter.next() {
write!(f, "{}: {}", pair.0, pair.1)?;
if let Some(_) = iter.peek() {
write!(f, ", ")?;
}
}
write!(f, "{} ", "}")
}
_ => write!(f, "UNIMPLEMENTED"),
}
}
}
#[derive(Debug, Clone)]
pub enum BinOp {
Add,
AddAssign,
Sub,
SubAssign,
Mul,
MulAssign,
Div,
DivAssign,
Mod,
Less,
LessEq,
Greater,
GreaterEq,
Equal,
Assign,
Custom(String),
}
impl From<OpTok> for BinOp {
fn from(token: OpTok) -> BinOp {
use self::BinOp::*;
match &token.0[..] {
"+" => Add,
"+=" => AddAssign,
"-" => Sub,
"-=" => SubAssign,
"*" => Mul,
"*=" => MulAssign,
"/" => Div,
"/=" => DivAssign,
"%" => Mod,
"<" => Less,
"<=" => LessEq,
">" => Greater,
">=" => GreaterEq,
"==" => Equal,
"=" => Assign,
op => Custom(op.to_string()),
}
}
}
type Precedence = u8;
// TODO make this support incomplete parses
pub type ParseResult<T> = Result<T, ParseError>;
#[derive(Debug)]
pub struct ParseError {
pub msg: String,
pub remaining_tokens: Vec<Token>,
}
impl ParseError {
fn result_from_str<T>(msg: &str) -> ParseResult<T> {
Err(ParseError {
msg: msg.to_string(),
remaining_tokens: vec![],
})
}
}
struct Parser {
tokens: Vec<Token>,
}
impl Parser {
fn initialize(tokens: &[Token]) -> Parser {
let mut tokens = tokens.to_vec();
tokens.reverse();
Parser { tokens: tokens }
}
fn peek(&self) -> Option<Token> {
self.tokens.last().map(|x| x.clone())
}
fn next(&mut self) -> Option<Token> {
self.tokens.pop()
}
fn get_precedence(&self, op: &OpTok) -> Precedence {
match &op.0[..] {
"+" => 10,
"-" => 10,
"*" => 20,
"/" => 20,
"%" => 20,
"==" => 40,
"=" | "+=" | "-=" | "*=" | "/=" => 1,
">" | ">=" | "<" | "<=" => 30,
_ => 255,
}
}
}
macro_rules! expect {
($self_:expr, $token:pat) => {
match $self_.peek() {
Some($token) => {$self_.next();},
Some(x) => {
let err = format!("Expected `{:?}` but got `{:?}`", stringify!($token), x);
return ParseError::result_from_str(&err)
},
None => {
let err = format!("Expected `{:?}` but got end of input", stringify!($token));
return ParseError::result_from_str(&err) //TODO make this not require 2 stringifications
}
}
}
}
macro_rules! expect_identifier {
($self_:expr) => {
match $self_.peek() {
Some(Identifier(s)) => {$self_.next(); s},
Some(x) => return ParseError::result_from_str(&format!("Expected identifier, but got {:?}", x)),
None => return ParseError::result_from_str("Expected identifier, but got end of input"),
}
}
}
macro_rules! skip_whitespace {
($_self: expr) => {
loop {
match $_self.peek() {
Some(ref t) if is_delimiter(t) => {
$_self.next();
continue;
}
_ => break,
}
}
}
}
macro_rules! delimiter_block {
($_self: expr, $try_parse: ident, $($break_pattern: pat)|+) => {
{
let mut acc = Vec::new();
loop {
match $_self.peek() {
None => break,
Some(ref t) if is_delimiter(t) => { $_self.next(); continue; },
$($break_pattern)|+ => break,
_ => {
let a = try!($_self.$try_parse());
acc.push(a);
}
}
}
acc
}
}
}
fn is_delimiter(token: &Token) -> bool {
match *token {
Newline | Semicolon => true,
_ => false,
}
}
impl Parser {
fn program(&mut self) -> ParseResult<AST> {
let mut ast = Vec::new(); //TODO have this come from previously-parsed tree
loop {
let result: ParseResult<Statement> = match self.peek() {
Some(ref t) if is_delimiter(t) => {
self.next();
continue;
}
Some(_) => self.statement(),
None => break,
};
match result {
Ok(node) => ast.push(node),
Err(mut err) => {
err.remaining_tokens = self.tokens.clone();
err.remaining_tokens.reverse();
return Err(err);
}
}
}
Ok(ast)
}
fn statement(&mut self) -> ParseResult<Statement> {
let node: Statement = match self.peek() {
Some(Keyword(Kw::Fn)) => self.declaration()?,
Some(_) => Statement::ExprNode(self.expression()?),
None => panic!("Unexpected end of tokens"),
};
Ok(node)
}
fn declaration(&mut self) -> ParseResult<Statement> {
expect!(self, Keyword(Kw::Fn));
let prototype = self.prototype()?;
expect!(self, LCurlyBrace);
let body = self.body()?;
expect!(self, RCurlyBrace);
Ok(Statement::FuncDefNode(Function {
prototype: prototype,
body: body,
}))
}
fn prototype(&mut self) -> ParseResult<Prototype> {
let name = expect_identifier!(self);
expect!(self, LParen);
let parameters = self.identlist()?;
expect!(self, RParen);
Ok(Prototype {
name: name,
parameters: parameters,
})
}
fn identlist(&mut self) -> ParseResult<Vec<Rc<String>>> {
let mut args = Vec::new();
while let Some(Identifier(name)) = self.peek() {
args.push(name.clone());
self.next();
match self.peek() {
Some(Comma) => {self.next();},
_ => break,
}
}
Ok(args)
}
fn exprlist(&mut self) -> ParseResult<Vec<Expression>> {
let mut exprs = Vec::new();
loop {
if let Some(RParen) = self.peek() {
break;
}
let exp = self.expression()?;
exprs.push(exp);
match self.peek() {
Some(Comma) => {self.next();},
_ => break,
}
}
Ok(exprs)
}
fn itemlist(&mut self) -> ParseResult<VecDeque<(Rc<String>, Expression)>> {
let mut items = VecDeque::new();
loop {
if let Some(RCurlyBrace) = self.peek() {
break;
}
let name = expect_identifier!(self);
expect!(self, Colon);
let expr = self.expression()?;
items.push_back((name, expr));
match self.peek() {
Some(Comma) => {self.next();},
_ => break,
};
}
Ok(items)
}
fn body(&mut self) -> ParseResult<Vec<Statement>> {
let statements = delimiter_block!(
self,
statement,
Some(RCurlyBrace)
);
Ok(statements)
}
fn expression(&mut self) -> ParseResult<Expression> {
let lhs: Expression = self.postop_expression()?;
self.precedence_expr(lhs, 0)
}
fn precedence_expr(&mut self,
mut lhs: Expression,
min_precedence: u8)
-> ParseResult<Expression> {
while let Some(Operator(op)) = self.peek() {
let precedence = self.get_precedence(&op);
if precedence < min_precedence {
break;
}
self.next();
let mut rhs = self.postop_expression()?;
while let Some(Operator(ref op)) = self.peek() {
if self.get_precedence(op) > precedence {
let new_prec = self.get_precedence(op);
rhs = self.precedence_expr(rhs, new_prec)?;
} else {
break;
}
}
lhs = Expression::BinExp(op.into(), Box::new(lhs), Box::new(rhs));
}
Ok(lhs)
}
fn postop_expression(&mut self) -> ParseResult<Expression> {
use self::Expression::*;
let expr = self.primary_expression()?;
let ret = match self.peek() {
Some(LParen) => {
let args = self.call_expression()?;
match expr {
Lambda(f) => Call(Callable::Lambda(f), args),
e => {
let err = format!("Expected lambda expression before a call, got {:?}", e);
return ParseError::result_from_str(&err);
},
}
},
Some(LSquareBracket) => {
expect!(self, LSquareBracket);
let index_expr = self.expression()?;
expect!(self, RSquareBracket);
Index(Box::new(expr), Box::new(index_expr))
},
_ => {
expr
}
};
Ok(ret)
}
fn primary_expression(&mut self) -> ParseResult<Expression> {
Ok(match self.peek() {
Some(Keyword(Kw::Null)) => {
self.next();
Expression::Null
}
Some(NumLiteral(_)) => self.number_expression()?,
Some(Operator(OpTok(ref a))) if **a == "+" || **a == "-" => self.number_expression()?,
Some(StrLiteral(s)) => {
self.next();
Expression::StringLiteral(s)
}
Some(Keyword(Kw::If)) => self.conditional_expr()?,
Some(Keyword(Kw::While)) => self.while_expr()?,
Some(Identifier(_)) => self.identifier_expr()?,
Some(Token::LParen) => self.paren_expr()?,
Some(Keyword(Kw::Fn)) => self.lambda_expr()?,
Some(Token::LSquareBracket) => self.list_expr()?,
Some(Token::LCurlyBrace) => self.struct_expr()?,
Some(e) => {
return ParseError::result_from_str(&format!("Expected primary expression, got \
{:?}",
e));
}
None => return ParseError::result_from_str("Expected primary expression received EoI"),
})
}
fn list_expr(&mut self) -> ParseResult<Expression> {
expect!(self, LSquareBracket);
let exprlist: Vec<Expression> = self.exprlist()?;
expect!(self, RSquareBracket);
Ok(Expression::ListLiteral(VecDeque::from(exprlist)))
}
fn struct_expr(&mut self) -> ParseResult<Expression> {
expect!(self, LCurlyBrace);
let struct_items = self.itemlist()?;
expect!(self, RCurlyBrace);
Ok(Expression::StructLiteral(struct_items))
}
fn number_expression(&mut self) -> ParseResult<Expression> {
let mut multiplier = 1;
loop {
match self.peek() {
Some(NumLiteral(n)) => {
self.next();
return Ok(Expression::Number(n * multiplier as f64));
}
Some(Operator(OpTok(ref a))) if **a == "+" => {
self.next();
}
Some(Operator(OpTok(ref a))) if **a == "-" => {
multiplier *= -1;
self.next();
}
Some(e) => {
return ParseError::result_from_str(
&format!("Expected +, - or number, got {:?}", e));
}
None => {
return ParseError::result_from_str(
&format!("Expected +, - or number, got EoI"));
}
}
}
}
fn lambda_expr(&mut self) -> ParseResult<Expression> {
use self::Expression::*;
expect!(self, Keyword(Kw::Fn));
skip_whitespace!(self);
expect!(self, LParen);
let parameters = self.identlist()?;
expect!(self, RParen);
skip_whitespace!(self);
expect!(self, LCurlyBrace);
let body = self.body()?;
expect!(self, RCurlyBrace);
let prototype = Prototype {
name: Rc::new("a lambda yo!".to_string()),
parameters: parameters,
};
let function = Function {
prototype: prototype,
body: body,
};
Ok(Lambda(function))
}
fn while_expr(&mut self) -> ParseResult<Expression> {
use self::Expression::*;
expect!(self, Keyword(Kw::While));
let test = self.expression()?;
expect!(self, LCurlyBrace);
let body = delimiter_block!(
self,
expression,
Some(RCurlyBrace)
);
expect!(self, RCurlyBrace);
Ok(While(Box::new(test), body))
}
fn conditional_expr(&mut self) -> ParseResult<Expression> {
use self::Expression::*;
expect!(self, Keyword(Kw::If));
let test = self.expression()?;
skip_whitespace!(self);
expect!(self, LCurlyBrace);
skip_whitespace!(self);
let then_block = delimiter_block!(
self,
expression,
Some(RCurlyBrace)
);
expect!(self, RCurlyBrace);
skip_whitespace!(self);
let else_block = if let Some(Keyword(Kw::Else)) = self.peek() {
self.next();
skip_whitespace!(self);
expect!(self, LCurlyBrace);
let else_exprs = delimiter_block!(
self,
expression,
Some(RCurlyBrace)
);
Some(else_exprs)
} else {
None
};
expect!(self, RCurlyBrace);
Ok(Conditional(Box::new(test),
Box::new(Block(VecDeque::from(then_block))),
else_block.map(|list| Box::new(Block(VecDeque::from(list))))))
}
fn identifier_expr(&mut self) -> ParseResult<Expression> {
let name = expect_identifier!(self);
let expr = match self.peek() {
Some(LParen) => {
let args = self.call_expression()?;
Expression::Call(Callable::NamedFunction(name), args)
}
__ => Expression::Variable(name),
};
Ok(expr)
}
fn call_expression(&mut self) -> ParseResult<Vec<Expression>> {
expect!(self, LParen);
let args: Vec<Expression> = self.exprlist()?;
expect!(self, RParen);
Ok(args)
}
fn paren_expr(&mut self) -> ParseResult<Expression> {
expect!(self, Token::LParen);
let expr = self.expression()?;
expect!(self, Token::RParen);
Ok(expr)
}
}
pub fn parse(tokens: &[Token], _parsed_tree: &[Statement]) -> ParseResult<AST> {
let mut parser = Parser::initialize(tokens);
parser.program()
}
/*
#[cfg(test)]
mod tests {
use schala_lang::tokenizer;
use super::*;
use super::Statement::*;
use super::Expression::*;
macro_rules! parsetest {
($input:expr, $output:pat, $ifexpr:expr) => {
{
let tokens = tokenizer::tokenize($input).unwrap();
let ast = parse(&tokens, &[]).unwrap();
match &ast[..] {
$output if $ifexpr => (),
x => panic!("Error in parse test, got {:?} instead", x)
}
}
}
}
#[test]
fn function_parse_test() {
use super::Function;
parsetest!(
"fn a() { 1 + 2 }",
&[FuncDefNode(Function {prototype: Prototype { ref name, ref parameters }, ref body})],
match &body[..] { &[ExprNode(BinExp(_, box Number(1.0), box Number(2.0)))] => true, _ => false }
&& **name == "a" && match &parameters[..] { &[] => true, _ => false }
);
parsetest!(
"fn a(x,y){ 1 + 2 }",
&[FuncDefNode(Function {prototype: Prototype { ref name, ref parameters }, ref body})],
match &body[..] { &[ExprNode(BinExp(_, box Number(1.0), box Number(2.0)))] => true, _ => false }
&& **name == "a" && *parameters[0] == "x" && *parameters[1] == "y" && parameters.len() == 2
);
let t3 = "fn (x) { x + 2 }";
let tokens3 = tokenizer::tokenize(t3).unwrap();
assert!(parse(&tokens3, &[]).is_err());
}
#[test]
fn expression_parse_test() {
parsetest!("a", &[ExprNode(Variable(ref s))], **s == "a");
parsetest!("a + b",
&[ExprNode(BinExp(BinOp::Add, box Variable(ref a), box Variable(ref b)))],
**a == "a" && **b == "b");
parsetest!("a + b * c",
&[ExprNode(BinExp(BinOp::Add, box Variable(ref a), box BinExp(BinOp::Mul, box Variable(ref b), box Variable(ref c))))],
**a == "a" && **b == "b" && **c == "c");
parsetest!("a * b + c",
&[ExprNode(BinExp(BinOp::Add, box BinExp(BinOp::Mul, box Variable(ref a), box Variable(ref b)), box Variable(ref c)))],
**a == "a" && **b == "b" && **c == "c");
parsetest!("(a + b) * c",
&[ExprNode(BinExp(BinOp::Mul, box BinExp(BinOp::Add, box Variable(ref a), box Variable(ref b)), box Variable(ref c)))],
**a == "a" && **b == "b" && **c == "c");
}
#[test]
fn lambda_parse_test() {
use schala_lang::tokenizer;
let t1 = "(fn(x) { x + 2 })";
let tokens1 = tokenizer::tokenize(t1).unwrap();
match parse(&tokens1, &[]).unwrap()[..] {
_ => (),
}
let t2 = "fn(x) { x + 2 }";
let tokens2 = tokenizer::tokenize(t2).unwrap();
assert!(parse(&tokens2, &[]).is_err());
let t3 = "(fn(x) { x + 10 })(20)";
let tokens3 = tokenizer::tokenize(t3).unwrap();
match parse(&tokens3, &[]).unwrap() {
_ => (),
};
}
#[test]
fn conditional_parse_test() {
use schala_lang::tokenizer;
let t1 = "if null { 20 } else { 40 }";
let tokens = tokenizer::tokenize(t1).unwrap();
match parse(&tokens, &[]).unwrap()[..] {
[ExprNode(Conditional(box Null, box Block(_), Some(box Block(_))))] => (),
_ => panic!(),
}
let t2 = r"
if null {
20
} else {
40
}
";
let tokens2 = tokenizer::tokenize(t2).unwrap();
match parse(&tokens2, &[]).unwrap()[..] {
[ExprNode(Conditional(box Null, box Block(_), Some(box Block(_))))] => (),
_ => panic!(),
}
let t2 = r"
if null {
20 } else
{
40
}
";
let tokens3 = tokenizer::tokenize(t2).unwrap();
match parse(&tokens3, &[]).unwrap()[..] {
[ExprNode(Conditional(box Null, box Block(_), Some(box Block(_))))] => (),
_ => panic!(),
}
}
}
*/

208
maaru/src/tokenizer.rs Normal file
View File

@ -0,0 +1,208 @@
extern crate itertools;
use std::iter::Peekable;
use std::str::Chars;
use self::itertools::Itertools;
use std::rc::Rc;
use TokenError;
#[derive(Debug, Clone, PartialEq)]
pub enum Token {
Newline,
Semicolon,
LParen,
RParen,
LSquareBracket,
RSquareBracket,
LCurlyBrace,
RCurlyBrace,
Comma,
Period,
Colon,
NumLiteral(f64),
StrLiteral(Rc<String>),
Identifier(Rc<String>),
Operator(OpTok),
Keyword(Kw),
}
#[derive(Debug, Clone, PartialEq)]
pub struct OpTok(pub Rc<String>);
#[derive(Debug, Clone, PartialEq)]
pub enum Kw {
If,
Else,
While,
Let,
Fn,
Null,
}
pub type TokenizeResult = Result<Vec<Token>, TokenError>;
fn is_digit(c: &char) -> bool {
c.is_digit(10)
}
pub fn tokenize(input: &str) -> TokenizeResult {
use self::Token::*;
let mut tokens = Vec::new();
let mut iter: Peekable<Chars> = input.chars().peekable();
while let Some(c) = iter.next() {
if c == '#' {
while let Some(c) = iter.next() {
if c == '\n' {
break;
}
}
continue;
}
let cur_tok = match c {
c if char::is_whitespace(c) && c != '\n' => continue,
'\n' => Newline,
';' => Semicolon,
'(' => LParen,
')' => RParen,
':' => Colon,
',' => Comma,
'{' => LCurlyBrace,
'}' => RCurlyBrace,
'[' => LSquareBracket,
']' => RSquareBracket,
'"' => tokenize_str(&mut iter)?,
c if !char::is_alphanumeric(c) => tokenize_operator(c, &mut iter)?,
c @ '.' | c if is_digit(&c) => tokenize_number_or_period(c, &mut iter)?,
c => tokenize_identifier(c, &mut iter)?,
};
tokens.push(cur_tok);
}
Ok(tokens)
}
fn tokenize_str(iter: &mut Peekable<Chars>) -> Result<Token, TokenError> {
let mut buffer = String::new();
loop {
// TODO handle string escapes, interpolation
match iter.next() {
Some(x) if x == '"' => break,
Some(x) => buffer.push(x),
None => return Err(TokenError::new("Unclosed quote")),
}
}
Ok(Token::StrLiteral(Rc::new(buffer)))
}
fn tokenize_operator(c: char, iter: &mut Peekable<Chars>) -> Result<Token, TokenError> {
let mut buffer = String::new();
buffer.push(c);
buffer.extend(iter.peeking_take_while(|x| !char::is_alphanumeric(*x) && !char::is_whitespace(*x)));
Ok(Token::Operator(OpTok(Rc::new(buffer))))
}
fn tokenize_number_or_period(c: char, iter: &mut Peekable<Chars>) -> Result<Token, TokenError> {
if c == '.' && !iter.peek().map_or(false, is_digit) {
return Ok(Token::Period);
}
let mut buffer = String::new();
buffer.push(c);
buffer.extend(iter.peeking_take_while(|x| is_digit(x) || *x == '.'));
match buffer.parse::<f64>() {
Ok(f) => Ok(Token::NumLiteral(f)),
Err(_) => Err(TokenError::new("Failed to parse digit")),
}
}
fn tokenize_identifier(c: char, iter: &mut Peekable<Chars>) -> Result<Token, TokenError> {
fn ends_identifier(c: &char) -> bool {
let c = *c;
char::is_whitespace(c) || is_digit(&c) || c == ';' || c == '(' || c == ')' ||
c == ',' || c == '.' || c == ',' || c == ':' || c == '[' || c == ']'
}
use self::Token::*;
let mut buffer = String::new();
buffer.push(c);
buffer.extend(iter.peeking_take_while(|x| !ends_identifier(x)));
Ok(match &buffer[..] {
"if" => Keyword(Kw::If),
"else" => Keyword(Kw::Else),
"while" => Keyword(Kw::While),
"let" => Keyword(Kw::Let),
"fn" => Keyword(Kw::Fn),
"null" => Keyword(Kw::Null),
b => Identifier(Rc::new(b.to_string())),
})
}
/*
#[cfg(test)]
mod tests {
use super::*;
use super::Token::*;
macro_rules! token_test {
($input: expr, $output: pat, $ifexpr: expr) => {
let tokens = tokenize($input).unwrap();
match tokens[..] {
$output if $ifexpr => (),
_ => panic!("Actual output: {:?}", tokens),
}
}
}
#[test]
fn basic_tokeniziation_tests() {
token_test!("let a = 3\n",
[Keyword(Kw::Let), Identifier(ref a), Operator(OpTok(ref b)), NumLiteral(3.0), Newline],
**a == "a" && **b == "=");
token_test!("2+1",
[NumLiteral(2.0), Operator(OpTok(ref a)), NumLiteral(1.0)],
**a == "+");
token_test!("2 + 1",
[NumLiteral(2.0), Operator(OpTok(ref a)), NumLiteral(1.0)],
**a == "+");
token_test!("2.3*49.2",
[NumLiteral(2.3), Operator(OpTok(ref a)), NumLiteral(49.2)],
**a == "*");
token_test!("a+3",
[Identifier(ref a), NumLiteral(3.0)],
**a == "a+");
assert!(tokenize("2.4.5").is_err());
token_test!("fn my_func(a) { a ? 3[1] }",
[Keyword(Kw::Fn), Identifier(ref a), LParen, Identifier(ref b), RParen, LCurlyBrace, Identifier(ref c),
Operator(OpTok(ref d)), NumLiteral(3.0), LSquareBracket, NumLiteral(1.0), RSquareBracket, RCurlyBrace],
**a == "my_func" && **b == "a" && **c == "a" && **d == "?");
}
#[test]
fn string_test() {
token_test!("null + \"a string\"",
[Keyword(Kw::Null), Operator(OpTok(ref a)), StrLiteral(ref b)],
**a == "+" && **b == "a string");
token_test!("\"{?'q@?\"",
[StrLiteral(ref a)],
**a == "{?'q@?");
}
#[test]
fn operator_test() {
token_test!("a *> b",
[Identifier(ref a), Operator(OpTok(ref b)), Identifier(ref c)],
**a == "a" && **b == "*>" && **c == "b");
}
}
*/

11
robo/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "robo-lang"
version = "0.1.0"
authors = ["greg <greg.shuflin@protonmail.com>"]
[dependencies]
itertools = "0.5.8"
take_mut = "0.1.3"
llvm-sys = "*"
schala-repl = { path = "../schala-repl" }

172
robo/src/lib.rs Normal file
View File

@ -0,0 +1,172 @@
#![feature(box_patterns)]
extern crate itertools;
extern crate schala_repl;
use itertools::Itertools;
use schala_repl::{ProgrammingLanguageInterface, EvalOptions, LanguageOutput};
pub struct Robo {
}
impl Robo {
pub fn new() -> Robo {
Robo { }
}
}
#[derive(Debug)]
pub struct TokenError {
pub msg: String,
}
impl TokenError {
pub fn new(msg: &str) -> TokenError {
TokenError { msg: msg.to_string() }
}
}
#[allow(dead_code)]
#[derive(Debug)]
pub enum Token {
StrLiteral(String),
Backtick,
Newline,
LParen,
RParen,
LBracket,
RBracket,
LBrace,
RBrace,
Period,
Comma,
Colon,
Semicolon,
SingleQuote,
Identifier(String),
Operator(String),
NumLiteral(Number),
}
#[allow(dead_code)]
#[derive(Debug)]
pub enum Number {
IntegerRep(String),
FloatRep(String)
}
#[allow(dead_code)]
pub type AST = Vec<ASTNode>;
#[allow(dead_code)]
#[derive(Debug)]
pub enum ASTNode {
FunctionDefinition(String, Expression),
ImportStatement(String),
}
#[allow(dead_code)]
#[derive(Debug)]
pub enum Expression {
}
fn tokenize(input: &str) -> Result<Vec<Token>, TokenError> {
use self::Token::*;
let mut tokens = Vec::new();
let mut iter = input.chars().peekable();
while let Some(c) = iter.next() {
if c == ';' {
while let Some(c) = iter.next() {
if c == '\n' {
break;
}
}
continue;
}
let cur_tok = match c {
c if char::is_whitespace(c) && c != '\n' => continue,
'\n' => Newline,
'(' => LParen,
')' => RParen,
'[' => LBracket,
']' => RBracket,
'{' => LBrace,
'}' => RBrace,
',' => Comma,
':' => Colon,
';' => Semicolon,
'.' => Period,
'`' => Backtick,
'\'' => SingleQuote,
'"' => {
let mut buffer = String::new();
loop {
match iter.next() {
Some(x) if x == '"' => break,
Some(x) => buffer.push(x),
None => return Err(TokenError::new("Unclosed quote")),
}
}
StrLiteral(buffer)
}
c if c.is_digit(10) => {
let mut integer = true;
let mut buffer = String::new();
buffer.push(c);
buffer.extend(iter.peeking_take_while(|x| x.is_digit(10)));
if let Some(&'.') = iter.peek() {
buffer.push(iter.next().unwrap());
integer = false;
}
buffer.extend(iter.peeking_take_while(|x| x.is_digit(10)));
let inner = if integer {
Number::IntegerRep(buffer)
} else {
Number::FloatRep(buffer)
};
NumLiteral(inner)
},
c if char::is_alphanumeric(c) => {
let mut buffer = String::new();
buffer.push(c);
buffer.extend(iter.peeking_take_while(|x| char::is_alphanumeric(*x)));
Identifier(buffer)
},
c => {
let mut buffer = String::new();
buffer.push(c);
buffer.extend(iter.peeking_take_while(|x| !char::is_whitespace(*x)));
Operator(buffer)
}
};
tokens.push(cur_tok);
}
Ok(tokens)
}
impl ProgrammingLanguageInterface for Robo {
fn get_language_name(&self) -> String {
"Robo".to_string()
}
fn get_source_file_suffix(&self) -> String {
format!("robo")
}
fn evaluate_in_repl(&mut self, input: &str, _eval_options: &EvalOptions) -> LanguageOutput {
let mut output = LanguageOutput::default();
let tokens = match tokenize(input) {
Ok(tokens) => tokens,
Err(e) => {
output.add_output(format!("Tokenize error: {:?}", e));
return output;
}
};
output.add_output(format!("{:?}", tokens));
output
}
}

11
rukka/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "rukka-lang"
version = "0.1.0"
authors = ["greg <greg.shuflin@protonmail.com>"]
[dependencies]
itertools = "0.5.8"
take_mut = "0.1.3"
llvm-sys = "*"
schala-repl = { path = "../schala-repl" }

437
rukka/src/lib.rs Normal file
View File

@ -0,0 +1,437 @@
#![feature(box_patterns)]
extern crate itertools;
extern crate schala_repl;
use itertools::Itertools;
use schala_repl::{ProgrammingLanguageInterface, EvalOptions, LanguageOutput};
use std::iter::Peekable;
use std::vec::IntoIter;
use std::str::Chars;
use std::collections::HashMap;
pub struct EvaluatorState {
binding_stack: Vec<HashMap<String, Sexp>>
}
impl EvaluatorState {
fn new() -> EvaluatorState {
use self::Sexp::Primitive;
use self::PrimitiveFn::*;
let mut default_map = HashMap::new();
default_map.insert(format!("+"), Primitive(Plus));
default_map.insert(format!("-"), Primitive(Minus));
default_map.insert(format!("*"), Primitive(Mult));
default_map.insert(format!("/"), Primitive(Div));
default_map.insert(format!("%"), Primitive(Mod));
default_map.insert(format!(">"), Primitive(Greater));
default_map.insert(format!("<"), Primitive(Less));
default_map.insert(format!("<="), Primitive(LessThanOrEqual));
default_map.insert(format!(">="), Primitive(GreaterThanOrEqual));
default_map.insert(format!("display"), Primitive(Display));
EvaluatorState {
binding_stack: vec![default_map],
}
}
fn set_var(&mut self, var: String, value: Sexp) {
let binding = self.binding_stack.last_mut().unwrap();
binding.insert(var, value);
}
fn get_var(&self, var: &str) -> Option<&Sexp> {
for bindings in self.binding_stack.iter().rev() {
match bindings.get(var) {
Some(x) => return Some(x),
None => (),
}
}
None
}
fn push_env(&mut self) {
self.binding_stack.push(HashMap::new());
}
fn pop_env(&mut self) {
self.binding_stack.pop();
}
}
pub struct Rukka {
state: EvaluatorState
}
impl Rukka {
pub fn new() -> Rukka { Rukka { state: EvaluatorState::new() } }
}
impl ProgrammingLanguageInterface for Rukka {
fn get_language_name(&self) -> String {
"Rukka".to_string()
}
fn get_source_file_suffix(&self) -> String {
format!("rukka")
}
fn evaluate_in_repl(&mut self, input: &str, _eval_options: &EvalOptions) -> LanguageOutput {
let mut output = LanguageOutput::default();
let sexps = match read(input) {
Err(err) => {
output.add_output(format!("Error: {}", err));
return output;
},
Ok(sexps) => sexps
};
let output_str: String = sexps.into_iter().enumerate().map(|(i, sexp)| {
match self.state.eval(sexp) {
Ok(result) => format!("{}: {}", i, result.print()),
Err(err) => format!("{} Error: {}", i, err),
}
}).intersperse(format!("\n")).collect();
output.add_output(output_str);
output
}
}
impl EvaluatorState {
fn eval(&mut self, expr: Sexp) -> Result<Sexp, String> {
use self::Sexp::*;
Ok(match expr {
SymbolAtom(ref sym) => match self.get_var(sym) {
Some(ref sexp) => {
let q: &Sexp = sexp; //WTF? if I delete this line, the copy doesn't work??
q.clone() //TODO make this not involve a clone
},
None => return Err(format!("Variable {} not bound", sym)),
},
expr @ Primitive(_) => expr,
expr @ FnLiteral { .. } => expr,
expr @ StringAtom(_) => expr,
expr @ NumberAtom(_) => expr,
expr @ BoolAtom(_) => expr,
Cons(box operator, box operands) => match operator {
SymbolAtom(ref sym) if match &sym[..] {
"quote" | "eq?" | "cons" | "car" | "cdr" | "atom?" | "define" | "lambda" | "if" | "cond" => true, _ => false
} => self.eval_special_form(sym, operands)?,
_ => {
let evaled = self.eval(operator)?;
self.apply(evaled, operands)?
}
},
Nil => Nil,
})
}
fn eval_special_form(&mut self, form: &str, operands: Sexp) -> Result<Sexp, String> {
use self::Sexp::*;
Ok(match form {
"quote" => match operands {
Cons(box quoted, box Nil) => quoted,
_ => return Err(format!("Bad syntax in quote")),
},
"eq?" => match operands {//TODO make correct
Cons(box lhs, box Cons(box rhs, _)) => BoolAtom(lhs == rhs),
_ => BoolAtom(true),
},
"cons" => match operands {
Cons(box cadr, box Cons(box caddr, box Nil)) => {
let newl = self.eval(cadr)?;
let newr = self.eval(caddr)?;
Cons(Box::new(newl), Box::new(newr))
},
_ => return Err(format!("Bad arguments for cons")),
},
"car" => match operands {
Cons(box car, _) => car,
_ => return Err(format!("called car with a non-pair argument")),
},
"cdr" => match operands {
Cons(_, box cdr) => cdr,
_ => return Err(format!("called cdr with a non-pair argument")),
},
"atom?" => match operands {
Cons(_, _) => BoolAtom(false),
_ => BoolAtom(true),
},
"define" => match operands {
Cons(box SymbolAtom(sym), box Cons(box expr, box Nil)) => {
let evaluated = self.eval(expr)?;
self.set_var(sym, evaluated);
Nil
},
_ => return Err(format!("Bad assignment")),
}
"lambda" => match operands {
Cons(box mut paramlist, box Cons(box formalexp, box Nil)) => {
let mut formal_params = vec![];
{
let mut ptr = &paramlist;
loop {
match ptr {
&Cons(ref arg, ref rest) => {
if let SymbolAtom(ref sym) = **arg {
formal_params.push(sym.clone());
ptr = rest;
} else {
return Err(format!("Bad lambda format"));
}
},
_ => break,
}
}
}
FnLiteral {
formal_params,
body: Box::new(formalexp)
}
},
_ => return Err(format!("Bad lambda expression")),
},
"if" => match operands {
Cons(box test, box body) => {
let truth_value = test.truthy();
match (truth_value, body) {
(true, Cons(box consequent, _)) => consequent,
(false, Cons(_, box Cons(box alternative, _))) => alternative,
_ => return Err(format!("Bad if expression"))
}
},
_ => return Err(format!("Bad if expression"))
},
s => return Err(format!("Non-existent special form {}; this should never happen", s)),
})
}
fn apply(&mut self, function: Sexp, operands: Sexp) -> Result<Sexp, String> {
use self::Sexp::*;
match function {
FnLiteral { formal_params, body } => {
self.push_env();
let mut cur = operands;
for param in formal_params {
match cur {
Cons(box arg, box rest) => {
cur = rest;
self.set_var(param, arg);
},
_ => return Err(format!("Bad argument for function application")),
}
}
let result = self.eval(*body);
self.pop_env();
result
},
Primitive(prim) => {
let mut evaled_operands = Vec::new();
let mut cur_operand = operands;
loop {
match cur_operand {
Nil => break,
Cons(box l, box rest) => {
evaled_operands.push(self.eval(l)?);
cur_operand = rest;
},
_ => return Err(format!("Bad operands list"))
}
}
prim.apply(evaled_operands)
}
_ => return Err(format!("Bad type to apply")),
}
}
}
fn read(input: &str) -> Result<Vec<Sexp>, String> {
let mut chars: Peekable<Chars> = input.chars().peekable();
let mut tokens = tokenize(&mut chars).into_iter().peekable();
let mut sexps = Vec::new();
while let Some(_) = tokens.peek() {
sexps.push(parse(&mut tokens)?);
}
Ok(sexps)
}
#[derive(Debug)]
enum Token {
LParen,
RParen,
Quote,
Word(String),
StringLiteral(String),
NumLiteral(u64),
}
//TODO make this notion of Eq more sophisticated
#[derive(Debug, PartialEq, Clone)]
enum Sexp {
SymbolAtom(String),
StringAtom(String),
NumberAtom(u64),
BoolAtom(bool),
Cons(Box<Sexp>, Box<Sexp>),
Nil,
FnLiteral {
formal_params: Vec<String>,
body: Box<Sexp>
},
Primitive(PrimitiveFn)
}
#[derive(Debug, PartialEq, Clone)]
enum PrimitiveFn {
Plus, Minus, Mult, Div, Mod, Greater, Less, GreaterThanOrEqual, LessThanOrEqual, Display
}
impl PrimitiveFn {
fn apply(&self, evaled_operands: Vec<Sexp>) -> Result<Sexp, String> {
use self::Sexp::*;
use self::PrimitiveFn::*;
let op = self.clone();
Ok(match op {
Display => {
for arg in evaled_operands {
print!("{}\n", arg.print());
}
Nil
},
Plus | Mult => {
let mut result = match op { Plus => 0, Mult => 1, _ => unreachable!() };
for arg in evaled_operands {
if let NumberAtom(n) = arg {
if let Plus = op {
result += n;
} else if let Mult = op {
result *= n;
}
} else {
return Err(format!("Bad operand: {:?}", arg));
}
}
NumberAtom(result)
},
op => return Err(format!("Primitive op {:?} not implemented", op)),
})
}
}
impl Sexp {
fn print(&self) -> String {
use self::Sexp::*;
match self {
&BoolAtom(true) => format!("#t"),
&BoolAtom(false) => format!("#f"),
&SymbolAtom(ref sym) => format!("{}", sym),
&StringAtom(ref s) => format!("\"{}\"", s),
&NumberAtom(ref n) => format!("{}", n),
&Cons(ref car, ref cdr) => format!("({} . {})", car.print(), cdr.print()),
&Nil => format!("()"),
&FnLiteral { ref formal_params, .. } => format!("<lambda {:?}>", formal_params),
&Primitive(ref sym) => format!("<primitive \"{:?}\">", sym),
}
}
fn truthy(&self) -> bool {
use self::Sexp::*;
match self {
&BoolAtom(false) => false,
_ => true
}
}
}
fn tokenize(input: &mut Peekable<Chars>) -> Vec<Token> {
use self::Token::*;
let mut tokens = Vec::new();
loop {
match input.next() {
None => break,
Some('(') => tokens.push(LParen),
Some(')') => tokens.push(RParen),
Some('\'') => tokens.push(Quote),
Some(c) if c.is_whitespace() => continue,
Some(c) if c.is_numeric() => {
let tok: String = input.peeking_take_while(|next| next.is_numeric()).collect();
let n: u64 = format!("{}{}", c, tok).parse().unwrap();
tokens.push(NumLiteral(n));
},
Some('"') => {
let string: String = input.scan(false, |escape, cur_char| {
let seen_escape = *escape;
*escape = cur_char == '\\' && !seen_escape;
match (cur_char, seen_escape) {
('"', false) => None,
('\\', false) => Some(None),
(c, _) => Some(Some(c))
}
}).filter_map(|x| x).collect();
tokens.push(StringLiteral(string));
}
Some(c) => {
let sym: String = input.peeking_take_while(|next| {
match *next {
'(' | ')' => false,
c if c.is_whitespace() => false,
_ => true
}
}).collect();
tokens.push(Word(format!("{}{}", c, sym)));
}
}
}
tokens
}
fn parse(tokens: &mut Peekable<IntoIter<Token>>) -> Result<Sexp, String> {
use self::Token::*;
use self::Sexp::*;
match tokens.next() {
Some(Word(ref s)) if s == "#f" => Ok(BoolAtom(false)),
Some(Word(ref s)) if s == "#t" => Ok(BoolAtom(true)),
Some(Word(s)) => Ok(SymbolAtom(s)),
Some(StringLiteral(s)) => Ok(StringAtom(s)),
Some(LParen) => parse_sexp(tokens),
Some(RParen) => Err(format!("Unexpected ')'")),
Some(Quote) => {
let quoted = parse(tokens)?;
Ok(Cons(Box::new(SymbolAtom(format!("quote"))), Box::new(Cons(Box::new(quoted), Box::new(Nil)))))
},
Some(NumLiteral(n)) => Ok(NumberAtom(n)),
None => Err(format!("Unexpected end of input")),
}
}
fn parse_sexp(tokens: &mut Peekable<IntoIter<Token>>) -> Result<Sexp, String> {
use self::Token::*;
use self::Sexp::*;
let mut cell = Nil;
{
let mut cell_ptr = &mut cell;
loop {
match tokens.peek() {
None => return Err(format!("Unexpected end of input")),
Some(&RParen) => {
tokens.next();
break;
},
_ => {
let current = parse(tokens)?;
let new_cdr = Cons(Box::new(current), Box::new(Nil));
match cell_ptr {
&mut Cons(_, ref mut cdr) => **cdr = new_cdr,
&mut Nil => *cell_ptr = new_cdr,
_ => unreachable!()
};
let old_ptr = cell_ptr;
let new_ptr: &mut Sexp = match old_ptr { &mut Cons(_, ref mut cdr) => cdr, _ => unreachable!() } as &mut Sexp;
cell_ptr = new_ptr;
}
}
}
}
Ok(cell)
}

12
schala-codegen/Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "schala-codegen"
version = "0.1.0"
authors = ["greg <greg.shuflin@protonmail.com>"]
[dependencies]
quote = "0.5.2"
syn = { version = "0.13.1", features = ["full", "extra-traits"] }
[lib]
proc-macro = true

95
schala-codegen/src/lib.rs Normal file
View File

@ -0,0 +1,95 @@
#![feature(proc_macro)]
extern crate proc_macro;
#[macro_use]
extern crate syn;
#[macro_use]
extern crate quote;
use proc_macro::TokenStream;
use syn::{Expr, Lit, ExprLit};
use syn::punctuated::Punctuated;
use syn::synom::Synom;
fn get_string_args(input: Expr) -> Vec<String> {
let mut contained_strings = Vec::new();
match input {
Expr::Array(array) => {
for item in array.elems {
if let Expr::Lit(ExprLit { lit: Lit::Str(s), ..}) = item {
contained_strings.push(s.value());
} else {
panic!("Non-string-literal input to compiler_pass_sequence");
}
}
},
_ => panic!("Non-array input to compiler_pass_sequence"),
}
contained_strings
}
#[proc_macro]
pub fn compiler_pass_sequence(input: TokenStream) -> TokenStream {
/*
for token_tree in input {
//println!("ITEM: {:?}", token_tree.kind);
match token_tree.kind {
TokenNode::Literal(l) => println!("{:?}", l),
_ => ()
}
}
*/
let input: Expr = syn::parse(input).unwrap();
let stages = get_string_args(input);
let from_macro = format!("{:?}", stages);
let output = quote! {
fn new_execute(&mut self, input: &str, _options: &EvalOptions) -> FinishedComputation {
let evaluation = UnfinishedComputation::default();
evaluation.output(Err(#from_macro.to_string()))
}
};
output.into()
}
/* #[compiler_pass(<name of pass>*/
#[proc_macro_attribute]
pub fn compiler_pass(metadata: TokenStream, function: TokenStream) -> TokenStream {
//println!("FROM MACRO: {}", function);
println!("Compiler pass metadata: {}", metadata);
function
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
/* in Rocket
*
#[get("/")]
fn hi() -> &'static str {
"hello"
}
GETS MAPPED TO:
static hi_info = RouteInfo {
name: "hi",
method: Method::Get,
path: "/",
handler: hi_route,
}
fn hi_route(req: &Request) -> Outcome {
let responder = hi();
Outcome::from(req, responder);
}
*/

13
schala-lang/Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "schala-lang"
version = "0.1.0"
authors = ["greg <greg.shuflin@protonmail.com>"]
[dependencies]
itertools = "0.5.8"
take_mut = "0.1.3"
maplit = "*"
lazy_static = "0.2.8"
schala-repl = { path = "../schala-repl" }
schala-codegen = { path = "../schala-codegen" }

View File

@ -0,0 +1,77 @@
use std::rc::Rc;
use std::collections::HashMap;
use typechecking::{Type, TypeResult, TConst};
use self::Type::*; use self::TConst::*;
#[derive(Debug, PartialEq, Clone)]
pub struct BinOp {
sigil: Rc<String>
}
impl BinOp {
pub fn from_sigil(sigil: &str) -> BinOp {
BinOp { sigil: Rc::new(sigil.to_string()) }
}
pub fn sigil(&self) -> &Rc<String> {
&self.sigil
}
pub fn get_type(&self) -> TypeResult<Type> {
let s = self.sigil.as_str();
BINOPS.get(s).map(|x| x.0.clone()).ok_or(format!("Binop {} not found", s))
}
pub fn min_precedence() -> i32 {
i32::min_value()
}
pub fn get_precedence(op: &str) -> i32 {
let default = 10_000_000;
BINOPS.get(op).map(|x| x.2.clone()).unwrap_or(default)
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct PrefixOp {
sigil: Rc<String>
}
impl PrefixOp {
pub fn from_sigil(sigil: &str) -> PrefixOp {
PrefixOp { sigil: Rc::new(sigil.to_string()) }
}
pub fn sigil(&self) -> &Rc<String> {
&self.sigil
}
pub fn is_prefix(op: &str) -> bool {
PREFIX_OPS.get(op).is_some()
}
pub fn get_type(&self) -> TypeResult<Type> {
let s = self.sigil.as_str();
PREFIX_OPS.get(s).map(|x| x.0.clone()).ok_or(format!("Prefix op {} not found", s))
}
}
lazy_static! {
static ref PREFIX_OPS: HashMap<&'static str, (Type, ())> =
hashmap! {
"+" => (Func(bx!(Const(Int)), bx!(Const(Int))), ()),
"-" => (Func(bx!(Const(Int)), bx!(Const(Int))), ()),
"!" => (Func(bx!(Const(Bool)), bx!(Const(Bool))), ()),
};
}
/* the second tuple member is a placeholder for when I want to make evaluation rules tied to the
* binop definition */
lazy_static! {
static ref BINOPS: HashMap<&'static str, (Type, (), i32)> =
hashmap! {
"+" => (Func(bx!(Const(Int)), bx!(Func(bx!(Const(Int)), bx!(Const(Int))))), (), 10),
"-" => (Func(bx!(Const(Int)), bx!(Func(bx!(Const(Int)), bx!(Const(Int))))), (), 10),
"*" => (Func(bx!(Const(Int)), bx!(Func(bx!(Const(Int)), bx!(Const(Int))))), (), 20),
"/" => (Func(bx!(Const(Int)), bx!(Func(bx!(Const(Int)), bx!(Const(Float))))), (), 20),
"//" => (Func(bx!(Const(Int)), bx!(Func(bx!(Const(Int)), bx!(Const(Int))))), (), 20), //TODO change this to `quot`
"%" => (Func(bx!(Const(Int)), bx!(Func(bx!(Const(Int)), bx!(Const(Int))))), (), 20),
"++" => (Func(bx!(Const(StringT)), bx!(Func(bx!(Const(StringT)), bx!(Const(StringT))))), (), 30),
"^" => (Func(bx!(Const(Int)), bx!(Func(bx!(Const(Int)), bx!(Const(Int))))), (), 20),
"&" => (Func(bx!(Const(Int)), bx!(Func(bx!(Const(Int)), bx!(Const(Int))))), (), 20),
"|" => (Func(bx!(Const(Int)), bx!(Func(bx!(Const(Int)), bx!(Const(Int))))), (), 20),
};
}

317
schala-lang/src/eval.rs Normal file
View File

@ -0,0 +1,317 @@
use std::collections::HashMap;
use std::rc::Rc;
use std::fmt::Write;
use itertools::Itertools;
use parsing::{AST, Statement, Declaration, Expression, Variant, ExpressionType};
use builtin::{BinOp, PrefixOp};
pub struct State<'a> {
parent_frame: Option<&'a State<'a>>,
values: HashMap<Rc<String>, ValueEntry>,
}
impl<'a> State<'a> {
fn insert(&mut self, name: Rc<String>, value: ValueEntry) {
self.values.insert(name, value);
}
fn lookup(&self, name: &Rc<String>) -> Option<&ValueEntry> {
match (self.values.get(name), self.parent_frame) {
(None, None) => None,
(None, Some(parent)) => parent.lookup(name),
(Some(value), _) => Some(value),
}
}
}
#[derive(Debug)]
enum ValueEntry {
Binding {
val: FullyEvaluatedExpr,
},
Function {
param_names: Vec<Rc<String>>,
body: Vec<Statement>,
}
}
type EvalResult<T> = Result<T, String>;
#[derive(Debug, PartialEq, Clone)]
enum FullyEvaluatedExpr {
UnsignedInt(u64),
SignedInt(i64),
Float(f64),
Str(String),
Bool(bool),
FuncLit(Rc<String>),
Custom {
string_rep: Rc<String>,
},
Tuple(Vec<FullyEvaluatedExpr>),
List(Vec<FullyEvaluatedExpr>)
}
impl FullyEvaluatedExpr {
fn to_string(&self) -> String {
use self::FullyEvaluatedExpr::*;
match self {
&UnsignedInt(ref n) => format!("{}", n),
&SignedInt(ref n) => format!("{}", n),
&Float(ref f) => format!("{}", f),
&Str(ref s) => format!("\"{}\"", s),
&Bool(ref b) => format!("{}", b),
&Custom { ref string_rep } => format!("{}", string_rep),
&Tuple(ref items) => {
let mut buf = String::new();
write!(buf, "(").unwrap();
for term in items.iter().map(|e| Some(e)).intersperse(None) {
match term {
Some(e) => write!(buf, "{}", e.to_string()).unwrap(),
None => write!(buf, ", ").unwrap(),
};
}
write!(buf, ")").unwrap();
buf
},
&FuncLit(ref name) => format!("<function {}>", name),
&List(ref items) => {
let mut buf = String::new();
write!(buf, "[").unwrap();
for term in items.iter().map(|e| Some(e)).intersperse(None) {
match term {
Some(e) => write!(buf, "{}", e.to_string()).unwrap(),
None => write!(buf, ", ").unwrap()
}
}
write!(buf, "]").unwrap();
buf
}
}
}
}
impl<'a> State<'a> {
pub fn new() -> State<'a> {
State { parent_frame: None, values: HashMap::new() }
}
pub fn new_with_parent(parent: &'a State<'a>) -> State<'a> {
State { parent_frame: Some(parent), values: HashMap::new() }
}
pub fn evaluate(&mut self, ast: AST) -> Vec<Result<String, String>> {
let mut acc = vec![];
for statement in ast.0 {
match self.eval_statement(statement) {
Ok(output) => {
if let Some(fully_evaluated) = output {
acc.push(Ok(fully_evaluated.to_string()));
}
},
Err(error) => {
acc.push(Err(format!("Eval error: {}", error)));
return acc;
},
}
}
acc
}
}
impl<'a> State<'a> {
fn eval_statement(&mut self, statement: Statement) -> EvalResult<Option<FullyEvaluatedExpr>> {
Ok(match statement {
Statement::ExpressionStatement(expr) => Some(self.eval_expr(expr)?),
Statement::Declaration(decl) => { self.eval_decl(decl)?; None }
})
}
fn eval_decl(&mut self, decl: Declaration) -> EvalResult<()> {
use self::Declaration::*;
use self::Variant::*;
match decl {
FuncDecl(signature, statements) => {
let name = signature.name;
let param_names: Vec<Rc<String>> = signature.params.iter().map(|fp| fp.0.clone()).collect();
self.insert(name, ValueEntry::Function { body: statements.clone(), param_names });
},
TypeDecl(_name, body) => {
for variant in body.0.iter() {
match variant {
&UnitStruct(ref name) => self.insert(name.clone(),
ValueEntry::Binding { val: FullyEvaluatedExpr::Custom { string_rep: name.clone() } }),
&TupleStruct(ref _name, ref _args) => unimplemented!(),
&Record(ref _name, ref _fields) => unimplemented!(),
};
}
},
Binding { name, expr, ..} => {
let val = self.eval_expr(expr)?;
self.insert(name.clone(), ValueEntry::Binding { val });
},
_ => return Err(format!("Declaration evaluation not yet implemented"))
}
Ok(())
}
fn eval_expr(&mut self, expr: Expression) -> EvalResult<FullyEvaluatedExpr> {
use self::ExpressionType::*;
use self::FullyEvaluatedExpr::*;
let expr_type = expr.0;
match expr_type {
IntLiteral(n) => Ok(UnsignedInt(n)),
FloatLiteral(f) => Ok(Float(f)),
StringLiteral(s) => Ok(Str(s.to_string())),
BoolLiteral(b) => Ok(Bool(b)),
PrefixExp(op, expr) => self.eval_prefix_exp(op, expr),
BinExp(op, lhs, rhs) => self.eval_binexp(op, lhs, rhs),
Value(name) => self.eval_value(name),
TupleLiteral(expressions) => {
let mut evals = Vec::new();
for expr in expressions {
match self.eval_expr(expr) {
Ok(fully_evaluated) => evals.push(fully_evaluated),
error => return error,
}
}
Ok(Tuple(evals))
}
Call { f, arguments } => {
let mut evaled_arguments = Vec::new();
for arg in arguments.into_iter() {
evaled_arguments.push(self.eval_expr(arg)?);
}
self.eval_application(*f, evaled_arguments)
},
Index { box indexee, indexers } => {
let evaled = self.eval_expr(indexee)?;
match evaled {
Tuple(mut exprs) => {
let len = indexers.len();
if len == 1 {
let idx = indexers.into_iter().nth(0).unwrap();
match self.eval_expr(idx)? {
UnsignedInt(n) if (n as usize) < exprs.len() => Ok(exprs.drain(n as usize..).next().unwrap()),
UnsignedInt(n) => Err(format!("Index {} out of range", n)),
other => Err(format!("{:?} is not an unsigned integer", other)),
}
} else {
Err(format!("Tuple index must be one integer"))
}
},
_ => Err(format!("Bad index expression"))
}
},
ListLiteral(items) => Ok(List(items.into_iter().map(|item| self.eval_expr(item)).collect::<Result<Vec<_>,_>>()?)),
x => Err(format!("Unimplemented thing {:?}", x)),
}
}
fn eval_application(&mut self, f: Expression, arguments: Vec<FullyEvaluatedExpr>) -> EvalResult<FullyEvaluatedExpr> {
use self::ExpressionType::*;
match f {
Expression(Value(ref identifier), _) if self.is_builtin(identifier) => self.eval_builtin(identifier, arguments),
Expression(Value(identifier), _) => {
match self.lookup(&identifier) {
Some(&ValueEntry::Function { ref body, ref param_names }) => {
if arguments.len() != param_names.len() {
return Err(format!("Wrong number of arguments for the function"));
}
let mut new_state = State::new_with_parent(self);
let sub_ast = body.clone();
for (param, val) in param_names.iter().zip(arguments.into_iter()) {
new_state.insert(param.clone(), ValueEntry::Binding { val });
}
let mut ret: Option<FullyEvaluatedExpr> = None;
for statement in sub_ast.into_iter() {
ret = new_state.eval_statement(statement)?;
}
Ok(ret.unwrap_or(FullyEvaluatedExpr::Custom { string_rep: Rc::new("()".to_string()) }))
},
_ => Err(format!("Function {} not found", identifier)),
}
},
x => Err(format!("Trying to apply {:?} which is not a function", x)),
}
}
fn is_builtin(&self, name: &Rc<String>) -> bool {
match &name.as_ref()[..] {
"print" | "println" => true,
_ => false
}
}
fn eval_builtin(&mut self, name: &Rc<String>, args: Vec<FullyEvaluatedExpr>) -> EvalResult<FullyEvaluatedExpr> {
use self::FullyEvaluatedExpr::*;
match &name.as_ref()[..] {
"print" => {
for arg in args {
print!("{}", arg.to_string());
}
Ok(Tuple(vec![]))
},
"println" => {
for arg in args {
println!("{}", arg.to_string());
}
Ok(Tuple(vec![]))
},
_ => unreachable!()
}
}
fn eval_value(&mut self, name: Rc<String>) -> EvalResult<FullyEvaluatedExpr> {
use self::ValueEntry::*;
match self.lookup(&name) {
None => return Err(format!("Value {} not found", *name)),
Some(lookup) => match lookup {
&Binding { ref val } => Ok(val.clone()),
&Function { .. } => Ok(FullyEvaluatedExpr::FuncLit(name.clone()))
}
}
}
fn eval_binexp(&mut self, op: BinOp, lhs: Box<Expression>, rhs: Box<Expression>) -> EvalResult<FullyEvaluatedExpr> {
use self::FullyEvaluatedExpr::*;
let evaled_lhs = self.eval_expr(*lhs)?;
let evaled_rhs = self.eval_expr(*rhs)?;
let sigil = op.sigil();
//let sigil: &str = op.sigil().as_ref().as_str();
Ok(match (sigil.as_str(), evaled_lhs, evaled_rhs) {
("+", UnsignedInt(l), UnsignedInt(r)) => UnsignedInt(l + r),
("++", Str(s1), Str(s2)) => Str(format!("{}{}", s1, s2)),
("-", UnsignedInt(l), UnsignedInt(r)) => UnsignedInt(l - r),
("*", UnsignedInt(l), UnsignedInt(r)) => UnsignedInt(l * r),
("/", UnsignedInt(l), UnsignedInt(r)) => Float((l as f64)/ (r as f64)),
("//", UnsignedInt(l), UnsignedInt(r)) => if r == 0 {
return Err(format!("Runtime error: divide by zero"));
} else {
UnsignedInt(l / r)
},
("%", UnsignedInt(l), UnsignedInt(r)) => UnsignedInt(l % r),
("^", UnsignedInt(l), UnsignedInt(r)) => UnsignedInt(l ^ r),
("&", UnsignedInt(l), UnsignedInt(r)) => UnsignedInt(l & r),
("|", UnsignedInt(l), UnsignedInt(r)) => UnsignedInt(l | r),
_ => return Err(format!("Runtime error: not yet implemented")),
})
}
fn eval_prefix_exp(&mut self, op: PrefixOp, expr: Box<Expression>) -> EvalResult<FullyEvaluatedExpr> {
use self::FullyEvaluatedExpr::*;
let evaled_expr = self.eval_expr(*expr)?;
let sigil = op.sigil();
Ok(match (sigil.as_str(), evaled_expr) {
("!", Bool(true)) => Bool(false),
("!", Bool(false)) => Bool(true),
("-", UnsignedInt(n)) => SignedInt(-1*(n as i64)),
("-", SignedInt(n)) => SignedInt(-1*(n as i64)),
("+", SignedInt(n)) => SignedInt(n),
("+", UnsignedInt(n)) => UnsignedInt(n),
_ => return Err(format!("Runtime error: not yet implemented")),
})
}
}

127
schala-lang/src/lib.rs Normal file
View File

@ -0,0 +1,127 @@
#![feature(slice_patterns, box_patterns, box_syntax)]
#![feature(proc_macro)]
extern crate itertools;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate maplit;
extern crate schala_repl;
extern crate schala_codegen;
use std::collections::HashMap;
use itertools::Itertools;
use schala_repl::{ProgrammingLanguageInterface, EvalOptions, TraceArtifact, UnfinishedComputation, FinishedComputation};
macro_rules! bx {
($e:expr) => { Box::new($e) }
}
mod builtin;
mod tokenizing;
mod parsing;
mod typechecking;
mod eval;
use self::typechecking::{TypeContext};
/* TODO eventually custom-derive ProgrammingLanguageInterface with compiler passes as options */
pub struct Schala {
state: eval::State<'static>,
type_context: TypeContext
}
impl Schala {
pub fn new() -> Schala {
Schala {
state: eval::State::new(),
type_context: TypeContext::new(),
}
}
}
impl ProgrammingLanguageInterface for Schala {
schala_codegen::compiler_pass_sequence!(["tokenize", "parse", "yolo"]);
fn get_language_name(&self) -> String {
"Schala".to_string()
}
fn get_source_file_suffix(&self) -> String {
format!("schala")
}
fn execute(&mut self, input: &str, options: &EvalOptions) -> FinishedComputation {
let mut evaluation = UnfinishedComputation::default();
//tokenzing
let tokens = tokenizing::tokenize(input);
if options.debug.tokens {
let token_string = tokens.iter().map(|t| format!("{:?}<L:{},C:{}>", t.token_type, t.offset.0, t.offset.1)).join(", ");
evaluation.add_artifact(TraceArtifact::new("tokens", token_string));
}
{
let token_errors: Vec<&String> = tokens.iter().filter_map(|t| t.get_error()).collect();
if token_errors.len() != 0 {
return evaluation.output(Err(format!("Tokenization error: {:?}\n", token_errors)));
}
}
// parsing
let ast = match parsing::parse(tokens) {
(Ok(ast), trace) => {
if options.debug.parse_tree {
evaluation.add_artifact(TraceArtifact::new_parse_trace(trace));
}
if options.debug.ast {
evaluation.add_artifact(TraceArtifact::new("ast", format!("{:#?}", ast)));
}
ast
},
(Err(err), trace) => {
if options.debug.parse_tree {
evaluation.add_artifact(TraceArtifact::new_parse_trace(trace));
}
return evaluation.output(Err(format!("Parse error: {:?}\n", err.msg)));
}
};
//symbol table
match self.type_context.add_top_level_types(&ast) {
Ok(()) => (),
Err(msg) => {
if options.debug.type_checking {
evaluation.add_artifact(TraceArtifact::new("type_check", msg));
}
}
};
//typechecking
match self.type_context.type_check_ast(&ast) {
Ok(ty) => {
if options.debug.type_checking {
evaluation.add_artifact(TraceArtifact::new("type_check", format!("{:?}", ty)));
}
},
Err(msg) => evaluation.add_artifact(TraceArtifact::new("type_check", msg)),
};
let text = self.type_context.debug_symbol_table();
if options.debug.symbol_table {
evaluation.add_artifact(TraceArtifact::new("symbol_table", text));
}
let evaluation_outputs = self.state.evaluate(ast);
let text_output: Result<Vec<String>, String> = evaluation_outputs
.into_iter()
.collect();
let eval_output = text_output
.map(|v| { v.into_iter().intersperse(format!("\n")).collect() });
evaluation.output(eval_output)
}
}

1235
schala-lang/src/parsing.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,314 @@
use itertools::Itertools;
use std::collections::HashMap;
use std::rc::Rc;
use std::iter::{Iterator, Peekable};
use std::fmt;
use ::schala_codegen;
#[derive(Debug, PartialEq, Clone)]
pub enum TokenType {
Newline, Semicolon,
LParen, RParen,
LSquareBracket, RSquareBracket,
LAngleBracket, RAngleBracket,
LCurlyBrace, RCurlyBrace,
Pipe,
Comma, Period, Colon, Underscore,
Slash,
Operator(Rc<String>),
DigitGroup(Rc<String>), HexLiteral(Rc<String>), BinNumberSigil,
StrLiteral(Rc<String>),
Identifier(Rc<String>),
Keyword(Kw),
EOF,
Error(String),
}
use self::TokenType::*;
impl fmt::Display for TokenType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&Operator(ref s) => write!(f, "Operator({})", **s),
&DigitGroup(ref s) => write!(f, "DigitGroup({})", s),
&HexLiteral(ref s) => write!(f, "HexLiteral({})", s),
&StrLiteral(ref s) => write!(f, "StrLiteral({})", s),
&Identifier(ref s) => write!(f, "Identifier({})", s),
&Error(ref s) => write!(f, "Error({})", s),
other => write!(f, "{:?}", other),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Kw {
If, Else,
Func,
For,
Match,
Var, Const, Let, In,
Return,
Alias, Type, SelfType, SelfIdent,
Interface, Impl,
True, False,
Module
}
lazy_static! {
static ref KEYWORDS: HashMap<&'static str, Kw> =
hashmap! {
"if" => Kw::If,
"else" => Kw::Else,
"fn" => Kw::Func,
"for" => Kw::For,
"match" => Kw::Match,
"var" => Kw::Var,
"const" => Kw::Const,
"let" => Kw::Let,
"in" => Kw::In,
"return" => Kw::Return,
"alias" => Kw::Alias,
"type" => Kw::Type,
"Self" => Kw::SelfType,
"self" => Kw::SelfIdent,
"interface" => Kw::Interface,
"impl" => Kw::Impl,
"true" => Kw::True,
"false" => Kw::False,
"module" => Kw::Module,
};
}
#[derive(Debug, Clone)]
pub struct Token {
pub token_type: TokenType,
pub offset: (usize, usize),
}
impl Token {
pub fn get_error(&self) -> Option<&String> {
match self.token_type {
TokenType::Error(ref s) => Some(s),
_ => None,
}
}
pub fn to_string_with_metadata(&self) -> String {
format!("{}(L:{},c:{})", self.token_type, self.offset.0, self.offset.1)
}
}
const OPERATOR_CHARS: [char; 18] = ['!', '$', '%', '&', '*', '+', '-', '.', ':', '<', '>', '=', '?', '@', '^', '|', '~', '`'];
fn is_operator(c: &char) -> bool {
OPERATOR_CHARS.iter().any(|x| x == c)
}
type CharIter<I: Iterator<Item=(usize,usize,char)>> = Peekable<I>;
#[schala_codegen::compiler_pass = "tokenization"]
pub fn tokenize(input: &str) -> Vec<Token> {
let mut tokens: Vec<Token> = Vec::new();
let mut input = input.lines().enumerate()
.intersperse((0, "\n"))
.flat_map(|(line_idx, ref line)| {
line.chars().enumerate().map(move |(ch_idx, ch)| (line_idx, ch_idx, ch))
})
.peekable();
while let Some((line_idx, ch_idx, c)) = input.next() {
let cur_tok_type = match c {
'/' => match input.peek().map(|t| t.2) {
Some('/') => {
while let Some((_, _, c)) = input.next() {
if c == '\n' {
break;
}
}
continue;
},
Some('*') => {
input.next();
let mut comment_level = 1;
while let Some((_, _, c)) = input.next() {
if c == '*' && input.peek().map(|t| t.2) == Some('/') {
input.next();
comment_level -= 1;
} else if c == '/' && input.peek().map(|t| t.2) == Some('*') {
input.next();
comment_level += 1;
}
if comment_level == 0 {
break;
}
}
continue;
},
_ => Slash
},
c if c.is_whitespace() && c != '\n' => continue,
'\n' => Newline, ';' => Semicolon,
':' => Colon, ',' => Comma,
'(' => LParen, ')' => RParen,
'{' => LCurlyBrace, '}' => RCurlyBrace,
'[' => LSquareBracket, ']' => RSquareBracket,
'"' => handle_quote(&mut input),
c if c.is_digit(10) => handle_digit(c, &mut input),
c if c.is_alphabetic() || c == '_' => handle_alphabetic(c, &mut input), //TODO I'll probably have to rewrite this if I care about types being uppercase, also type parameterization
c if is_operator(&c) => handle_operator(c, &mut input),
unknown => Error(format!("Unexpected character: {}", unknown)),
};
tokens.push(Token { token_type: cur_tok_type, offset: (line_idx, ch_idx) });
}
tokens
}
fn handle_digit<I: Iterator<Item=(usize,usize,char)>>(c: char, input: &mut CharIter<I>) -> TokenType {
if c == '0' && input.peek().map_or(false, |&(_, _, c)| { c == 'x' }) {
input.next();
let rest: String = input.peeking_take_while(|&(_, _, ref c)| c.is_digit(16) || *c == '_').map(|(_, _, c)| { c }).collect();
HexLiteral(Rc::new(rest))
} else if c == '0' && input.peek().map_or(false, |&(_, _, c)| { c == 'b' }) {
input.next();
BinNumberSigil
} else {
let mut buf = c.to_string();
buf.extend(input.peeking_take_while(|&(_, _, ref c)| c.is_digit(10)).map(|(_, _, c)| { c }));
DigitGroup(Rc::new(buf))
}
}
fn handle_quote<I: Iterator<Item=(usize,usize,char)>>(input: &mut CharIter<I>) -> TokenType {
let mut buf = String::new();
loop {
match input.next().map(|(_, _, c)| { c }) {
Some('"') => break,
Some('\\') => {
let next = input.peek().map(|&(_, _, c)| { c });
if next == Some('n') {
input.next();
buf.push('\n')
} else if next == Some('"') {
input.next();
buf.push('"');
} else if next == Some('t') {
input.next();
buf.push('\t');
}
},
Some(c) => buf.push(c),
None => return TokenType::Error(format!("Unclosed string")),
}
}
TokenType::StrLiteral(Rc::new(buf))
}
fn handle_alphabetic<I: Iterator<Item=(usize,usize,char)>>(c: char, input: &mut CharIter<I>) -> TokenType {
let mut buf = String::new();
buf.push(c);
if c == '_' && input.peek().map(|&(_, _, c)| { !c.is_alphabetic() }).unwrap_or(true) {
return TokenType::Underscore
}
loop {
match input.peek().map(|&(_, _, c)| { c }) {
Some(c) if c.is_alphanumeric() => {
input.next();
buf.push(c);
},
_ => break,
}
}
match KEYWORDS.get(buf.as_str()) {
Some(kw) => TokenType::Keyword(*kw),
None => TokenType::Identifier(Rc::new(buf)),
}
}
fn handle_operator<I: Iterator<Item=(usize,usize,char)>>(c: char, input: &mut CharIter<I>) -> TokenType {
match c {
'<' | '>' | '|' | '.' => {
let ref next = input.peek().map(|&(_, _, c)| { c });
if !next.map(|n| { is_operator(&n) }).unwrap_or(false) {
return match c {
'<' => LAngleBracket,
'>' => RAngleBracket,
'|' => Pipe,
'.' => Period,
_ => unreachable!(),
}
}
},
_ => (),
};
let mut buf = String::new();
if c == '`' {
loop {
match input.peek().map(|&(_, _, c)| { c }) {
Some(c) if c.is_alphabetic() || c == '_' => {
input.next();
buf.push(c);
},
Some('`') => {
input.next();
break;
},
_ => break
}
}
} else {
buf.push(c);
loop {
match input.peek().map(|&(_, _, c)| { c }) {
Some(c) if is_operator(&c) => {
input.next();
buf.push(c);
},
_ => break
}
}
}
TokenType::Operator(Rc::new(buf))
}
#[cfg(test)]
mod schala_tokenizer_tests {
use super::*;
use super::Kw::*;
macro_rules! digit { ($ident:expr) => { DigitGroup(Rc::new($ident.to_string())) } }
macro_rules! ident { ($ident:expr) => { Identifier(Rc::new($ident.to_string())) } }
macro_rules! op { ($ident:expr) => { Operator(Rc::new($ident.to_string())) } }
#[test]
fn tokens() {
let a = tokenize("let a: A<B> = c ++ d");
let token_types: Vec<TokenType> = a.into_iter().map(move |t| t.token_type).collect();
assert_eq!(token_types, vec![Keyword(Let), ident!("a"), Colon, ident!("A"),
LAngleBracket, ident!("B"), RAngleBracket, op!("="), ident!("c"), op!("++"), ident!("d")]);
}
#[test]
fn underscores() {
let token_types: Vec<TokenType> = tokenize("4_8").into_iter().map(move |t| t.token_type).collect();
assert_eq!(token_types, vec![digit!("4"), Underscore, digit!("8")]);
}
#[test]
fn comments() {
let token_types: Vec<TokenType> = tokenize("1 + /* hella /* bro */ */ 2").into_iter().map(move |t| t.token_type).collect();
assert_eq!(token_types, vec![digit!("1"), op!("+"), digit!("2")]);
}
#[test]
fn backtick_operators() {
let token_types: Vec<TokenType> = tokenize("1 `plus` 2").into_iter().map(move |t| t.token_type).collect();
assert_eq!(token_types, vec![digit!("1"), op!("plus"), digit!("2")]);
}
}

View File

@ -0,0 +1,445 @@
use std::collections::HashMap;
use std::rc::Rc;
use parsing::{AST, Statement, Declaration, Signature, Expression, ExpressionType, Operation, Variant, TypeName, TypeSingletonName};
// from Niko's talk
/* fn type_check(expression, expected_ty) -> Ty {
let ty = bare_type_check(expression, expected_type);
if ty icompatible with expected_ty {
try_coerce(expression, ty, expected_ty)
} else {
ty
}
}
fn bare_type_check(exprssion, expected_type) -> Ty { ... }
*/
/* H-M ALGO NOTES
from https://www.youtube.com/watch?v=il3gD7XMdmA
(also check out http://dev.stephendiehl.com/fun/006_hindley_milner.html)
typeInfer :: Expr a -> Matching (Type a)
unify :: Type a -> Type b -> Matching (Type c)
(Matching a) is a monad in which unification is done
ex:
typeInfer (If e1 e2 e3) = do
t1 <- typeInfer e1
t2 <- typeInfer e2
t3 <- typeInfer e3
_ <- unify t1 BoolType
unify t2 t3 -- b/c t2 and t3 have to be the same type
typeInfer (Const (ConstInt _)) = IntType -- same for other literals
--function application
typeInfer (Apply f x) = do
tf <- typeInfer f
tx <- typeInfer x
case tf of
FunctionType t1 t2 -> do
_ <- unify t1 tx
return t2
_ -> fail "Not a function"
--type annotation
typeInfer (Typed x t) = do
tx <- typeInfer x
unify tx t
--variable and let expressions - need to pass around a map of variable names to types here
typeInfer :: [ (Var, Type Var) ] -> Expr Var -> Matching (Type Var)
typeInfer ctx (Var x) = case (lookup x ctx) of
Just t -> return t
Nothing -> fail "Unknown variable"
--let x = e1 in e2
typeInfer ctx (Let x e1 e2) = do
t1 <- typeInfer ctx e1
typeInfer ((x, t1) :: ctx) e2
--lambdas are complicated (this represents ʎx.e)
typeInfer ctx (Lambda x e) = do
t1 <- allocExistentialVariable
t2 <- typeInfer ((x, t1) :: ctx) e
return $ FunctionType t1 t2 -- ie. t1 -> t2
--to solve the problem of map :: (a -> b) -> [a] -> [b]
when we use a variable whose type has universal tvars, convert those universal
tvars to existential ones
-and each distinct universal tvar needs to map to the same existential type
-so we change typeinfer:
typeInfer ctx (Var x) = do
case (lookup x ctx) of
Nothing -> ...
Just t -> do
let uvars = nub (toList t) -- nub removes duplicates, so this gets unique universally quantified variables
evars <- mapM (const allocExistentialVariable) uvars
let varMap = zip uvars evars
let vixVar varMap v = fromJust $ lookup v varMap
return (fmap (fixVar varMap) t)
--how do we define unify??
-recall, type signature is:
unify :: Type a -> Type b -> Matching (Type c)
unify BoolType BoolType = BoolType --easy, same for all constants
unify (FunctionType t1 t2) (FunctionType t3 t4) = do
t5 <- unify t1 t3
t6 <- unify t2 t4
return $ FunctionType t5 t6
unify (TVar a) (TVar b) = if a == b then TVar a else fail
--existential types can be assigned another type at most once
--some complicated stuff about hanlding existential types
--everything else is a type error
unify a b = fail
SKOLEMIZATION - how you prevent an unassigned existential type variable from leaking!
-before a type gets to global scope, replace all unassigned existential vars w/ new unique universal
type variables
*/
#[derive(Debug, PartialEq, Clone)]
pub enum Type {
TVar(TypeVar),
TConst(TypeConst),
TFunc(Box<Type>, Box<Type>),
}
#[derive(Debug, PartialEq, Clone)]
pub enum TypeVar {
Univ(Rc<String>),
Exist(u64),
}
impl TypeVar {
fn univ(label: &str) -> TypeVar {
TypeVar::Univ(Rc::new(label.to_string()))
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum TypeConst {
UserT(Rc<String>),
Integer,
Float,
StringT,
Boolean,
Unit,
Bottom,
}
type TypeCheckResult = Result<Type, String>;
#[derive(Debug, PartialEq, Eq, Hash)]
struct PathSpecifier(Rc<String>);
#[derive(Debug, PartialEq, Clone)]
struct TypeContextEntry {
ty: Type,
constant: bool
}
pub struct TypeContext {
symbol_table: HashMap<PathSpecifier, TypeContextEntry>,
evar_table: HashMap<u64, Type>,
existential_type_label_count: u64
}
impl TypeContext {
pub fn new() -> TypeContext {
TypeContext {
symbol_table: HashMap::new(),
evar_table: HashMap::new(),
existential_type_label_count: 0,
}
}
pub fn add_symbols(&mut self, ast: &AST) {
use self::Declaration::*;
use self::Type::*;
use self::TypeConst::*;
for statement in ast.0.iter() {
match *statement {
Statement::ExpressionStatement(_) => (),
Statement::Declaration(ref decl) => match *decl {
FuncSig(_) => (),
Impl { .. } => (),
TypeDecl(ref type_constructor, ref body) => {
for variant in body.0.iter() {
let (spec, ty) = match variant {
&Variant::UnitStruct(ref data_constructor) => {
let spec = PathSpecifier(data_constructor.clone());
let ty = TConst(UserT(type_constructor.name.clone()));
(spec, ty)
},
&Variant::TupleStruct(ref data_construcor, ref args) => {
//TODO fix
let arg = args.get(0).unwrap();
let type_arg = self.from_anno(arg);
let spec = PathSpecifier(data_construcor.clone());
let ty = TFunc(Box::new(type_arg), Box::new(TConst(UserT(type_constructor.name.clone()))));
(spec, ty)
},
&Variant::Record(_, _) => unimplemented!(),
};
let entry = TypeContextEntry { ty, constant: true };
self.symbol_table.insert(spec, entry);
}
},
TypeAlias { .. } => (),
Binding {ref name, ref constant, ref expr} => {
let spec = PathSpecifier(name.clone());
let ty = expr.1.as_ref()
.map(|ty| self.from_anno(ty))
.unwrap_or_else(|| { self.alloc_existential_type() }); // this call to alloc_existential is OK b/c a binding only ever has one type, so if the annotation is absent, it's fine to just make one de novo
let entry = TypeContextEntry { ty, constant: *constant };
self.symbol_table.insert(spec, entry);
},
FuncDecl(ref signature, _) => {
let spec = PathSpecifier(signature.name.clone());
let ty = self.from_signature(signature);
let entry = TypeContextEntry { ty, constant: true };
self.symbol_table.insert(spec, entry);
},
}
}
}
}
fn lookup(&mut self, binding: &Rc<String>) -> Option<TypeContextEntry> {
let key = PathSpecifier(binding.clone());
self.symbol_table.get(&key).map(|entry| entry.clone())
}
pub fn debug_symbol_table(&self) -> String {
format!("Symbol table:\n {:?}\nEvar table:\n{:?}", self.symbol_table, self.evar_table)
}
fn alloc_existential_type(&mut self) -> Type {
let ret = Type::TVar(TypeVar::Exist(self.existential_type_label_count));
self.existential_type_label_count += 1;
ret
}
fn from_anno(&mut self, anno: &TypeName) -> Type {
use self::Type::*;
use self::TypeConst::*;
match anno {
&TypeName::Singleton(TypeSingletonName { ref name, .. }) => {
match name.as_ref().as_ref() {
"Int" => TConst(Integer),
"Float" => TConst(Float),
"Bool" => TConst(Boolean),
"String" => TConst(StringT),
s => TVar(TypeVar::Univ(Rc::new(format!("{}",s)))),
}
},
&TypeName::Tuple(ref items) => {
if items.len() == 1 {
TConst(Unit)
} else {
TConst(Bottom)
}
}
}
}
fn from_signature(&mut self, sig: &Signature) -> Type {
use self::Type::*;
use self::TypeConst::*;
//TODO this won't work properly until you make sure that all (universal) type vars in the function have the same existential type var
// actually this should never even put existential types into the symbol table at all
//this will crash if more than 5 arg function is used
let names = vec!["a", "b", "c", "d", "e", "f"];
let mut idx = 0;
let mut get_type = || { let q = TVar(TypeVar::Univ(Rc::new(format!("{}", names.get(idx).unwrap())))); idx += 1; q };
let return_type = sig.type_anno.as_ref().map(|anno| self.from_anno(&anno)).unwrap_or_else(|| { get_type() });
if sig.params.len() == 0 {
TFunc(Box::new(TConst(Unit)), Box::new(return_type))
} else {
let mut output_type = return_type;
for p in sig.params.iter() {
let p_type = p.1.as_ref().map(|anno| self.from_anno(anno)).unwrap_or_else(|| { get_type() });
output_type = TFunc(Box::new(p_type), Box::new(output_type));
}
output_type
}
}
pub fn type_check(&mut self, ast: &AST) -> TypeCheckResult {
use self::Type::*;
use self::TypeConst::*;
let mut last = TConst(Unit);
for statement in ast.0.iter() {
match statement {
&Statement::Declaration(ref _decl) => {
//return Err(format!("Declarations not supported"));
},
&Statement::ExpressionStatement(ref expr) => {
last = self.infer(expr)?;
}
}
}
Ok(last)
}
fn infer(&mut self, expr: &Expression) -> TypeCheckResult {
match (&expr.0, &expr.1) {
(exprtype, &Some(ref anno)) => {
let tx = self.infer_no_anno(exprtype)?;
let ty = self.from_anno(anno);
self.unify(tx, ty)
},
(exprtype, &None) => self.infer_no_anno(exprtype),
}
}
fn infer_no_anno(&mut self, ex: &ExpressionType) -> TypeCheckResult {
use self::ExpressionType::*;
use self::Type::*;
use self::TypeConst::*;
Ok(match ex {
&IntLiteral(_) => TConst(Integer),
&FloatLiteral(_) => TConst(Float),
&StringLiteral(_) => TConst(StringT),
&BoolLiteral(_) => TConst(Boolean),
&Value(ref name, _) => {
self.lookup(name)
.map(|entry| entry.ty)
.ok_or(format!("Couldn't find {}", name))?
},
&BinExp(ref op, ref lhs, ref rhs) => {
let t_lhs = self.infer(lhs)?;
match self.infer_op(op)? {
TFunc(t1, t2) => {
let _ = self.unify(t_lhs, *t1)?;
let t_rhs = self.infer(rhs)?;
let x = *t2;
match x {
TFunc(t3, t4) => {
let _ = self.unify(t_rhs, *t3)?;
*t4
},
_ => return Err(format!("Not a function type either")),
}
},
_ => return Err(format!("Op {:?} is not a function type", op)),
}
},
&Call { ref f, ref arguments } => {
let tf = self.infer(f)?;
let targ = self.infer(arguments.get(0).unwrap())?;
match tf {
TFunc(box t1, box t2) => {
let _ = self.unify(t1, targ)?;
t2
},
_ => return Err(format!("Not a function!")),
}
},
_ => TConst(Bottom),
})
}
fn infer_op(&mut self, op: &Operation) -> TypeCheckResult {
use self::Type::*;
use self::TypeConst::*;
macro_rules! binoptype {
($lhs:expr, $rhs:expr, $out:expr) => { TFunc(Box::new($lhs), Box::new(TFunc(Box::new($rhs), Box::new($out)))) };
}
Ok(match (*op.0).as_ref() {
"+" => binoptype!(TConst(Integer), TConst(Integer), TConst(Integer)),
"++" => binoptype!(TConst(StringT), TConst(StringT), TConst(StringT)),
"-" => binoptype!(TConst(Integer), TConst(Integer), TConst(Integer)),
"*" => binoptype!(TConst(Integer), TConst(Integer), TConst(Integer)),
"/" => binoptype!(TConst(Integer), TConst(Integer), TConst(Integer)),
"%" => binoptype!(TConst(Integer), TConst(Integer), TConst(Integer)),
_ => TConst(Bottom)
})
}
fn unify(&mut self, t1: Type, t2: Type) -> TypeCheckResult {
use self::Type::*;
use self::TypeVar::*;
println!("Calling unify with `{:?}` and `{:?}`", t1, t2);
match (&t1, &t2) {
(&TConst(ref c1), &TConst(ref c2)) if c1 == c2 => Ok(TConst(c1.clone())),
(&TFunc(ref t1, ref t2), &TFunc(ref t3, ref t4)) => {
let t5 = self.unify(*t1.clone().clone(), *t3.clone().clone())?;
let t6 = self.unify(*t2.clone().clone(), *t4.clone().clone())?;
Ok(TFunc(Box::new(t5), Box::new(t6)))
},
(&TVar(Univ(ref a)), &TVar(Univ(ref b))) => {
if a == b {
Ok(TVar(Univ(a.clone())))
} else {
Err(format!("Couldn't unify universal types {} and {}", a, b))
}
},
//the interesting case!!
(&TVar(Exist(ref a)), ref t2) => {
let x = self.evar_table.get(a).map(|x| x.clone());
match x {
Some(ref t1) => self.unify(t1.clone().clone(), t2.clone().clone()),
None => {
self.evar_table.insert(*a, t2.clone().clone());
Ok(t2.clone().clone())
}
}
},
(ref t1, &TVar(Exist(ref a))) => {
let x = self.evar_table.get(a).map(|x| x.clone());
match x {
Some(ref t2) => self.unify(t2.clone().clone(), t1.clone().clone()),
None => {
self.evar_table.insert(*a, t1.clone().clone());
Ok(t1.clone().clone())
}
}
},
_ => Err(format!("Types {:?} and {:?} don't unify", t1, t2))
}
}
}
#[cfg(test)]
mod tests {
use super::{Type, TypeVar, TypeConst, TypeContext};
use super::Type::*;
use super::TypeConst::*;
use schala_lang::parsing::{parse, tokenize};
macro_rules! type_test {
($input:expr, $correct:expr) => {
{
let mut tc = TypeContext::new();
let ast = parse(tokenize($input)).0.unwrap() ;
tc.add_symbols(&ast);
assert_eq!($correct, tc.type_check(&ast).unwrap())
}
}
}
#[test]
fn basic_inference() {
type_test!("30", TConst(Integer));
type_test!("fn x(a: Int): Bool {}; x(1)", TConst(Boolean));
}
}

View File

@ -0,0 +1,254 @@
use std::rc::Rc;
use std::collections::HashMap;
use std::char;
use std::fmt;
use std::fmt::Write;
use itertools::Itertools;
use parsing;
pub struct TypeContext {
type_var_count: u64,
bindings: HashMap<Rc<String>, Type>,
}
#[derive(Debug, PartialEq, Clone)]
pub enum Type {
Const(TConst),
Sum(Vec<Type>),
Func(Box<Type>, Box<Type>),
UVar(String),
EVar(u64),
Void
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Type::*;
match self {
&Const(ref c) => write!(f, "{:?}", c),
&Sum(ref types) => {
write!(f, "(")?;
for item in types.iter().map(|ty| Some(ty)).intersperse(None) {
match item {
Some(ty) => write!(f, "{}", ty)?,
None => write!(f, ",")?,
};
}
write!(f, ")")
},
&Func(ref a, ref b) => write!(f, "{} -> {}", a, b),
&UVar(ref s) => write!(f, "{}_u", s),
&EVar(ref n) => write!(f, "{}_e", n),
&Void => write!(f, "Void")
}
}
}
#[derive(Default)]
struct UVarGenerator {
n: u32,
}
impl UVarGenerator {
fn new() -> UVarGenerator {
UVarGenerator::default()
}
fn next(&mut self) -> Type {
//TODO handle this in the case where someone wants to make a function with more than 26 variables
let s = format!("{}", unsafe { char::from_u32_unchecked(self.n + ('a' as u32)) });
self.n += 1;
Type::UVar(s)
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum TConst {
Unit,
Int,
Float,
StringT,
Bool,
Custom(String),
}
impl parsing::TypeName {
fn to_type(&self) -> TypeResult<Type> {
use self::parsing::TypeSingletonName;
use self::parsing::TypeName::*;
use self::Type::*; use self::TConst::*;
Ok(match self {
&Tuple(_) => return Err(format!("Tuples not yet implemented")),
&Singleton(ref name) => match name {
&TypeSingletonName { ref name, .. } => match &name[..] {
"Int" => Const(Int),
"Float" => Const(Float),
"Bool" => Const(Bool),
"String" => Const(StringT),
n => Const(Custom(n.to_string()))
}
}
})
}
}
pub type TypeResult<T> = Result<T, String>;
impl TypeContext {
pub fn new() -> TypeContext {
TypeContext { bindings: HashMap::new(), type_var_count: 0 }
}
pub fn fresh(&mut self) -> Type {
let ret = self.type_var_count;
self.type_var_count += 1;
Type::EVar(ret)
}
}
impl TypeContext {
pub fn add_top_level_types(&mut self, ast: &parsing::AST) -> TypeResult<()> {
use self::parsing::TypeName;
use self::parsing::Declaration::*;
use self::Type::*;
for statement in ast.0.iter() {
if let &self::parsing::Statement::Declaration(ref decl) = statement {
match decl {
&FuncSig(ref signature) | &FuncDecl(ref signature, _) => {
let mut uvar_gen = UVarGenerator::new();
let mut ty: Type = signature.type_anno.as_ref().map(|name: &TypeName| name.to_type()).unwrap_or_else(|| {Ok(uvar_gen.next())} )?;
for &(_, ref type_name) in signature.params.iter().rev() {
let arg_type = type_name.as_ref().map(|name| name.to_type()).unwrap_or_else(|| {Ok(uvar_gen.next())} )?;
ty = Func(bx!(arg_type), bx!(ty));
}
self.bindings.insert(signature.name.clone(), ty);
},
_ => ()
}
}
}
Ok(())
}
pub fn debug_symbol_table(&self) -> String {
let mut output = format!("Symbols\n");
for (sym, ty) in &self.bindings {
write!(output, "{} : {}\n", sym, ty).unwrap();
}
output
}
}
impl TypeContext {
pub fn type_check_ast(&mut self, ast: &parsing::AST) -> TypeResult<Type> {
use self::Type::*; use self::TConst::*;
let mut ret_type = Const(Unit);
for statement in ast.0.iter() {
ret_type = self.type_check_statement(statement)?;
}
Ok(ret_type)
}
fn type_check_statement(&mut self, statement: &parsing::Statement) -> TypeResult<Type> {
use self::parsing::Statement::*;
match statement {
&ExpressionStatement(ref expr) => self.infer(expr),
&Declaration(ref decl) => self.add_declaration(decl),
}
}
fn add_declaration(&mut self, decl: &parsing::Declaration) -> TypeResult<Type> {
use self::parsing::Declaration::*;
use self::Type::*;
match decl {
&Binding { ref name, ref expr, .. } => {
let ty = self.infer(expr)?;
self.bindings.insert(name.clone(), ty);
},
_ => return Err(format!("other formats not done"))
}
Ok(Void)
}
fn infer(&mut self, expr: &parsing::Expression) -> TypeResult<Type> {
use self::parsing::Expression;
match expr {
&Expression(ref e, Some(ref anno)) => {
let anno_ty = anno.to_type()?;
let ty = self.infer_exprtype(&e)?;
self.unify(ty, anno_ty)
},
&Expression(ref e, None) => self.infer_exprtype(e)
}
}
fn infer_exprtype(&mut self, expr: &parsing::ExpressionType) -> TypeResult<Type> {
use self::parsing::ExpressionType::*;
use self::Type::*; use self::TConst::*;
match expr {
&IntLiteral(_) => Ok(Const(Int)),
&FloatLiteral(_) => Ok(Const(Float)),
&StringLiteral(_) => Ok(Const(StringT)),
&BoolLiteral(_) => Ok(Const(Bool)),
&BinExp(ref op, ref lhs, ref rhs) => { /* remember there are both the haskell convention talk and the write you a haskell ways to do this! */
match op.get_type()? {
Func(box t1, box Func(box t2, box t3)) => {
let lhs_ty = self.infer(lhs)?;
let rhs_ty = self.infer(rhs)?;
self.unify(t1, lhs_ty)?;
self.unify(t2, rhs_ty)?;
Ok(t3)
},
other => Err(format!("{:?} is not a binary function type", other))
}
},
&PrefixExp(ref op, ref expr) => match op.get_type()? {
Func(box t1, box t2) => {
let expr_ty = self.infer(expr)?;
self.unify(t1, expr_ty)?;
Ok(t2)
},
other => Err(format!("{:?} is not a prefix op function type", other))
},
&Value(ref name) => {
match self.bindings.get(name) {
Some(ty) => Ok(ty.clone()),
None => Err(format!("No binding found for variable: {}", name)),
}
},
&Call { ref f, ref arguments } => {
let mut tf = self.infer(f)?;
for arg in arguments.iter() {
match tf {
Func(box t, box rest) => {
let t_arg = self.infer(arg)?;
self.unify(t, t_arg)?;
tf = rest;
},
other => return Err(format!("Function call failed to unify; last type: {:?}", other)),
}
}
Ok(tf)
},
&TupleLiteral(ref expressions) => {
let mut types = vec![];
for expr in expressions {
types.push(self.infer(expr)?);
}
Ok(Sum(types))
},
/*
Index {
indexee: Box<Expression>,
indexers: Vec<Expression>,
},
IfExpression(Box<Expression>, Vec<Statement>, Option<Vec<Statement>>),
MatchExpression(Box<Expression>, Vec<MatchArm>),
ForExpression
*/
_ => Err(format!("Type not yet implemented"))
}
}
fn unify(&mut self, t1: Type, t2: Type) -> TypeResult<Type> {
use self::Type::*;// use self::TConst::*;
match (t1, t2) {
(Const(ref a), Const(ref b)) if a == b => Ok(Const(a.clone())),
(a, b) => Err(format!("Types {:?} and {:?} don't unify", a, b))
}
}
}

25
schala-repl/Cargo.toml Normal file
View File

@ -0,0 +1,25 @@
[package]
name = "schala-repl"
version = "0.1.0"
authors = ["greg <greg.shuflin@protonmail.com>"]
[dependencies]
llvm-sys = "*"
take_mut = "0.1.3"
itertools = "0.5.8"
getopts = "*"
lazy_static = "0.2.8"
maplit = "*"
colored = "1.5"
serde = "1.0.15"
serde_derive = "1.0.15"
serde_json = "1.0.3"
rocket = "0.3.5"
rocket_codegen = "0.3.5"
rocket_contrib = "0.3.5"
phf = "0.7.12"
includedir = "0.2.0"
rustyline = "1.0.0"
[build-dependencies]
includedir_codegen = "0.2.0"

10
schala-repl/build.rs Normal file
View File

@ -0,0 +1,10 @@
extern crate includedir_codegen;
use includedir_codegen::Compression;
fn main() {
includedir_codegen::start("WEBFILES")
.dir("../static", Compression::Gzip)
.build("static.rs")
.unwrap();
}

181
schala-repl/src/language.rs Normal file
View File

@ -0,0 +1,181 @@
use std::collections::HashMap;
use colored::*;
use std::fmt::Write;
pub struct LLVMCodeString(pub String);
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct EvalOptions {
pub debug: DebugOptions,
pub execution_method: ExecutionMethod
}
#[derive(Debug, Serialize, Deserialize)]
pub enum ExecutionMethod {
Compile,
Interpret,
}
impl Default for ExecutionMethod {
fn default() -> ExecutionMethod {
ExecutionMethod::Interpret
}
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct DebugOptions {
pub tokens: bool,
pub parse_tree: bool,
pub ast: bool,
pub type_checking: bool,
pub symbol_table: bool,
pub evaluation: bool,
pub llvm_ir: bool,
}
#[derive(Debug, Default)]
pub struct LanguageOutput {
output: String,
artifacts: Vec<TraceArtifact>,
pub failed: bool,
}
impl LanguageOutput {
pub fn add_artifact(&mut self, artifact: TraceArtifact) {
self.artifacts.push(artifact);
}
pub fn add_output(&mut self, output: String) {
self.output = output;
}
pub fn to_string(&self) -> String {
let mut acc = String::new();
for line in self.artifacts.iter() {
acc.push_str(&line.debug_output.color(line.text_color).to_string());
acc.push_str(&"\n");
}
acc.push_str(&self.output);
acc
}
pub fn print_to_screen(&self) {
for line in self.artifacts.iter() {
let color = line.text_color;
let stage = line.stage_name.color(color).to_string();
let output = line.debug_output.color(color).to_string();
println!("{}: {}", stage, output);
}
println!("{}", self.output);
}
}
#[derive(Debug, Default)]
pub struct UnfinishedComputation {
artifacts: HashMap<String, TraceArtifact>,
}
#[derive(Debug)]
pub struct FinishedComputation {
artifacts: HashMap<String, TraceArtifact>,
text_output: Result<String, String>,
}
impl UnfinishedComputation {
pub fn add_artifact(&mut self, artifact: TraceArtifact) {
self.artifacts.insert(artifact.stage_name.clone(), artifact);
}
pub fn output(self, output: Result<String, String>) -> FinishedComputation {
FinishedComputation {
artifacts: self.artifacts,
text_output: output
}
}
}
impl FinishedComputation {
pub fn to_repl(&self) -> String {
let mut buf = String::new();
for stage in ["tokens", "parse_trace", "ast", "symbol_table", "type_check"].iter() {
if let Some(artifact) = self.artifacts.get(&stage.to_string()) {
let color = artifact.text_color;
let stage = stage.color(color).bold();
let output = artifact.debug_output.color(color);
write!(&mut buf, "{}: {}\n", stage, output).unwrap();
}
}
match self.text_output {
Ok(ref output) => write!(&mut buf, "{}", output).unwrap(),
Err(ref err) => write!(&mut buf, "{} {}", "Error: ".red().bold(), err).unwrap(),
}
buf
}
pub fn to_noninteractive(&self) -> Option<String> {
match self.text_output {
Ok(_) => {
let mut buf = String::new();
for stage in ["tokens", "parse_trace", "ast", "symbol_table", "type_check"].iter() {
if let Some(artifact) = self.artifacts.get(&stage.to_string()) {
let color = artifact.text_color;
let stage = stage.color(color).bold();
let output = artifact.debug_output.color(color);
write!(&mut buf, "{}: {}\n", stage, output).unwrap();
}
}
if buf == "" { None } else { Some(buf) }
},
Err(ref s) => Some(format!("{} {}", "Error: ".red().bold(), s))
}
}
}
#[derive(Debug)]
pub struct TraceArtifact {
stage_name: String,
debug_output: String,
text_color: &'static str,
}
impl TraceArtifact {
pub fn new(stage: &str, debug: String) -> TraceArtifact {
let color = match stage {
"parse_trace" | "ast" => "red",
"tokens" => "green",
"type_check" => "magenta",
_ => "blue",
};
TraceArtifact { stage_name: stage.to_string(), debug_output: debug, text_color: color}
}
pub fn new_parse_trace(trace: Vec<String>) -> TraceArtifact {
let mut output = String::new();
for t in trace {
output.push_str(&t);
output.push_str("\n");
}
TraceArtifact { stage_name: "parse_trace".to_string(), debug_output: output, text_color: "red"}
}
}
pub trait ProgrammingLanguageInterface {
/* old */
fn evaluate_in_repl(&mut self, _: &str, _: &EvalOptions) -> LanguageOutput {
LanguageOutput { output: format!("Defunct"), artifacts: vec![], failed: false }
}
/* old */
fn new_execute(&mut self, input: &str, _options: &EvalOptions) -> FinishedComputation {
FinishedComputation { artifacts: HashMap::new(), text_output: Err(format!("NOT DONE")) }
}
fn execute(&mut self, _input: &str, _eval_options: &EvalOptions) -> FinishedComputation {
FinishedComputation { artifacts: HashMap::new(), text_output: Err(format!("REPL evaluation not implemented")) }
}
fn get_language_name(&self) -> String;
fn get_source_file_suffix(&self) -> String;
fn handle_custom_interpreter_directives(&mut self, commands: &Vec<&str>) -> Option<String> {
None
}
fn custom_interpreter_directives_help(&self) -> String {
format!(">> No custom interpreter directives specified <<")
}
}

391
schala-repl/src/lib.rs Normal file
View File

@ -0,0 +1,391 @@
#![feature(link_args)]
#![feature(slice_patterns, box_patterns, box_syntax)]
#![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate getopts;
extern crate rustyline;
extern crate itertools;
extern crate colored;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate rocket;
extern crate rocket_contrib;
extern crate includedir;
extern crate phf;
use std::path::Path;
use std::fs::File;
use std::io::{Read, Write};
use std::process::exit;
use std::default::Default;
use std::fmt::Write as FmtWrite;
use rustyline::error::ReadlineError;
use rustyline::Editor;
use self::colored::*;
mod language;
mod webapp;
pub mod llvm_wrap;
const VERSION_STRING: &'static str = "0.1.0";
include!(concat!(env!("OUT_DIR"), "/static.rs"));
pub use language::{LLVMCodeString, ProgrammingLanguageInterface, EvalOptions, ExecutionMethod, TraceArtifact, LanguageOutput, FinishedComputation, UnfinishedComputation};
pub type PLIGenerator = Box<Fn() -> Box<ProgrammingLanguageInterface> + Send + Sync>;
pub fn repl_main(generators: Vec<PLIGenerator>) {
let languages: Vec<Box<ProgrammingLanguageInterface>> = generators.iter().map(|x| x()).collect();
let option_matches = program_options().parse(std::env::args()).unwrap_or_else(|e| {
println!("{:?}", e);
exit(1);
});
if option_matches.opt_present("list-languages") {
for lang in languages {
println!("{}", lang.get_language_name());
}
exit(1);
}
if option_matches.opt_present("help") {
println!("{}", program_options().usage("Schala metainterpreter"));
exit(0);
}
if option_matches.opt_present("webapp") {
webapp::web_main(generators);
exit(0);
}
let mut options = EvalOptions::default();
if let Some(ref ltrs) = option_matches.opt_str("debug") {
options.debug.tokens = ltrs.contains("l");
options.debug.ast = ltrs.contains("a");
options.debug.parse_tree = ltrs.contains("r");
options.debug.symbol_table = ltrs.contains("s");
}
let language_names: Vec<String> = languages.iter().map(|lang| {lang.get_language_name()}).collect();
let initial_index: usize =
option_matches.opt_str("lang")
.and_then(|lang| { language_names.iter().position(|x| { x.to_lowercase() == lang.to_lowercase() }) })
.unwrap_or(0);
options.execution_method = match option_matches.opt_str("eval-style") {
Some(ref s) if s == "compile" => ExecutionMethod::Compile,
_ => ExecutionMethod::Interpret,
};
match option_matches.free[..] {
[] | [_] => {
let mut repl = Repl::new(languages, initial_index);
repl.run();
}
[_, ref filename, _..] => {
run_noninteractive(filename, languages, options);
}
};
}
fn run_noninteractive(filename: &str, languages: Vec<Box<ProgrammingLanguageInterface>>, options: EvalOptions) {
let path = Path::new(filename);
let ext = path.extension().and_then(|e| e.to_str()).unwrap_or_else(|| {
println!("Source file lacks extension");
exit(1);
});
let mut language = Box::new(languages.into_iter().find(|lang| lang.get_source_file_suffix() == ext)
.unwrap_or_else(|| {
println!("Extension .{} not recognized", ext);
exit(1);
}));
let mut source_file = File::open(path).unwrap();
let mut buffer = String::new();
source_file.read_to_string(&mut buffer).unwrap();
match options.execution_method {
ExecutionMethod::Compile => {
/*
let llvm_bytecode = language.compile(&buffer);
compilation_sequence(llvm_bytecode, filename);
*/
panic!("Not ready to go yet");
},
ExecutionMethod::Interpret => {
let output = language.execute(&buffer, &options);
output.to_noninteractive().map(|text| println!("{}", text));
}
}
}
struct Repl {
options: EvalOptions,
languages: Vec<Box<ProgrammingLanguageInterface>>,
current_language_index: usize,
interpreter_directive_sigil: char,
console: rustyline::Editor<()>,
}
impl Repl {
fn new(languages: Vec<Box<ProgrammingLanguageInterface>>, initial_index: usize) -> Repl {
let i = if initial_index < languages.len() { initial_index } else { 0 };
let console = Editor::<()>::new();
Repl {
options: Repl::get_options(),
languages: languages,
current_language_index: i,
interpreter_directive_sigil: ':',
console
}
}
fn get_options() -> EvalOptions {
File::open(".schala_repl")
.and_then(|mut file| {
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
})
.and_then(|contents| {
let options: EvalOptions = serde_json::from_str(&contents)?;
Ok(options)
}).unwrap_or(EvalOptions::default())
}
fn save_options(&self) {
let ref options = self.options;
let read = File::create(".schala_repl")
.and_then(|mut file| {
let buf = serde_json::to_string(options).unwrap();
file.write_all(buf.as_bytes())
});
if let Err(err) = read {
println!("Error saving .schala_repl file {}", err);
}
}
fn run(&mut self) {
println!("Schala MetaInterpreter version {}", VERSION_STRING);
println!("Type {}help for help with the REPL", self.interpreter_directive_sigil);
self.console.get_history().load(".schala_history").unwrap_or(());
loop {
let language_name = self.languages[self.current_language_index].get_language_name();
let prompt_str = format!("{} >> ", language_name);
match self.console.readline(&prompt_str) {
Err(ReadlineError::Eof) | Err(ReadlineError::Interrupted) => break,
Err(e) => {
println!("Terminal read error: {}", e);
},
Ok(ref input) => {
let output = match input.chars().nth(0) {
Some(ch) if ch == self.interpreter_directive_sigil => self.handle_interpreter_directive(input),
_ => {
self.console.get_history().add(input);
Some(self.input_handler(input))
}
};
if let Some(o) = output {
println!("=> {}", o);
}
}
}
}
self.console.get_history().save(".schala_history").unwrap_or(());
self.save_options();
println!("Exiting...");
}
fn input_handler(&mut self, input: &str) -> String {
let ref mut language = self.languages[self.current_language_index];
let interpreter_output = language.new_execute(input, &self.options);
interpreter_output.to_repl()
}
fn handle_interpreter_directive(&mut self, input: &str) -> Option<String> {
let mut iter = input.chars();
iter.next();
let commands: Vec<&str> = iter
.as_str()
.split_whitespace()
.collect();
let cmd: &str = match commands.get(0).clone() {
None => return None,
Some(s) => s
};
match cmd {
"exit" | "quit" => {
self.save_options();
exit(0)
},
"lang" | "language" => match commands.get(1) {
Some(&"show") => {
let mut buf = String::new();
for (i, lang) in self.languages.iter().enumerate() {
write!(buf, "{}{}\n", if i == self.current_language_index { "* "} else { "" }, lang.get_language_name()).unwrap();
}
Some(buf)
},
Some(&"go") => match commands.get(2) {
None => Some(format!("Must specify a language name")),
Some(&desired_name) => {
for (i, _) in self.languages.iter().enumerate() {
let lang_name = self.languages[i].get_language_name();
if lang_name.to_lowercase() == desired_name.to_lowercase() {
self.current_language_index = i;
return Some(format!("Switching to {}", self.languages[self.current_language_index].get_language_name()));
}
}
Some(format!("Language {} not found", desired_name))
}
},
Some(&"next") | Some(&"n") => {
self.current_language_index = (self.current_language_index + 1) % self.languages.len();
Some(format!("Switching to {}", self.languages[self.current_language_index].get_language_name()))
},
Some(&"previous") | Some(&"p") | Some(&"prev") => {
self.current_language_index = if self.current_language_index == 0 { self.languages.len() - 1 } else { self.current_language_index - 1 };
Some(format!("Switching to {}", self.languages[self.current_language_index].get_language_name()))
},
Some(e) => Some(format!("Bad `lang(uage)` argument: {}", e)),
None => Some(format!("Valid arguments for `lang(uage)` are `show`, `next`|`n`, `previous`|`prev`|`n`"))
},
"help" => {
let mut buf = String::new();
let ref lang = self.languages[self.current_language_index];
writeln!(buf, "MetaInterpreter options").unwrap();
writeln!(buf, "-----------------------").unwrap();
writeln!(buf, "exit | quit - exit the REPL").unwrap();
writeln!(buf, "lang [prev|next|go <name> |show] - toggle to previous or next language, go to a specific language by name, or show all languages").unwrap();
writeln!(buf, "Language-specific help for {}", lang.get_language_name()).unwrap();
writeln!(buf, "-----------------------").unwrap();
writeln!(buf, "{}", lang.custom_interpreter_directives_help()).unwrap();
Some(buf)
},
"set" => {
let show = match commands.get(1) {
Some(&"show") => true,
Some(&"hide") => false,
Some(e) => {
return Some(format!("Bad `set` argument: {}", e));
}
None => {
return Some(format!("`set` - valid arguments `show {{option}}`, `hide {{option}}`"));
}
};
match commands.get(2) {
Some(&"tokens") => self.options.debug.tokens = show,
Some(&"parse") => self.options.debug.parse_tree = show,
Some(&"ast") => self.options.debug.ast = show,
Some(&"symbols") => self.options.debug.symbol_table = show,
Some(&"llvm") => self.options.debug.llvm_ir = show,
Some(e) => return Some(format!("Bad `show`/`hide` argument: {}", e)),
None => return Some(format!("`show`/`hide` requires an argument")),
};
None
},
"options" => {
let ref d = self.options.debug;
let tokens = if d.tokens { "true".green() } else { "false".red() };
let parse_tree = if d.parse_tree { "true".green() } else { "false".red() };
let ast = if d.ast { "true".green() } else { "false".red() };
let symbol_table = if d.symbol_table { "true".green() } else { "false".red() };
Some(format!(r#"Debug:
tokens: {}, parse: {}, ast: {}, symbols: {}"#, tokens, parse_tree, ast, symbol_table))
},
e => self.languages[self.current_language_index]
.handle_custom_interpreter_directives(&commands)
.or(Some(format!("Unknown command: {}", e)))
}
}
}
/*
pub fn compilation_sequence(llvm_code: LLVMCodeString, sourcefile: &str) {
use std::process::Command;
let ll_filename = "out.ll";
let obj_filename = "out.o";
let q: Vec<&str> = sourcefile.split('.').collect();
let bin_filename = match &q[..] {
&[name, "maaru"] => name,
_ => panic!("Bad filename {}", sourcefile),
};
let LLVMCodeString(llvm_str) = llvm_code;
println!("Compilation process finished for {}", ll_filename);
File::create(ll_filename)
.and_then(|mut f| f.write_all(llvm_str.as_bytes()))
.expect("Error writing file");
let llc_output = Command::new("llc")
.args(&["-filetype=obj", ll_filename, "-o", obj_filename])
.output()
.expect("Failed to run llc");
if !llc_output.status.success() {
println!("{}", String::from_utf8_lossy(&llc_output.stderr));
}
let gcc_output = Command::new("gcc")
.args(&["-o", bin_filename, &obj_filename])
.output()
.expect("failed to run gcc");
if !gcc_output.status.success() {
println!("{}", String::from_utf8_lossy(&gcc_output.stdout));
println!("{}", String::from_utf8_lossy(&gcc_output.stderr));
}
for filename in [obj_filename].iter() {
Command::new("rm")
.arg(filename)
.output()
.expect(&format!("failed to run rm {}", filename));
}
}
*/
fn program_options() -> getopts::Options {
let mut options = getopts::Options::new();
options.optopt("s",
"eval-style",
"Specify whether to compile (if supported) or interpret the language. If not specified, the default is language-specific",
"[compile|interpret]"
);
options.optflag("",
"list-languages",
"Show a list of all supported languages");
options.optopt("l",
"lang",
"Start up REPL in a language",
"LANGUAGE");
options.optflag("h",
"help",
"Show help text");
options.optflag("w",
"webapp",
"Start up web interpreter");
options.optopt("d",
"debug",
"Debug a stage (l = tokenizer, a = AST, r = parse trace, s = symbol table)",
"[l|a|r|s]");
options
}

View File

@ -0,0 +1,279 @@
#![allow(non_snake_case)]
#![allow(dead_code)]
extern crate llvm_sys;
use self::llvm_sys::{LLVMIntPredicate, LLVMRealPredicate};
use self::llvm_sys::prelude::*;
use self::llvm_sys::core;
use std::ptr;
use std::ffi::{CString, CStr};
use std::os::raw::c_char;
pub fn create_context() -> LLVMContextRef {
unsafe { core::LLVMContextCreate() }
}
pub fn module_create_with_name(name: &str) -> LLVMModuleRef {
unsafe {
let n = name.as_ptr() as *const _;
core::LLVMModuleCreateWithName(n)
}
}
pub fn CreateBuilderInContext(context: LLVMContextRef) -> LLVMBuilderRef {
unsafe { core::LLVMCreateBuilderInContext(context) }
}
pub fn AppendBasicBlockInContext(context: LLVMContextRef,
function: LLVMValueRef,
name: &str)
-> LLVMBasicBlockRef {
let c_name = CString::new(name).unwrap();
unsafe { core::LLVMAppendBasicBlockInContext(context, function, c_name.as_ptr()) }
}
pub fn AddFunction(module: LLVMModuleRef, name: &str, function_type: LLVMTypeRef) -> LLVMValueRef {
let c_name = CString::new(name).unwrap();
unsafe { core::LLVMAddFunction(module, c_name.as_ptr(), function_type) }
}
pub fn FunctionType(return_type: LLVMTypeRef,
mut param_types: Vec<LLVMTypeRef>,
is_var_rag: bool)
-> LLVMTypeRef {
let len = param_types.len();
unsafe {
let pointer = param_types.as_mut_ptr();
core::LLVMFunctionType(return_type,
pointer,
len as u32,
if is_var_rag { 1 } else { 0 })
}
}
pub fn GetNamedFunction(module: LLVMModuleRef,
name: &str) -> Option<LLVMValueRef> {
let c_name = CString::new(name).unwrap();
let ret = unsafe { core::LLVMGetNamedFunction(module, c_name.as_ptr()) };
if ret.is_null() {
None
} else {
Some(ret)
}
}
pub fn VoidTypeInContext(context: LLVMContextRef) -> LLVMTypeRef {
unsafe { core::LLVMVoidTypeInContext(context) }
}
pub fn DisposeBuilder(builder: LLVMBuilderRef) {
unsafe { core::LLVMDisposeBuilder(builder) }
}
pub fn DisposeModule(module: LLVMModuleRef) {
unsafe { core::LLVMDisposeModule(module) }
}
pub fn ContextDispose(context: LLVMContextRef) {
unsafe { core::LLVMContextDispose(context) }
}
pub fn PositionBuilderAtEnd(builder: LLVMBuilderRef, basic_block: LLVMBasicBlockRef) {
unsafe { core::LLVMPositionBuilderAtEnd(builder, basic_block) }
}
pub fn BuildRet(builder: LLVMBuilderRef, val: LLVMValueRef) -> LLVMValueRef {
unsafe { core::LLVMBuildRet(builder, val) }
}
pub fn BuildRetVoid(builder: LLVMBuilderRef) -> LLVMValueRef {
unsafe { core::LLVMBuildRetVoid(builder) }
}
pub fn DumpModule(module: LLVMModuleRef) {
unsafe { core::LLVMDumpModule(module) }
}
pub fn Int64TypeInContext(context: LLVMContextRef) -> LLVMTypeRef {
unsafe { core::LLVMInt64TypeInContext(context) }
}
pub fn ConstInt(int_type: LLVMTypeRef, n: u64, sign_extend: bool) -> LLVMValueRef {
unsafe { core::LLVMConstInt(int_type, n, if sign_extend { 1 } else { 0 }) }
}
pub fn BuildAdd(builder: LLVMBuilderRef,
lhs: LLVMValueRef,
rhs: LLVMValueRef,
reg_name: &str)
-> LLVMValueRef {
let name = CString::new(reg_name).unwrap();
unsafe { core::LLVMBuildAdd(builder, lhs, rhs, name.as_ptr()) }
}
pub fn BuildSub(builder: LLVMBuilderRef,
lhs: LLVMValueRef,
rhs: LLVMValueRef,
reg_name: &str)
-> LLVMValueRef {
let name = CString::new(reg_name).unwrap();
unsafe { core::LLVMBuildSub(builder, lhs, rhs, name.as_ptr()) }
}
pub fn BuildMul(builder: LLVMBuilderRef,
lhs: LLVMValueRef,
rhs: LLVMValueRef,
reg_name: &str)
-> LLVMValueRef {
let name = CString::new(reg_name).unwrap();
unsafe { core::LLVMBuildMul(builder, lhs, rhs, name.as_ptr()) }
}
pub fn BuildUDiv(builder: LLVMBuilderRef,
lhs: LLVMValueRef,
rhs: LLVMValueRef,
reg_name: &str)
-> LLVMValueRef {
let name = CString::new(reg_name).unwrap();
unsafe { core::LLVMBuildUDiv(builder, lhs, rhs, name.as_ptr()) }
}
pub fn BuildSRem(builder: LLVMBuilderRef,
lhs: LLVMValueRef,
rhs: LLVMValueRef,
reg_name: &str)
-> LLVMValueRef {
let name = CString::new(reg_name).unwrap();
unsafe { core::LLVMBuildSRem(builder, lhs, rhs, name.as_ptr()) }
}
pub fn BuildCondBr(builder: LLVMBuilderRef,
if_expr: LLVMValueRef,
then_expr: LLVMBasicBlockRef,
else_expr: LLVMBasicBlockRef) -> LLVMValueRef {
unsafe { core::LLVMBuildCondBr(builder, if_expr, then_expr, else_expr) }
}
pub fn BuildBr(builder: LLVMBuilderRef,
dest: LLVMBasicBlockRef) -> LLVMValueRef {
unsafe { core::LLVMBuildBr(builder, dest) }
}
pub fn GetInsertBlock(builder: LLVMBuilderRef) -> LLVMBasicBlockRef {
unsafe { core::LLVMGetInsertBlock(builder) }
}
pub fn BuildPhi(builder: LLVMBuilderRef, ty: LLVMTypeRef, name: &str) -> LLVMValueRef {
let name = CString::new(name).unwrap();
unsafe { core::LLVMBuildPhi(builder, ty, name.as_ptr()) }
}
pub fn SetValueName(value: LLVMValueRef, name: &str) {
let name = CString::new(name).unwrap();
unsafe {
core::LLVMSetValueName(value, name.as_ptr())
}
}
pub fn GetValueName(value: LLVMValueRef) -> String {
unsafe {
let name_ptr: *const c_char = core::LLVMGetValueName(value);
CStr::from_ptr(name_ptr).to_string_lossy().into_owned()
}
}
pub fn GetParams(function: LLVMValueRef) -> Vec<LLVMValueRef> {
let size = CountParams(function);
unsafe {
let mut container = Vec::with_capacity(size);
container.set_len(size);
core::LLVMGetParams(function, container.as_mut_ptr());
container
}
}
pub fn CountParams(function: LLVMValueRef) -> usize {
unsafe { core::LLVMCountParams(function) as usize }
}
pub fn BuildFCmp(builder: LLVMBuilderRef,
op: LLVMRealPredicate,
lhs: LLVMValueRef,
rhs: LLVMValueRef,
name: &str) -> LLVMValueRef {
let name = CString::new(name).unwrap();
unsafe { core::LLVMBuildFCmp(builder, op, lhs, rhs, name.as_ptr()) }
}
pub fn BuildZExt(builder: LLVMBuilderRef,
val: LLVMValueRef,
dest_type: LLVMTypeRef,
name: &str) -> LLVMValueRef {
let name = CString::new(name).unwrap();
unsafe { core::LLVMBuildZExt(builder, val, dest_type, name.as_ptr()) }
}
pub fn BuildUIToFP(builder: LLVMBuilderRef,
val: LLVMValueRef,
dest_type: LLVMTypeRef,
name: &str) -> LLVMValueRef {
let name = CString::new(name).unwrap();
unsafe { core::LLVMBuildUIToFP(builder, val, dest_type, name.as_ptr()) }
}
pub fn BuildICmp(builder: LLVMBuilderRef,
op: LLVMIntPredicate,
lhs: LLVMValueRef,
rhs: LLVMValueRef,
name: &str) -> LLVMValueRef {
let name = CString::new(name).unwrap();
unsafe { core::LLVMBuildICmp(builder, op, lhs, rhs, name.as_ptr()) }
}
pub fn GetBasicBlockParent(block: LLVMBasicBlockRef) -> LLVMValueRef {
unsafe { core::LLVMGetBasicBlockParent(block) }
}
pub fn GetBasicBlocks(function: LLVMValueRef) -> Vec<LLVMBasicBlockRef> {
let size = CountBasicBlocks(function);
unsafe {
let mut container = Vec::with_capacity(size);
container.set_len(size);
core::LLVMGetBasicBlocks(function, container.as_mut_ptr());
container
}
}
pub fn CountBasicBlocks(function: LLVMValueRef) -> usize {
unsafe { core::LLVMCountBasicBlocks(function) as usize }
}
pub fn PrintModuleToString(module: LLVMModuleRef) -> String {
unsafe {
let str_ptr: *const c_char = core::LLVMPrintModuleToString(module);
CStr::from_ptr(str_ptr).to_string_lossy().into_owned()
}
}
pub fn AddIncoming(phi_node: LLVMValueRef, mut incoming_values: Vec<LLVMValueRef>,
mut incoming_blocks: Vec<LLVMBasicBlockRef>) {
let count = incoming_blocks.len() as u32;
if incoming_values.len() as u32 != count {
panic!("Bad invocation of AddIncoming");
}
unsafe {
let vals = incoming_values.as_mut_ptr();
let blocks = incoming_blocks.as_mut_ptr();
core::LLVMAddIncoming(phi_node, vals, blocks, count)
}
}
pub fn PrintModuleToFile(module: LLVMModuleRef, filename: &str) -> LLVMBool {
let out_file = CString::new(filename).unwrap();
unsafe { core::LLVMPrintModuleToFile(module, out_file.as_ptr(), ptr::null_mut()) }
}

44
schala-repl/src/webapp.rs Normal file
View File

@ -0,0 +1,44 @@
use rocket;
use rocket::State;
use rocket::response::Content;
use rocket::http::ContentType;
use rocket_contrib::Json;
use language::{ProgrammingLanguageInterface, EvalOptions};
use WEBFILES;
use ::PLIGenerator;
#[get("/")]
fn index() -> Content<String> {
let path = "static/index.html";
let html_contents = String::from_utf8(WEBFILES.get(path).unwrap().into_owned()).unwrap();
Content(ContentType::HTML, html_contents)
}
#[get("/bundle.js")]
fn js_bundle() -> Content<String> {
let path = "static/bundle.js";
let js_contents = String::from_utf8(WEBFILES.get(path).unwrap().into_owned()).unwrap();
Content(ContentType::JavaScript, js_contents)
}
#[derive(Debug, Serialize, Deserialize)]
struct Input {
source: String,
}
#[derive(Serialize, Deserialize)]
struct Output {
text: String,
}
#[post("/input", format = "application/json", data = "<input>")]
fn interpreter_input(input: Json<Input>, generators: State<Vec<PLIGenerator>>) -> Json<Output> {
let schala_gen = generators.get(0).unwrap();
let mut schala: Box<ProgrammingLanguageInterface> = schala_gen();
let code_output = schala.evaluate_in_repl(&input.source, &EvalOptions::default());
Json(Output { text: code_output.to_string() })
}
pub fn web_main(language_generators: Vec<PLIGenerator>) {
rocket::ignite().manage(language_generators).mount("/", routes![index, js_bundle, interpreter_input]).launch();
}

View File

@ -0,0 +1,11 @@
fn outer() {
fn inner(a) {
a + 10
}
inner(20) + 8.3
}
outer()

View File

@ -0,0 +1,21 @@
fn hella(a, b) {
a + b
}
fn paha(x, y, z) {
x * y * z
}
a = 1
c = if a {
10
} else {
20
}
q = 4
q = q + 2
q + 1 + c

View File

@ -0,0 +1,8 @@
if 20 {
a = 20
b = 30
c = 40
a + b + c
} else {
Null
}

View File

@ -0,0 +1,5 @@
(fn(q) { q * 2 }(25))
a = fn(x) { x + 5 }
a(2)

17
source_files/main.maaru Normal file
View File

@ -0,0 +1,17 @@
fn add(a, b) {
a + b
}
fn subtract(a, b) {
a - b
}
fn main() {
first_value = add(20, 20)
second_value = subtract(700, 650)
first_value + second_value
}
main()

View File

@ -0,0 +1,24 @@
fn hella(x) {
print("hey")
if x == 3 {
Null
} else {
hella(x + 1)
}
}
hella(0)
fn fib(x) {
if x < 3 {
1
} else {
fib(x - 1) + fib(x - 2)
}
}
fib(10)

View File

@ -0,0 +1,12 @@
fn main() {
const a = 10
const b = 20
a + b
}
//foo
print(main())

View File

@ -0,0 +1,105 @@
fn main() {
//comments are C-style
/* nested comments /* are cool */ */
}
@annotations are with @-
// variable expressions
var a: I32 = 20
const b: String = 20
there(); can(); be(); multiple(); statements(); per_line();
//string interpolation
const yolo = "I have ${a + b} people in my house"
// let expressions ??? not sure if I want this
let a = 10, b = 20, c = 30 in a + b + c
//list literal
const q = [1,2,3,4]
//lambda literal
q.map({|item| item * 100 })
fn yolo(a: MyType, b: YourType): ReturnType<Param1, Param2> {
if a == 20 {
return "early"
}
var sex = 20
sex
}
for {
//infinite loop
}
//iteration over a variable
for i <- [1..1000] {
} //return type is return type of block
//while loop
for a != 3 || fuckTard() {
break
} //return type is return type of block
//monadic decomposition
for {
a <- maybeInt();
s <- foo()
} return {
a + s
} //return type is Monad<return type of block>
// let statements too!!
for (a = 20
b = fuck) {
a + b
}
// pattern-matching
match <expr> {
Some(a) => {
},
None => {
},
}
//syntax is, I guess, for <expr> <brace-block>, where <expr> is a bool, or a <arrow-expr>
// type level alises
typealias <name> = <other type> #maybe thsi should be 'alias'?
/*
what if type A = B meant that you could had to create A's with A(B), but when you used A's the interface was exactly like B's?
maybe introduce a 'newtype' keyword for this
*/
//declaring types of all stripes
type MyData = { a: i32, b: String }
type MyType = MyType
type Option<a> = None | Some(a)
type Signal = Absence | SimplePresence(i32) | ComplexPresence {a: i32, b: MyCustomData}
//traits
trait Bashable { }
trait Luggable {
fn lug(self, a: Option<Self>)
}
}
// lambdas
// ruby-style not rust-style
const a: X -> Y -> Z = {|x,y| }

12
source_files/test.maaru Normal file
View File

@ -0,0 +1,12 @@
fn a(x) {
x + 20
}
fn x(x) {
x + a(9384)
}
a(0)
x(1)

3
source_files/test.rukka Normal file
View File

@ -0,0 +1,3 @@
(display (+ 1 2))
(display "Hello")

View File

@ -0,0 +1,8 @@
fn めんどくさい(a) {
a + 20
}
print(めんどくさい(394))

7
source_files/while.maaru Normal file
View File

@ -0,0 +1,7 @@
a = 0
while a < 100000
print("hello", a)
a = a + 1
end

View File

@ -1,3 +1,20 @@
extern crate schala_repl;
extern crate maaru_lang;
extern crate rukka_lang;
extern crate robo_lang;
extern crate schala_lang;
use schala_repl::{PLIGenerator, repl_main};
extern { }
fn main() {
println!("Hello, world!");
let generators: Vec<PLIGenerator> = vec![
Box::new(|| { Box::new(schala_lang::Schala::new())}),
Box::new(|| { Box::new(maaru_lang::Maaru::new())}),
Box::new(|| { Box::new(robo_lang::Robo::new())}),
Box::new(|| { Box::new(rukka_lang::Rukka::new())}),
];
repl_main(generators);
}

17
static/index.html Normal file
View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<title>Schala Metainterpreter Web Evaluator</title>
<style>
.CodeArea {
display: flex;
flex-direction: row;
}
</style>
</head>
<body>
<div id="main">
</div>
<script src="bundle.js"></script>
</body>
</html>

64
static/main.jsx Normal file
View File

@ -0,0 +1,64 @@
const React = require("react");
const ReactDOM = require("react-dom");
const superagent = require("superagent");
const serverAddress = "http://localhost:8000";
class CodeArea extends React.Component {
constructor(props) {
super(props);
this.state = {value: "", lastOutput: null};
this.handleChange = this.handleChange.bind(this);
this.submit = this.submit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
submit(event) {
console.log("Event", this.state.value);
const source = this.state.value;
superagent.post(`${serverAddress}/input`)
.send({ source })
.set("accept", "json")
.end((error, response) => {
if (response) {
console.log("Resp", response);
this.setState({lastOutput: response.body.text})
} else {
console.error("Error: ", error);
}
});
}
renderOutput() {
if (!this.state.lastOutput) {
return null;
}
return <textarea readOnly value={ this.state.lastOutput } />;
}
render() {
return (<div className="CodeArea">
<div className="input">
<textarea value={ this.state.value } onChange={this.handleChange}>
</textarea>
<button onClick={ this.submit }>Run!</button>
</div>
<div className="output">
{ this.renderOutput() }
</div>
</div>);
}
}
const main = (<div>
<h1>Schala web input</h1>
<p>Write your source code here</p>
<CodeArea/>
</div>);
const rootDom = document.getElementById("main");
ReactDOM.render(main, rootDom);

27
static/package.json Normal file
View File

@ -0,0 +1,27 @@
{
"name": "static",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"babel": "^6.23.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babelify": "^7.3.0",
"browserify": "^14.4.0",
"react": "^15.6.1",
"react-dom": "^15.6.1",
"superagent": "^3.6.3",
"uglify-js": "^3.1.1"
},
"babel": {
"presets": [
"babel-preset-react",
"babel-preset-es2015"
]
},
"scripts": {
"build": "browserify main.jsx -t babelify -o bundle.js",
"build-minify": "browserify main.jsx -t babelify | uglifyjs > bundle.js"
}
}

1728
static/yarn.lock Normal file

File diff suppressed because it is too large Load Diff