557 Commits

Author SHA1 Message Date
greg
7c27cace9f Add note 2018-09-17 00:03:29 -07:00
greg
23c54ae186 proc macro feature no longer needed 2018-09-10 21:28:34 -07:00
greg
015840ac38 Get program compileable 2018-09-10 21:28:14 -07:00
greg
b8487aa0d4 Add note from Sergei W. on subtyping 2018-09-10 21:01:56 -07:00
greg
bac5761534 Add a note 2018-09-06 02:12:37 -07:00
greg
926631ba8f Pattern matching experimental code
WIP
2018-08-29 03:00:54 -07:00
greg
1abbe2e448 Add guard to Alternative
The semantics are:
    -if tag is Some(_), assume the condition is a constructor,
    and compare tags
    - if guard is Some(_), evaluate true/false *after* having
    applied any bound variables

With this, I can technically get rid of bare conditionals now, since
they are the same as an Alternative with a None tag
2018-08-27 12:45:08 -07:00
greg
065bdd6bda Starting custom operators
Can now parse custom operators. Maybe I want to make it so that you
have to put explicit backticks if you define a custom operator,
currently you can just do: fn +() { .. }
2018-08-24 16:49:59 -07:00
greg
e125e8b440 Add spaceship operator for getting an ord 2018-08-24 16:29:28 -07:00
greg
8565c7dfb3 Some work on reduced ast pattern 2018-08-24 16:04:18 -07:00
greg
f885d5dfb6 Remove type alias 2018-08-22 23:22:08 -07:00
greg
b85725125c Start using HalfExp 2018-08-22 16:41:31 -07:00
greg
2d961d6402 Fix other pattern parsing bugs 2018-08-21 20:02:10 -07:00
greg
fa7b6ce96b Handle negatives in patterns correctly 2018-08-21 19:57:45 -07:00
greg
5c9180efc2 Some updates to Schala source files 2018-08-20 19:03:55 -07:00
greg
1d5e5aa735 Some type renaming in builtins
Builtins will remain entirely separate from the actual type
representation, whatever that ends up being
2018-08-19 22:00:20 -07:00
greg
2c298c7247 Add warning for undefined operator
In practice this will probably always not typecheck, but it's a valid
parse
2018-08-19 21:40:30 -07:00
greg
f00fee0e37 Rename StateStack -> ScopeStack 2018-08-19 21:31:45 -07:00
greg
0d13b5e3bc Preliminary support for binops in if-discriminators
The BNF grammar is a bit more liberal than any successfully-compiled
schala program should be, in that it allows things like `if x < is
pattern`. It's okay if that parses successfully and then is an error at
typechecking.
2018-08-19 21:25:07 -07:00
greg
98f597f00a Implement comparison operators correctly 2018-08-19 21:11:43 -07:00
greg
fb71881409 Refactor binop parsing 2018-08-19 20:33:50 -07:00
greg
d1c3b4a81b Starting on halfexprs / binops 2018-08-19 18:44:54 -07:00
greg
f9181b5786 use expr_or_block where appropriate 2018-08-19 15:58:31 -07:00
greg
0e914cf057 Error message for parsing guards 2018-08-19 15:12:34 -07:00
greg
04ea8c5ebc More unused code removal 2018-08-19 15:06:01 -07:00
greg
492ef4ae19 Clear up some unused code to reduce compile noise
And add some notes to the README
2018-08-19 15:03:41 -07:00
greg
75a7a4499d Added some more cases to the match handling 2018-08-19 10:53:43 -07:00
greg
99e6668c9a Add rusty-tags to .gitignore 2018-08-18 23:28:06 -07:00
greg
1d38a07cf8 Add timing debugging print 2018-08-16 01:47:21 -07:00
greg
0fa844bcf9 Print timing in debug info 2018-08-16 01:43:42 -07:00
greg
97bee58fbe More work with guards 2018-08-15 22:34:04 -07:00
greg
34c2b43371 More work on if matching 2018-08-15 18:32:44 -07:00
greg
88b617de52 More atlernatives work 2018-08-15 11:44:55 -07:00
greg
482674b19a Start on expr_or_block
WIP doesn't work yet
2018-08-15 09:34:00 -07:00
greg
a72b387ceb Remove some more dead code warnings 2018-08-14 23:19:27 -07:00
greg
864e932e9f Getting rid of more unused items 2018-08-14 23:09:11 -07:00
greg
d7e73be44c Getting rid of some unused warnings 2018-08-14 23:07:00 -07:00
greg
6a548c9086 Keep track of durations of each pipeline stage 2018-08-14 22:56:22 -07:00
greg
0c0690e86e Provide error message here 2018-08-14 21:53:57 -07:00
greg
6d18f80185 Use Result in test 2018-08-14 21:46:48 -07:00
greg
6825de3916 new_frame -> new_scope 2018-08-14 21:45:45 -07:00
greg
1b78fbff82 Tests for basic pattern matching 2018-08-14 21:39:33 -07:00
greg
897c1181a9 Basic pattern matching working 2018-08-14 21:17:43 -07:00
greg
6833bc4f00 Start on CaseMatch eval 2018-08-14 12:43:06 -07:00
greg
f2ded78776 ReducedAST: Match -> CaseMatch
makes it easier to grep for
2018-08-14 12:37:18 -07:00
greg
9debdd8d66 Primitive tuple 2018-08-14 02:03:05 -07:00
greg
8067c862f3 Switch out types for evaluator 2018-08-14 00:11:13 -07:00
greg
f9c2fc3f9d Make code more concise 2018-08-07 17:09:53 -07:00
greg
5ead1e5d44 NewConstructor -> Constructor 2018-08-05 19:14:02 -07:00
greg
348a6f7c76 More work on pattern-matching
I think I need to entirely change the types in the evaluator.
ReducedAST should only care about NewConstructor (which I gotta rename),
and the evaluator is the only place that an implementation of a
primitive constructed type should live (see Peyton-Jones implementing a
functional langauge p. 70)
2018-08-05 19:11:42 -07:00
greg
5f336ec1a9 Add lookup_by_name to symbol table 2018-08-05 18:19:48 -07:00
greg
da59fae0d3 More work on pattern-matching 2018-08-05 18:01:42 -07:00
greg
5b5689accf Changing representation of primitive objects 2018-08-05 17:15:58 -07:00
greg
32acf89814 New Constructor 2018-08-05 16:04:52 -07:00
greg
c637a922a9 Start implementing constructors/matches
as per Implementing Functional Programming Languages by Peyton-Jones
2018-08-05 14:23:08 -07:00
greg
42d0aba21c Add index of variants to symbol table
Also new prelude type, just for testing
2018-08-05 13:59:13 -07:00
greg
7548bdbb78 Add note 2018-07-31 01:59:49 -07:00
greg
bc6d4d19b5 reduced ast match 2018-07-26 00:52:46 -07:00
greg
a2b1b0f953 Pattern-matching in reduced AST 2018-07-26 00:52:46 -07:00
greg
75bf4b5697 Fill out variants to be reduced 2018-07-26 00:52:46 -07:00
greg
35f5a9623a Check out cranelift 2018-07-26 00:52:46 -07:00
greg
98e812968b Fix parsing additional options 2018-07-26 00:52:46 -07:00
greg
250c486143 Fix derive code 2018-07-26 00:52:46 -07:00
greg
38eb065511 Broken proc macro custom derive code 2018-07-26 00:52:46 -07:00
greg
9e24c3b336 update rocket version 2018-07-26 00:52:46 -07:00
greg
1d2f1624a1 Some codegen work to make pass options work 2018-07-26 00:52:46 -07:00
greg
4ca57e4aea Change name of debug options struct 2018-07-26 00:52:46 -07:00
greg
82502ad0ad Move some parsing code around 2018-07-26 00:52:46 -07:00
greg
7d68b2a05a Get rid of code related to old match stuff 2018-07-26 00:52:46 -07:00
greg
c2db212c78 Some more guard arm stuff + dealing with split binexp
... in if-blocks. Need to do some re-architecture I think
2018-07-26 00:52:46 -07:00
greg
837a180b09 Starting work on guard arms 2018-07-26 00:52:46 -07:00
greg
5ecd298e6a Record Pattern 2018-07-26 00:52:46 -07:00
greg
8aa33d0872 Starting on guards 2018-07-26 00:52:46 -07:00
greg
21a8868bcf Fixed test 2018-07-26 00:52:46 -07:00
greg
5a91957fa1 Some incomplete parse work 2018-07-26 00:52:46 -07:00
greg
176d43e56f Remove old comment 2018-07-26 00:52:46 -07:00
greg
90ecde89c9 Mutable types 2018-07-26 00:52:46 -07:00
greg
65c2cd521b Mutable types
This bit of syntax is meant for extendable enum types
2018-07-26 00:52:46 -07:00
greg
f7dbbddad1 Let and let mut syntax 2018-07-26 00:52:46 -07:00
greg
43ff08b04c Add some debugging info around parse error 2018-07-26 00:52:46 -07:00
greg
00692aa89e Support for underscores 2018-07-26 00:52:46 -07:00
greg
0ec29f6dd0 Fix repl 2018-07-26 00:52:46 -07:00
greg
5e48eb2dee Broken - some pass abstraction work 2018-07-26 00:52:46 -07:00
greg
3597ad4eef Compact parsing 2018-07-26 00:52:46 -07:00
greg
072eab1a80 Thread debug opts around where they need to be 2018-07-26 00:52:46 -07:00
greg
1761d11d36 Infrastructure for adding more debug options 2018-07-26 00:52:46 -07:00
greg
d075f613f9 Hook help messages into command data structure 2018-07-26 00:52:46 -07:00
greg
ee55729d5f Halfway through implementing help text on CommandTree 2018-07-26 00:52:46 -07:00
greg
17a4028185 Generate commandtree on repl 2018-07-26 00:52:46 -07:00
greg
947f4f2ea6 Command heirarchy for tab completion 2018-07-26 00:52:46 -07:00
greg
41c9dfae06 Some more tab completion work 2018-07-26 00:52:46 -07:00
greg
fe1a508e25 Switch back to line feed for better tab completion 2018-07-26 00:52:46 -07:00
greg
55a8cabd7c Some basic pattern stuff 2018-07-26 00:52:46 -07:00
greg
46b6aeb4db Add note to check out Gluon 2018-07-26 00:52:46 -07:00
greg
3c022fc4ef Clarified BNF 2018-07-26 00:52:46 -07:00
greg
0a02c21e70 Some more Patterns work
-at first brush, a pattern is like a single Variant with a list of free
vars
2018-07-26 00:52:46 -07:00
greg
927f427a86 Starting work on patterns 2018-07-26 00:52:46 -07:00
greg
005aba7a10 Test alt form 2018-07-26 00:52:46 -07:00
greg
7882e92ab5 Fix old style if 2018-07-26 00:52:46 -07:00
greg
f582ab4eaa Test for new style parsing 2018-07-26 00:52:46 -07:00
greg
f2dce38647 Broken, but compiling, move to new if paradigm 2018-07-26 00:52:46 -07:00
greg
ba4cd9da39 Kill match keyword + data structures
And add new unified keywords
2018-07-26 00:52:46 -07:00
greg
ba319a7bc3 Finally figured out unified condition syntax 2018-07-26 00:52:46 -07:00
greg
2d052d34f7 More thinking 2018-07-26 00:52:46 -07:00
greg
654eeef428 Clarified grammar BNF some 2018-07-26 00:52:46 -07:00
greg
a96fbc9592 Fix match expression parsing 2018-07-26 00:52:46 -07:00
greg
196954326e Print bare data constructor 2018-07-26 00:52:46 -07:00
greg
3f2fff276c Constructor eval 2018-07-26 00:52:46 -07:00
greg
e6679ff523 Playing around with conditional syntax 2018-07-26 00:52:46 -07:00
greg
ebcea685f3 Fix looking up functions 2018-07-26 00:52:46 -07:00
greg
3b9084810e Add constructor reduced ast node; fix test 2018-07-26 00:52:46 -07:00
greg
7809cda240 Pass symbol_table to ast reduce
To distinguish between values and data constructors
2018-07-26 00:52:46 -07:00
greg
f1679e83b7 Start trying to fix tests 2018-07-26 00:52:46 -07:00
greg
f98d8e2bb0 Move AST into its own module 2018-07-26 00:52:46 -07:00
greg
d0a0cc8209 Rename ast_reducing -> reduced_ast 2018-07-26 00:52:46 -07:00
greg
5aa0e10e7a Some ADT work 2018-07-26 00:52:46 -07:00
greg
27729cefdf Some improvements to the thing 2018-07-26 00:52:46 -07:00
greg
df76e7c120 Pretty-print type table 2018-07-26 00:52:46 -07:00
greg
889610f0b0 Pretty-print Symbol Table 2018-07-26 00:52:46 -07:00
greg
3beabf4678 Start eval-ing data constructors 2018-07-26 00:52:46 -07:00
greg
25790f8643 Added super-janky prelude capability 2018-07-26 00:52:46 -07:00
greg
ff5446af3f Add symbols from symbol table into global type context 2018-07-26 00:52:46 -07:00
greg
5d84153c9e Want bin expressions typed soon 2018-07-26 00:52:46 -07:00
greg
0b0f6b6b50 Symbol table handles functions better 2018-07-26 00:52:46 -07:00
greg
856a360aba Types need handle to symbol table 2018-07-26 00:52:46 -07:00
greg
81ca9ee20f Add some data structures back 2018-07-26 00:52:46 -07:00
greg
4caf8096b3 Add Scheme, TypeEnv, Substitution data structs 2018-07-26 00:52:46 -07:00
greg
c65907388d Some ADT work 2018-07-26 00:52:46 -07:00
greg
33c22c8bbc Typecheck values 2018-07-26 00:52:46 -07:00
greg
4a27af2136 Add a super-basic test 2018-07-26 00:52:46 -07:00
greg
07af54b78a More work 2018-07-26 00:52:46 -07:00
greg
c4666b82ec Basics 2018-07-26 00:52:46 -07:00
greg
274dd1ccb0 Basic stuff 2018-07-26 00:52:46 -07:00
greg
70ec79c4b3 Lol starting over from scratch again
H-M is hard :/
2018-07-26 00:52:46 -07:00
greg
f88d2331e3 printf debugs for problems with function typing 2018-07-26 00:52:46 -07:00
greg
c8f961abbf Functions 2018-07-26 00:52:46 -07:00
greg
d040d76bfa Start handling function case 2018-07-26 00:52:46 -07:00
greg
887ba46b0b Fix this thing 2018-07-26 00:52:46 -07:00
greg
a80db9e4c2 Debug types
WIP
2018-07-26 00:52:46 -07:00
greg
c986233a95 Adding bindings seems to work?
I'm playing real fast and loose though
2018-07-26 00:52:46 -07:00
greg
bb29df4a73 Variable binding insertion infrastructure 2018-07-26 00:52:46 -07:00
greg
4db3595d7c More work on variables 2018-07-26 00:52:46 -07:00
greg
217ee73fc9 Literals 2018-07-26 00:52:46 -07:00
greg
93309c025e Some work 2018-07-26 00:52:46 -07:00
greg
b67512a9e1 Add Infer struct 2018-07-26 00:52:46 -07:00
greg
8e6f605fab Type alias "TypeName" 2018-07-26 00:52:46 -07:00
greg
ba4185b0fb Back to including typechecking code in pipeline 2018-07-26 00:52:46 -07:00
greg
7a2a4df297 Clearing out most of the cruft from typechecking 2018-07-26 00:52:46 -07:00
greg
642e9da8ee Move everything symbol-table-related into a separate module 2018-07-26 00:52:46 -07:00
greg
cea7427847 put TypeEnvironment on TypeContext 2018-07-26 00:52:46 -07:00
greg
3156c31dfc Variable lookup 2018-07-26 00:52:46 -07:00
greg
2e457cd5e8 First real inferring 2018-07-26 00:52:46 -07:00
greg
843d895f2b infer infra 2018-07-26 00:52:46 -07:00
greg
734c53ce0d Starting to deal with actual expr inferring 2018-07-26 00:52:46 -07:00
greg
3a3b8dd440 TypeEnvironment lives in Infer 2018-07-26 00:52:46 -07:00
greg
c96a56a7ac fresh 2018-07-26 00:52:46 -07:00
greg
4017857a3a Unification 2018-07-26 00:52:46 -07:00
greg
131c83b64d Consult these haskell programs from https://github.com/quchen/articles/tree/master/hindley-milner 2018-07-26 00:52:46 -07:00
greg
9e0f8b8a14 InferError 2018-07-26 00:52:46 -07:00
greg
7121624f77 Type Env 2018-07-26 00:52:46 -07:00
greg
48e795decc apply_substitution for PolyTypes
If I made an error it's likely here...
2018-07-26 00:52:46 -07:00
greg
a26da934f4 Substitution monotypes 2018-07-26 00:52:46 -07:00
greg
1de1cd9cfd For H-M, add types and some impls 2018-07-26 00:52:46 -07:00
greg
6f639b9030 Type type structure 2018-07-26 00:52:46 -07:00
greg
8f0104ebc7 Deletion 2018-07-26 00:52:46 -07:00
greg
36cd7e080d Even more deletions 2018-07-26 00:52:46 -07:00
greg
f48a25779c Lol just get rid of all the old code, start from scratch again 2018-07-26 00:52:46 -07:00
greg
808a1bfc98 Still more deletions 2018-07-26 00:52:46 -07:00
greg
c7e46c1cfa KIll any commented code 2018-07-26 00:52:46 -07:00
greg
98cfcfc18d Eval shouldn't be aware of types 2018-07-26 00:52:46 -07:00
greg
b4c7ea3d02 Show bindings too in debug 2018-07-26 00:52:46 -07:00
greg
e7c89ed840 Some more refactoring 2018-07-26 00:52:46 -07:00
greg
b0e38f7f5b Refactor 2018-07-26 00:52:46 -07:00
greg
276662d98a Some code rearrangements 2018-07-26 00:52:46 -07:00
greg
e8e9265b26 Use less verbose match syntax 2018-07-26 00:52:46 -07:00
greg
cb316a973e Getting back to hindley-milner
First, clear out some of this cruft in the compiler warnings
2018-07-26 00:52:46 -07:00
greg
e64861b602 Some eval tests 2018-07-26 00:52:46 -07:00
greg
1673fd1cf9 Fix test 2018-07-26 00:52:46 -07:00
greg
c00effcbdd Add _ 2018-07-26 00:52:46 -07:00
greg
8378170fbd Kill comments 2018-07-26 00:52:46 -07:00
greg
7ab385d398 Bring custom ADTs to the repl 2018-07-26 00:52:46 -07:00
greg
9fb148bb02 Make compile again 2018-07-26 00:52:46 -07:00
greg
97df2fa344 I dunno 2018-07-26 00:52:46 -07:00
greg
a08134a747 Delete old code in eval 2018-07-26 00:52:46 -07:00
greg
6707b2bb9c debug outputs in order 2018-07-26 00:52:46 -07:00
greg
3ac50f974d Pass around reference to type context in evaluator 2018-07-26 00:52:46 -07:00
greg
160ce95e5f Fully comment example source file 2018-07-26 00:52:46 -07:00
greg
afc4281e7f Evaluate function arguments in context before applying them 2018-07-26 00:52:46 -07:00
greg
8d6fea942f Handle function definition before use
And some other ReducedAST - Evaluation niceties
2018-07-26 00:52:46 -07:00
greg
6d93c758a2 Add function to symbol table 2018-07-26 00:52:46 -07:00
greg
aff421cd99 Working with symbol table
Note that symbol table is a different object now than the previous
binding table that was used for type-checking. That binding table is not
currently debugged and should be debugged in a separate debug output with
typechecking proper.
2018-07-26 00:52:46 -07:00
greg
493d76da0b Add symbol table data structure to typechecking 2018-07-26 00:52:46 -07:00
greg
eb681fbff9 Make parse error message nicer 2018-07-26 00:52:46 -07:00
greg
f3e3843528 Decomplexify delimited! 2018-07-26 00:52:46 -07:00
greg
6b90e19eb1 Simplify expect! macro
Ends up printing a debug print, but whatever, will fix later
2018-07-26 00:52:46 -07:00
greg
210ae47c8b Get rid of lambda 2018-07-26 00:52:46 -07:00
greg
70794d8ff1 For expression parsing 2018-07-26 00:52:46 -07:00
greg
532c8c45b4 Parse while expressions
Decided to add while expressions to the language to make for-parsing
easier. Plus some other random notes
2018-07-26 00:52:46 -07:00
greg
24b532df06 This doesn't need to be a closure 2018-07-26 00:52:46 -07:00
greg
ac576be604 Trim newline in getline()
Ineffiicent but whatever
2018-07-26 00:52:46 -07:00
greg
6bf106a1a3 Equality 2018-07-26 00:52:46 -07:00
greg
161e47fe91 Add schala source file test.schala 2018-07-26 00:52:46 -07:00
greg
1a58f3b7af Add fizzbuzz source
Next goal will be, to implement enough to make this work
2018-07-26 00:52:46 -07:00
greg
44e585fca2 Conditionals 2018-07-26 00:52:46 -07:00
greg
3f836eb74f Kill some warnings 2018-07-26 00:52:46 -07:00
greg
abf25d648d Change repl behavior of strings 2018-07-26 00:52:46 -07:00
greg
1f6e6d9b31 Tuples 2018-07-26 00:52:46 -07:00
greg
e2703121d8 Kill unneeded import 2018-07-26 00:52:46 -07:00
greg
e5b6b41422 Error msg 2018-07-26 00:52:46 -07:00
greg
6c5e3dea5d Assignment 2018-07-26 00:52:46 -07:00
greg
bd8bf1945c Super simple janky input 2018-07-26 00:52:46 -07:00
greg
9e393d2753 Kill old type structure 2018-07-26 00:52:46 -07:00
greg
822420a9d5 Add an eval test 2018-07-26 00:52:46 -07:00
greg
6f8dc9bedd rename IntLiteral -> NatLiteral 2018-07-26 00:52:46 -07:00
greg
3b134d7fb6 very simple code 2018-07-26 00:52:46 -07:00
greg
e0cec8b8a6 print, println as builtins 2018-07-26 00:52:46 -07:00
greg
1a84f62818 Kill some old code, make very_simple example print 2018-07-26 00:52:46 -07:00
greg
b1966d7199 Function calling works kind of 2018-07-26 00:52:46 -07:00
greg
fdbb21990d Retrieve function from memory when called 2018-07-26 00:52:46 -07:00
greg
1011ff08f3 Use new rust 1.26 less verbose syntax 2018-07-26 00:52:46 -07:00
greg
6d8d2aecbd Functions 2018-07-26 00:52:46 -07:00
greg
848306ad1a Reduce defined function 2018-07-26 00:52:46 -07:00
greg
e6f0710e41 Debug ast rewrite 2018-07-26 00:52:46 -07:00
greg
d7e3f695b7 Very simple source file 2018-07-26 00:52:46 -07:00
greg
78ba4e1ed3 Variable lookup 2018-07-26 00:52:46 -07:00
greg
481afb0f87 Fix debugging and debug eval 2018-07-26 00:52:46 -07:00
greg
01986e7474 starting bindings 2018-07-26 00:52:46 -07:00
greg
9cf5260d4b Use impl Trait to simplify type signatures 2018-07-26 00:52:46 -07:00
greg
b50d87b85b Idea 2018-07-26 00:52:46 -07:00
greg
18c8176134 Get rid of unneeded imports 2018-07-26 00:52:46 -07:00
greg
2cb7d35008 Use EvalResult type 2018-07-26 00:52:46 -07:00
greg
bd1eed884f State type manipulations 2018-07-26 00:52:46 -07:00
greg
67917612e6 Swap over eval code to new paradigm
While keeping the old code commented for reference
2018-07-26 00:52:46 -07:00
greg
b4a16cdc55 Prefix ops 2018-07-26 00:52:46 -07:00
greg
4d5ab95946 Fix bug with / parsing 2018-07-26 00:52:46 -07:00
greg
ce71254b69 Implement a lot more ops 2018-07-26 00:52:46 -07:00
greg
065e58f87e Successfully interpreting addition 2018-07-26 00:52:46 -07:00
greg
29cabb119f Save interpreter directives in history 2018-07-26 00:52:46 -07:00
greg
6768cebc48 Literals 2018-07-26 00:52:46 -07:00
greg
ec5580d20b prefix op reduction 2018-07-26 00:52:46 -07:00
greg
9de66a9af3 Unimplemented sigil 2018-07-26 00:52:46 -07:00
greg
633b4fe7a4 Nats, some binop reduction 2018-07-26 00:52:46 -07:00
greg
87c3b8e234 Some work 2018-07-26 00:52:46 -07:00
greg
16a463b1a0 Method-style 2018-07-26 00:52:46 -07:00
greg
c3be644133 Optional scope name 2018-07-26 00:52:46 -07:00
greg
e7615fda8b Add new_frame method 2018-07-26 00:52:46 -07:00
greg
111657b567 Add generic stack data structure
I'll want to use this soon enough
2018-07-26 00:52:46 -07:00
greg
c5e8d3e080 Random notes re: symbol table
I'm proably gonna want to redo the symbol table stuff after reading Language
Implementation Patterns, esp. to accomodate scopes
2018-07-26 00:52:46 -07:00
greg
4f49c183b0 Float + reduce binop/prefixop 2018-07-26 00:52:46 -07:00
greg
81368179bb More boilerplate 2018-07-26 00:52:46 -07:00
greg
30128d7d34 Easy work 2018-07-26 00:52:46 -07:00
greg
6c718e5d4f Start AST-reducing 2018-07-26 00:52:46 -07:00
greg
774ddd665b Infrastructure to evaluate reduced AST 2018-07-26 00:52:46 -07:00
greg
0bb0ecea76 Add new ast reducing pass 2018-07-26 00:52:46 -07:00
greg
59a7c11031 Hook --debug flag to new debug framework 2018-07-26 00:52:46 -07:00
greg
b54a9774ed Rename schala source files to be clearer 2018-07-26 00:52:46 -07:00
greg
a9c0341d38 Half-assed implemention of tab completion
Bah this is boring. Maybe I want to switch back to linefeed?
2018-07-26 00:52:46 -07:00
greg
2d260c14d7 Add Unified Construction Syntax article 2018-07-26 00:52:46 -07:00
greg
7686707602 Type alias Vec<String> -> Block 2018-07-26 00:52:46 -07:00
greg
670833185b Start adding tab completion API 2018-07-26 00:52:46 -07:00
greg
012c50b7c3 Kill commented lines 2018-07-26 00:52:46 -07:00
greg
f1a64adfd9 Kill a few lines of code 2018-07-26 00:52:46 -07:00
greg
e46eeb91f3 Highlight enabled debug passes 2018-07-26 00:52:46 -07:00
greg
d524389f1d Kill old DebugOptions struct 2018-07-26 00:52:46 -07:00
greg
890e6bd4c5 Minor wording changes 2018-07-26 00:52:46 -07:00
greg
8826d5b0d4 For now, don't error out with typechecking 2018-07-26 00:52:46 -07:00
greg
8ad5dd9056 Rename passes 2018-07-26 00:52:46 -07:00
greg
fb168da8bd Kill comments 2018-07-26 00:52:46 -07:00
greg
78fdea180e Rename stages -> passes 2018-07-26 00:52:46 -07:00
greg
00e68d09c7 Kill comment 2018-07-26 00:52:46 -07:00
greg
73c3eeb69d stage -> pass 2018-07-26 00:52:46 -07:00
greg
86e88ee1bf Greatly fleshed out custom derive 2018-07-26 00:52:46 -07:00
greg
d1a2473bb2 More derive work 2018-07-26 00:52:46 -07:00
greg
57ccdd5ead Extract out attr parsing code into a separate function 2018-07-26 00:52:46 -07:00
greg
c0746028f4 Automate language name 2018-07-26 00:52:46 -07:00
greg
c6f038a307 Successfully parse language name 2018-07-26 00:52:46 -07:00
greg
e498e19ffc Use extra attribute 2018-07-26 00:52:46 -07:00
greg
51cdedb9cc Actually autogenerate the trait 2018-07-26 00:52:46 -07:00
greg
50236ac942 Kill unused mut 2018-07-26 00:52:46 -07:00
greg
1e4554258f KIll old code 2018-07-26 00:52:46 -07:00
greg
9ade0dd1e2 Kill unnecessary mutable 2018-07-26 00:52:46 -07:00
greg
7ba8c9dab9 Tokenize errors 2018-07-26 00:52:46 -07:00
greg
774ab5f72e Token errors WIP 2018-07-26 00:52:46 -07:00
greg
50499c8a33 Remove some compiler warnings 2018-07-26 00:52:46 -07:00
greg
a10df92ab8 Debug work 2018-07-26 00:52:46 -07:00
greg
fe64cbcd3a Refactor 2018-07-26 00:52:46 -07:00
greg
061d54702f Implement debug stages as a HashSet of strings
Change to custom enum type later
2018-07-26 00:52:46 -07:00
greg
27885500fd Show debug stages 2018-07-26 00:52:46 -07:00
greg
aaf98db2b7 Starting to move debug options around
+ add method to ProgrammingLanguageInterface that is a list of stages
2018-07-26 00:52:46 -07:00
greg
fff587cd6a Change "set" to "debug" 2018-07-26 00:52:46 -07:00
greg
83fe71f721 Kill old trait infrastructure 2018-07-26 00:52:46 -07:00
greg
491face68b More autoderive things 2018-07-26 00:52:46 -07:00
greg
258e813a39 Starting to write custom derive for ProgrammingLanguageInterface 2018-07-26 00:52:46 -07:00
greg
5d69b530c5 Remove comments 2018-07-26 00:52:46 -07:00
greg
8a5b8619fa Kill old execute method 2018-07-26 00:52:46 -07:00
greg
832d0d4ee3 Add more debug jank entries 2018-07-26 00:52:46 -07:00
greg
57a18a0768 Make (some) stages configurable
This is janky and needs to be more general
2018-07-26 00:52:46 -07:00
greg
2c5ebd636f Pass EvalOptions to macro 2018-07-26 00:52:46 -07:00
greg
06638dc030 Minor syntax changes 2018-07-26 00:52:46 -07:00
greg
3a181dd0ac Add passing debug into via &mut pointer 2018-07-26 00:52:46 -07:00
greg
1d1a5fb6fc Pass mutable handle to unfinishedcomputation 2018-07-26 00:52:46 -07:00
greg
fb4de6f2d6 Making use of UnfinishedComputation 2018-07-26 00:52:46 -07:00
greg
18c86c26f0 Passing comp around 2018-07-26 00:52:46 -07:00
greg
ac44df8d1e Semicolon 2018-07-26 00:52:46 -07:00
greg
12c7cebb38 Clarify comment 2018-07-26 00:52:46 -07:00
greg
f22f089b9b finish method on UnfinishedComputation 2018-07-26 00:52:46 -07:00
greg
3d960d5697 Implement most of pipeline 2018-07-26 00:52:46 -07:00
greg
1f4228b887 Successfully passing state handle to pass functions 2018-07-26 00:52:46 -07:00
greg
5abaadc0ca Add self 2018-07-26 00:52:46 -07:00
greg
fd89de77cc Making pipeline macro nicer 2018-07-26 00:52:46 -07:00
greg
a305610a39 Some kind of pipeline working
thanks to the rust syn crate guy for the macro idea
2018-07-26 00:52:46 -07:00
greg
14f31a5186 Adding proc macro for codegen
This should hopefully make the compiler pass thing I want to do possible
2018-07-26 00:52:46 -07:00
greg
b936132ca6 Backtick operators supported in tokenizing 2018-07-26 00:52:46 -07:00
greg
a1016293ac Show artifacts on failure 2018-07-26 00:52:46 -07:00
greg
8e42f7e0bc TODO note 2018-07-26 00:52:46 -07:00
greg
b8a25dbaac Put this stuff back
More complicated to separate out repl than I thought
2018-07-26 00:52:46 -07:00
greg
66b6ddcf93 Start refactoring how interpreter options are split up 2018-07-26 00:52:46 -07:00
greg
1c0365529d Swap sigil from . to : 2018-07-26 00:52:46 -07:00
greg
f795612884 Want to change 'trait' to 'interface' 2018-07-26 00:52:46 -07:00
greg
c9ea48e9d1 Fix history adding 2018-07-26 00:52:46 -07:00
greg
65f42981ff Trait -> Interface 2018-07-26 00:52:46 -07:00
greg
e2970dbc42 Kill old advanced_slice_patterns 2018-07-26 00:52:46 -07:00
greg
7d2bc4188d Debug stages from command line 2018-07-26 00:52:46 -07:00
greg
eb987bb5b0 Make REPL interpreter return a value 2018-07-26 00:52:46 -07:00
greg
0de504eb9e Kill unused items in schala-repl 2018-07-26 00:52:45 -07:00
greg
635887f7a5 Start killing old code in language 2018-07-26 00:52:45 -07:00
greg
ecebbb2eae Fix interspersing of newlines in tokenizer infra 2018-07-26 00:52:45 -07:00
greg
78f12c8f1d Show err output when evaluating non-interactively 2018-07-26 00:52:45 -07:00
greg
ebda79e5fd Colored repl command output 2018-07-26 00:52:45 -07:00
greg
819a06503f Hook schala function up to debug booleans
Not sure if I like this API, but eh, it's what I've got
2018-07-26 00:52:45 -07:00
greg
664003a9d7 Add back color 2018-07-26 00:52:45 -07:00
greg
e1398bd063 rename schala_main -> repl_main 2018-07-26 00:52:45 -07:00
greg
898b185509 Add version string 2018-07-26 00:52:45 -07:00
greg
7592209cdb Get rid of all top-level dependencies 2018-07-26 00:52:45 -07:00
greg
6f43c3b81d move schala into separate crate 2018-07-26 00:52:45 -07:00
greg
072eeaa127 Color in terminal error output 2018-07-26 00:52:45 -07:00
greg
6bd3ed7b65 Move robo to separate crate 2018-07-26 00:52:45 -07:00
greg
8f19f2e414 Move rukka to crate 2018-07-26 00:52:45 -07:00
greg
5f279cb400 Move maaru into separate crate 2018-07-26 00:52:45 -07:00
greg
795b4adc6b Rename schala-lib -> schala-repl 2018-07-26 00:52:45 -07:00
greg
9d4082463a Removed (for now) LLVMCodeString 2018-07-26 00:52:45 -07:00
greg
43ade31f3e new thing compiles 2018-07-26 00:52:45 -07:00
greg
9f2fbda31f Switch over schala to new system 2018-07-26 00:52:45 -07:00
greg
b31325c315 Update schala example code 2018-07-26 00:52:45 -07:00
greg
95a2620754 Nested comments 2018-07-26 00:52:45 -07:00
greg
e67b22d109 Changing comments to use //, /* 2018-07-26 00:52:45 -07:00
greg
61eccba173 Starting to improve infrastrucutre for lang output
To make repl vs non-repl output better
2018-07-26 00:52:45 -07:00
greg
f56d7120c4 Hacky fix for displaying error output non-interactively 2018-07-26 00:52:45 -07:00
greg
6140de9f9c Some changes necessary to handle non-interactive code 2018-07-26 00:52:45 -07:00
greg
b54c71633c Eval list literals 2018-07-26 00:52:45 -07:00
greg
1eeafb80dc Parse list literals 2018-07-26 00:52:45 -07:00
greg
59d621ed75 Tighten some code 2018-07-26 00:52:45 -07:00
greg
76fadf0701 Rename ReplOutput -> LanguageOutput 2018-07-26 00:52:45 -07:00
greg
6e6d494d50 Make directory for schala source files 2018-07-26 00:52:45 -07:00
greg
a0bb2837c1 Index evaluation 2018-07-26 00:52:45 -07:00
greg
a4dd492c26 Proper index exprs 2018-07-26 00:52:45 -07:00
greg
d0b6840670 Some macro simplifications 2018-07-26 00:52:45 -07:00
greg
b65eb0e459 Trying to make tests less verbose 2018-07-26 00:52:45 -07:00
greg
3f1e83dfda Added test for lambda call 2018-07-26 00:52:45 -07:00
greg
5ddfc132e7 Changed BNF grammar of call statements
To allow calling lambdas
2018-07-26 00:52:45 -07:00
greg
f1f7f43e20 lambdas 2018-07-26 00:52:45 -07:00
greg
86d9e90e7c Print output of tuples 2018-07-26 00:52:45 -07:00
greg
a7672171a6 Handle tuple literals in type system 2018-07-26 00:52:45 -07:00
greg
08e10739e5 Sum types in type schema 2018-07-26 00:52:45 -07:00
greg
a300f78e19 Kill unused import 2018-07-26 00:52:45 -07:00
greg
0423017125 Kill some compiler warnings 2018-07-26 00:52:45 -07:00
greg
8ef5a28aff Evaluator now only prints when a builtin print is called 2018-07-26 00:52:45 -07:00
greg
a92a2e4454 Kill comments 2018-07-26 00:52:45 -07:00
greg
8d79074ea9 Fix bug in delimited macro
Had to do with bad strictness testing.
2018-07-26 00:52:45 -07:00
greg
4e7806d053 Improve tokenizer debug output 2018-07-26 00:52:45 -07:00
greg
507e0b7255 Cleanup 2018-07-26 00:52:45 -07:00
greg
9b760244d5 Include line count in token debug 2018-07-26 00:52:45 -07:00
greg
88e027f536 Munged types to make tokenizer compile 2018-07-26 00:52:45 -07:00
greg
2e41f8ffe3 SOme work
WIP
2018-07-26 00:52:45 -07:00
greg
b18c2eee96 Fixed bug w/ lines in functions
Also improved debugging
2018-07-26 00:52:45 -07:00
greg
0c78f50568 Frame-aware lookups 2018-07-26 00:52:45 -07:00
greg
2dc9b4c09f Kill debug 2018-07-26 00:52:45 -07:00
greg
73206d345e Better debugging for types 2018-07-26 00:52:45 -07:00
greg
1a74e16af5 Use UVars in type signatures of functions 2018-07-26 00:52:45 -07:00
greg
ae2182db5d Add history saving 2018-07-26 00:52:45 -07:00
greg
ad450469a5 Switch to rustyline library 2018-07-26 00:52:45 -07:00
greg
df88e33579 Introduced fresh type variable method 2018-07-26 00:52:45 -07:00
greg
9d72a92f0b Continuing work 2018-07-26 00:52:45 -07:00
greg
fa6c2a6f45 Re-added symbol table infra 2018-07-26 00:52:45 -07:00
greg
92e6830979 Some logic for function call inferring 2018-07-26 00:52:45 -07:00
greg
ef9cd04605 Starting on function application typechecking 2018-07-26 00:52:45 -07:00
greg
1eaf201145 Move some code around 2018-07-26 00:52:45 -07:00
greg
876373c9fd Function calls work 2018-07-26 00:52:45 -07:00
greg
63f5f155ae Temporarily disable type-erroring
and tighten some code
2018-07-26 00:52:45 -07:00
greg
51cf8a4824 Handle variable lookups 2018-07-26 00:52:45 -07:00
greg
e0cc12276c Evaluate binding declarations 2018-07-26 00:52:45 -07:00
greg
d69970a806 Separate Value and NamedStruct syntactic categories 2018-07-26 00:52:45 -07:00
greg
522d9fc951 Fixed | 2018-07-26 00:52:45 -07:00
greg
63c3e0a4db More operator stuff 2018-07-26 00:52:45 -07:00
greg
547def990e Operator changes 2018-07-26 00:52:45 -07:00
greg
6e105bac55 Fixed tests w/ respect to binop
There's a few unnecessary conversions of &str 's to Rc<String> and back
2018-07-26 00:52:45 -07:00
greg
a396c448ec Centralize data for prefix ops too 2018-07-26 00:52:45 -07:00
greg
d3ef980dc5 Added type information to binop definitions
Also started centralizing precedence there too
2018-07-26 00:52:45 -07:00
greg
df86e0c16e Make sigil field private 2018-07-26 00:52:45 -07:00
greg
274bf80b5d Function evaluation work 2018-07-26 00:52:45 -07:00
greg
f0a39ac88a Give State a pointer to its parent
For function call lookups
2018-07-26 00:52:45 -07:00
greg
85e65273fe Finished initial BinOp/PrefixOp 2018-07-26 00:52:45 -07:00
greg
413c5afe67 Starting to munge BinOp types
Incomplete, doesn't yet compile
2018-07-26 00:52:45 -07:00
greg
36174140bc ReplState -> State
Not everythign is a repl
2018-07-26 00:52:45 -07:00
greg
75ecfb4e86 Move bx! macro up to mod.rs
And make use of it in parser
2018-07-26 00:52:45 -07:00
greg
e86d401c90 Move anno-to-type to a method on TypeName 2018-07-26 00:52:45 -07:00
greg
b2319f0971 Fix tests too 2018-07-26 00:52:45 -07:00
greg
d423e88845 Separate tokenizing module
Parsing was getting too long
2018-07-26 00:52:45 -07:00
greg
5cb0e6715d Some work on binops 2018-07-26 00:52:45 -07:00
greg
5bb2c319e8 Some more type-checking work 2018-07-26 00:52:45 -07:00
greg
440783bb64 More work on evaluating applications
for later testing + to kill a compiler warning
2018-07-26 00:52:45 -07:00
greg
9834ee295e Fix traits, silence warnings 2018-07-26 00:52:45 -07:00
greg
9346bb9581 type of a declaration should be Void, not Unit
I think this makes sense

Also kill some compiler warnings
2018-07-26 00:52:45 -07:00
greg
f46f593c44 Types in bindings 2018-07-26 00:52:45 -07:00
greg
ec7d185ed5 Simplified match 2018-07-26 00:52:45 -07:00
greg
3f1cf1d975 Added trait declaration 2018-07-26 00:52:45 -07:00
greg
39ee550b54 More static type work 2018-07-26 00:52:45 -07:00
greg
d5df868f10 Finished basic constant type inference 2018-07-26 00:52:45 -07:00
greg
55629e6d9d More type implementing - WIP
This has a borrowing bug currently
2018-07-26 00:52:45 -07:00
greg
9d99971f49 Fix some integer overflows with binary and hex 2018-07-26 00:52:45 -07:00
greg
76575e9ba3 Starting basic type stuff 2018-07-26 00:52:45 -07:00
greg
a50d8d9e3f Starting over with types 2018-07-26 00:52:45 -07:00
greg
c2cd419e5a Additional TODO 2018-07-26 00:52:45 -07:00
greg
bcec8e27f8 Add todo note 2018-07-26 00:52:45 -07:00
greg
e6a015090c More type things 2018-07-26 00:52:45 -07:00
greg
c18bf9c29f Type singletons test work 2018-07-26 00:52:45 -07:00
greg
cfc507a2df TypeSingletonName broken out 2018-07-26 00:52:45 -07:00
greg
f7e88c7cab 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-07-26 00:52:45 -07:00
greg
4d0bfa2a52 Don't need clone() here 2018-07-26 00:52:45 -07:00
greg
99e5d86764 Kill separate is_digit method
I care about 10 vs 16 distinction
2018-07-26 00:52:45 -07:00
greg
17e8ebe789 Hex parsing done 2018-07-26 00:52:45 -07:00
greg
253a85005c Save settings on ctrl-D 2018-07-26 00:52:45 -07:00
greg
967e5cc436 Added a bunch of notes 2018-07-26 00:52:45 -07:00
greg
7a6ace5db1 Fix parse level calculation 2018-07-26 00:52:45 -07:00
greg
129af43e69 Proper indentation of parser debug 2018-07-26 00:52:45 -07:00
greg
17dccf65c8 Move some code around 2018-07-26 00:52:45 -07:00
greg
95c6a23bf1 Better hex literals 2018-07-26 00:52:45 -07:00
greg
2bff53846c Starting hex parsing 2018-07-26 00:52:45 -07:00
greg
514d117c7e Simplify some code 2018-07-26 00:52:45 -07:00
greg
ae65687a93 Assign a specific rocket version 2018-07-26 00:52:45 -07:00
greg
9ec983dc20 unified BoolAtom 2018-07-26 00:52:45 -07:00
greg
cab0ca6f47 Rukka source file 2018-07-26 00:52:45 -07:00
greg
8f6c80ac8c Print operation 2018-07-26 00:52:45 -07:00
greg
7f546fa879 Refactoring 2018-07-26 00:52:45 -07:00
greg
48a35aa382 Delete some unneeded code 2018-07-26 00:52:45 -07:00
greg
0c64b14be0 Forgot to change name here 2018-07-26 00:52:45 -07:00
greg
5d9fa6679b Name change
builtin -> primitive
2018-07-26 00:52:45 -07:00
greg
ea24ae1bb5 Get rid of some printlns 2018-07-26 00:52:45 -07:00
greg
0d2a0e3536 Implement lambda application 2018-07-26 00:52:45 -07:00
greg
339e3464e3 Plus and multiply 2018-07-26 00:52:45 -07:00
greg
c35b684bdd Builtins - + 2018-07-26 00:52:45 -07:00
greg
d11c518721 Framework for multiple environments 2018-07-26 00:52:45 -07:00
greg
8dde8c7381 Apply wokr 2018-07-26 00:52:45 -07:00
greg
47cad3712c Fixing quote 2018-07-26 00:52:45 -07:00
greg
ffcc0ef379 Starting builtins 2018-07-26 00:52:45 -07:00
greg
6766791627 Lambda abstraction 2018-07-26 00:52:45 -07:00
greg
05de5ebe61 Kill this linker thing 2018-07-26 00:52:45 -07:00
greg
98fa8403b3 Flesh out TODO, README 2018-07-26 00:52:45 -07:00
greg
ce83306581 Add Rukka to README 2018-07-26 00:52:45 -07:00
greg
29ebd35165 Kill unused code 2018-07-26 00:52:45 -07:00
greg
622b50a40c Some lambda work 2018-07-26 00:52:45 -07:00
greg
9f916c7c02 Remove a unimplemented 2018-07-26 00:52:45 -07:00
greg
85375bb9df Add fn literal variant 2018-07-26 00:52:45 -07:00
greg
d11500c643 Even more concise 2018-07-26 00:52:45 -07:00
greg
8493233b69 Refactoring 2018-07-26 00:52:45 -07:00
greg
60644ba3d7 Starting lambdas 2018-07-26 00:52:45 -07:00
greg
254f2ae4b8 Make var methods better 2018-07-26 00:52:45 -07:00
greg
e243b99d3b If expressions 2018-07-26 00:52:45 -07:00
greg
3d023a6704 Rukka - Variables 2018-07-26 00:52:45 -07:00
greg
857b77f2e3 Add schala idea 2018-07-26 00:52:45 -07:00
greg
4d89dcc85e Can specify language name with -l in any case 2018-07-26 00:52:45 -07:00
greg
fe0e58efe7 Go directly to langauge by name 2018-07-26 00:52:45 -07:00
greg
73612d1465 Define half-working 2018-07-26 00:52:45 -07:00
greg
afd2b018f4 Language name in prompt 2018-07-26 00:52:45 -07:00
greg
d1a15b64ff Get rid of old import 2018-07-26 00:52:45 -07:00
greg
66e8643382 eq? 2018-07-26 00:52:45 -07:00
greg
ad58fc1ad1 True and False primitives 2018-07-26 00:52:45 -07:00
greg
adc7be30a9 Some primitive implementations 2018-07-26 00:52:45 -07:00
greg
72097fa125 Fix pointer alias problem 2018-07-26 00:52:45 -07:00
greg
ae9d93f6dc Still tryign to make the pointer-munging work 2018-07-26 00:52:45 -07:00
greg
3d421c7039 This has broken sexp parsing 2018-07-26 00:52:45 -07:00
greg
166bc3b3cb Fix print bug 2018-07-26 00:52:45 -07:00
greg
2f263de8ba Convert to more lispish Cons 2018-07-26 00:52:45 -07:00
greg
46ae176498 Special forms list 2018-07-26 00:52:45 -07:00
greg
d84def35e7 Unwraps 2018-07-26 00:52:45 -07:00
greg
07e55ca04e Handle top-level empty list 2018-07-26 00:52:45 -07:00
greg
6dcf5c7945 print list 2018-07-26 00:52:45 -07:00
greg
568ee88f3a Tighten code 2018-07-26 00:52:45 -07:00
greg
8749ed984d Some more code 2018-07-26 00:52:45 -07:00
greg
559eaf54de Type simplification 2018-07-26 00:52:45 -07:00
greg
bf42b58ca5 State for eval 2018-07-26 00:52:45 -07:00
greg
ecdcb7ff3d Numbers 2018-07-26 00:52:45 -07:00
greg
766209e5b2 Fixed string parsing 2018-07-26 00:52:45 -07:00
greg
e9429ed62a Strings partway working 2018-07-26 00:52:45 -07:00
greg
6e188976f9 Quotes 2018-07-26 00:52:45 -07:00
greg
d235b47bc5 Change Symbol -> Word for token 2018-07-26 00:52:45 -07:00
greg
3fcb840ce5 Fix bug 2018-07-26 00:52:45 -07:00
greg
523bd179a4 Tighten code 2018-07-26 00:52:45 -07:00
greg
35e715dfd6 Intersperse 2018-07-26 00:52:45 -07:00
greg
6eb0fc8834 Parsing correctly yay 2018-07-26 00:52:45 -07:00
greg
c0a5418c27 Tokens 2018-07-26 00:52:45 -07:00
greg
42749c1ff6 Sexp parsing 2018-07-26 00:52:45 -07:00
greg
42b9507af0 Parses ( 2018-07-26 00:52:45 -07:00
greg
38e85e2c78 Some halfwritten stuff 2018-07-26 00:52:45 -07:00
greg
7c5fef49f8 List datatype 2018-07-26 00:52:45 -07:00
greg
c1e214c701 Add a new language - Rukka
This is a (simple) lisp, partially for fun, partially for testing the
generic interfaces
2018-07-26 00:52:45 -07:00
greg
66e3de41dd Make schala-lib::language private and reexport 2018-07-26 00:52:45 -07:00
greg
9545130fd3 Take TokenError type out of schala-lib 2018-07-26 00:52:45 -07:00
greg
ef7412dcd5 I don't need this syntax 2018-07-26 00:52:45 -07:00
greg
dee470cb8b Kill some packages from schala bin 2018-07-26 00:52:45 -07:00
greg
c057f068ef Get rid of unused imports 2018-07-26 00:52:45 -07:00
greg
c4dbdf1fe7 Refactor into libs part II
woo it compiles
2018-07-26 00:52:45 -07:00
greg
4c7174e4c4 Halfway done to library-ifying schala 2018-07-26 00:52:45 -07:00
greg
d0538faef3 PLIGenerators can be authoritative, not the instances themselves 2018-07-26 00:52:45 -07:00
greg
b97da01370 Some simplification 2018-07-26 00:52:45 -07:00
greg
b09efd3660 Passing things along as generators 2018-07-26 00:52:45 -07:00
greg
a42a58b155 Don't need mutex, kill it 2018-07-26 00:52:45 -07:00
greg
708c0ab103 Finally removed schala dependency
Now need to clena up everything
2018-07-26 00:52:45 -07:00
greg
1d9d0c4395 Okay this compiles
The secret (from #rust) appeared to be that Fn() needed to have + Send
explicitly annotated on it
2018-07-26 00:52:45 -07:00
greg
ffb87ebb82 Working on solution to Rocket state problem 2018-07-26 00:52:45 -07:00
greg
30c741f459 Some linker bullshit
I don't know why I needed to do this
2018-07-26 00:52:45 -07:00
greg
d19541b3e1 Splitting up some code
In preparation for splitting schala into crates
2018-07-26 00:52:45 -07:00
greg
3651461bbc Some more structure in evaluator 2018-07-26 00:52:45 -07:00
greg
7730457878 Revert "Starting to split project into multiple crates"
This reverts commit e3b0f4a51e.
Bah, this was a bad idea, wrong way to do it
2018-07-26 00:52:45 -07:00
greg
46dbac7f69 Starting to split project into multiple crates 2018-07-26 00:52:45 -07:00
greg
f68167f3a2 Halfway done with evaluating tuples 2018-07-26 00:52:45 -07:00
greg
c9625ffa77 Add module keyword 2018-07-26 00:52:45 -07:00
greg
cc3833754d 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
2018-07-26 00:52:45 -07:00
greg
9afbd2305f Literal non-primitive values 2018-07-26 00:52:45 -07:00
greg
d7564f81c9 Starting work on literal non-primitve values 2018-07-26 00:52:45 -07:00
greg
2fbb8f2b2f Can eval custom data constructors now 2018-07-26 00:52:45 -07:00
greg
1884eae191 Float literals, kill old code 2018-07-26 00:52:45 -07:00
greg
bb880d44fa Some more primitive types + binop-checking 2018-07-26 00:52:45 -07:00
greg
22b4738726 Add required imports 2018-07-26 00:52:45 -07:00
greg
0202aab181 Some partial work on refactoring type infer fn 2018-07-26 00:52:45 -07:00
greg
f9c9ed6b29 Add colored output to non-interactive 2018-07-26 00:52:45 -07:00
greg
04cb1616f7 Convert webapp to using included files 2018-07-26 00:52:45 -07:00
greg
5f1c46cb87 Fix type check macro to add symbol table 2018-07-26 00:52:45 -07:00
greg
0ea9bd3d95 More work with unification 2018-07-26 00:52:45 -07:00
greg
0cf56eea4f the evar table
TODO find a better way to represent this
2018-07-26 00:52:45 -07:00
greg
ab53c5394e Unify work 2018-07-26 00:52:45 -07:00
greg
f6c85951fe Move type-level func up 2018-07-26 00:52:45 -07:00
greg
c530715671 Okay I am figuring things out about hindley-milner again 2018-07-26 00:52:45 -07:00
greg
617a30b967 rename type_var to ty 2018-07-26 00:52:45 -07:00
greg
cd11d18385 String and () types 2018-07-26 00:52:45 -07:00
greg
f82c6199c0 Change around some stuff 2018-07-26 00:52:45 -07:00
greg
f75cd763f8 Change Variable to Value 2018-07-26 00:52:45 -07:00
greg
54c16f0190 Partial handling of user defined types 2018-07-26 00:52:45 -07:00
greg
8d8e3cd565 Starting to make unify actually work 2018-07-26 00:52:45 -07:00
greg
47975cf8f6 Convert unify to are types
b/c Type implements Clone
Maybe wanna kill this later for efficiency
2018-07-26 00:52:45 -07:00
greg
ddd861fbea Have + do something different with strings
Needed to introduce polymorphism soon
2018-07-26 00:52:45 -07:00
greg
200d0f9867 Operator typing a little bit 2018-07-26 00:52:45 -07:00
greg
3e44bd3a18 Slight refactoring 2018-07-26 00:52:45 -07:00
greg
e2a94280c2 Renamed all the type-related types 2018-07-26 00:52:45 -07:00
greg
c5b3bafe43 Move some type checking code around 2018-07-26 00:52:45 -07:00
greg
b417451536 Basic typing test 2018-07-26 00:52:45 -07:00
greg
a0faed3603 String types 2018-07-26 00:52:45 -07:00
54 changed files with 7218 additions and 2578 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
Cargo.lock
target
.schala_repl
.schala_history
rusty-tags.vi

View File

@@ -5,18 +5,14 @@ authors = ["greg <greg.shuflin@protonmail.com>"]
[dependencies]
llvm-sys = "*"
take_mut = "0.1.3"
itertools = "0.5.8"
getopts = "*"
linefeed = "0.2.2"
lazy_static = "0.2.8"
maplit = "*"
colored = "1.5"
serde = "1.0.15"
serde_derive = "1.0.15"
serde_json = "1.0.3"
rocket = "*"
rocket_codegen = "*"
rocket_contrib = "*"
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" }
[build-dependencies]
includedir_codegen = "0.2.0"
[workspace]

920
HindleyMilner.hs Normal file
View File

@@ -0,0 +1,920 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}
-- | This module is an extensively documented walkthrough for typechecking a
-- basic functional language using the Hindley-Damas-Milner algorithm.
--
-- In the end, we'll be able to infer the type of expressions like
--
-- @
-- find (λx. (>) x 0)
-- :: [Integer] -> Either () Integer
-- @
--
-- It can be used in multiple different forms:
--
-- * The source is written in literate programming style, so you can almost
-- read it from top to bottom, minus some few references to later topics.
-- * /Loads/ of doctests (runnable and verified code examples) are included
-- * The code is runnable in GHCi, all definitions are exposed.
-- * A small main module that gives many examples of what you might try out in
-- GHCi is also included.
-- * The Haddock output yields a nice overview over the definitions given, with
-- a nice rendering of a truckload of Haddock comments.
module HindleyMilner where
import Control.Monad.Trans
import Control.Monad.Trans.Except
import Control.Monad.Trans.State
import Data.Map (Map)
import qualified Data.Map as M
import Data.Monoid
import Data.Set (Set)
import qualified Data.Set as S
import Data.String
import Data.Text (Text)
import qualified Data.Text as T
-- $setup
--
-- For running doctests:
--
-- >>> :set -XOverloadedStrings
-- >>> :set -XOverloadedLists
-- >>> :set -XLambdaCase
-- >>> import qualified Data.Text.IO as T
-- >>> let putPprLn = T.putStrLn . ppr
-- #############################################################################
-- #############################################################################
-- * Preliminaries
-- #############################################################################
-- #############################################################################
-- #############################################################################
-- ** Prettyprinting
-- #############################################################################
-- | A prettyprinter class. Similar to 'Show', but with a focus on having
-- human-readable output as opposed to being valid Haskell.
class Pretty a where
ppr :: a -> Text
-- #############################################################################
-- ** Names
-- #############################################################################
-- | A 'name' is an identifier in the language we're going to typecheck.
-- Variables on both the term and type level have 'Name's, for example.
newtype Name = Name Text
deriving (Eq, Ord, Show)
-- | >>> "lorem" :: Name
-- Name "lorem"
instance IsString Name where
fromString = Name . T.pack
-- | >>> putPprLn (Name "var")
-- var
instance Pretty Name where
ppr (Name n) = n
-- #############################################################################
-- ** Monotypes
-- #############################################################################
-- | A monotype is an unquantified/unparametric type, in other words it contains
-- no @forall@s. Monotypes are the inner building blocks of all types. Examples
-- of monotypes are @Int@, @a@, @a -> b@.
--
-- In formal notation, 'MType's are often called τ (tau) types.
data MType = TVar Name -- ^ @a@
| TFun MType MType -- ^ @a -> b@
| TConst Name -- ^ @Int@, @()@, …
-- Since we can't declare our own types in our simple type system
-- here, we'll hard-code certain basic ones so we can typecheck some
-- familar functions that use them later.
| TList MType -- ^ @[a]@
| TEither MType MType -- ^ @Either a b@
| TTuple MType MType -- ^ @(a,b)@
deriving Show
-- | >>> putPprLn (TFun (TEither (TVar "a") (TVar "b")) (TFun (TVar "c") (TVar "d")))
-- Either a b → c → d
--
-- Using the 'IsString' instance:
--
-- >>> putPprLn (TFun (TEither "a" "b") (TFun "c" "d"))
-- Either a b → c → d
instance Pretty MType where
ppr = go False
where
go _ (TVar name) = ppr name
go _ (TList a) = "[" <> ppr a <> "]"
go _ (TEither l r) = "Either " <> ppr l <> " " <> ppr r
go _ (TTuple a b) = "(" <> ppr a <> ", " <> ppr b <> ")"
go _ (TConst name) = ppr name
go parenthesize (TFun a b)
| parenthesize = "(" <> lhs <> "" <> rhs <> ")"
| otherwise = lhs <> "" <> rhs
where lhs = go True a
rhs = go False b
-- | >>> "var" :: MType
-- TVar (Name "var")
instance IsString MType where
fromString = TVar . fromString
-- | The free variables of an 'MType'. This is simply the collection of all the
-- individual type variables occurring inside of it.
--
-- __Example:__ The free variables of @a -> b@ are @a@ and @b@.
freeMType :: MType -> Set Name
freeMType = \case
TVar a -> [a]
TFun a b -> freeMType a <> freeMType b
TList a -> freeMType a
TEither l r -> freeMType l <> freeMType r
TTuple a b -> freeMType a <> freeMType b
TConst _ -> []
-- | Substitute all the contained type variables mentioned in the substitution,
-- and leave everything else alone.
instance Substitutable MType where
applySubst s = \case
TVar a -> let Subst s' = s
in M.findWithDefault (TVar a) a s'
TFun f x -> TFun (applySubst s f) (applySubst s x)
TList a -> TList (applySubst s a)
TEither l r -> TEither (applySubst s l) (applySubst s r)
TTuple a b -> TTuple (applySubst s a) (applySubst s b)
c@TConst {} -> c
-- #############################################################################
-- ** Polytypes
-- #############################################################################
-- | A polytype is a monotype universally quantified over a number of type
-- variables. In Haskell, all definitions have polytypes, but since the @forall@
-- is implicit they look a bit like monotypes, maybe confusingly so. For
-- example, the type of @1 :: Int@ is actually @forall <nothing>. Int@, and the
-- type of @id@ is @forall a. a -> a@, although GHC displays it as @a -> a@.
--
-- A polytype claims to work "for all imaginable type parameters", very similar
-- to how a lambda claims to work "for all imaginable value parameters". We can
-- insert a value into a lambda's parameter to evaluate it to a new value, and
-- similarly we'll later insert types into a polytype's quantified variables to
-- gain new types.
--
-- __Example:__ in a definition @id :: forall a. a -> a@, the @a@ after the
-- ∀ ("forall") is the collection of type variables, and @a -> a@ is the 'MType'
-- quantified over. When we have such an @id@, we also have its specialized
-- version @Int -> Int@ available. This process will be the topic of the type
-- inference/unification algorithms.
--
-- In formal notation, 'PType's are often called σ (sigma) types.
--
-- The purpose of having monotypes and polytypes is that we'd like to only have
-- universal quantification at the top level, restricting our language to rank-1
-- polymorphism, where type inferece is total (all types can be inferred) and
-- simple (only a handful of typing rules). Weakening this constraint would be
-- easy: if we allowed universal quantification within function types we would
-- get rank-N polymorphism. Taking it even further to allow it anywhere,
-- effectively replacing all occurrences of 'MType' with 'PType', yields
-- impredicative types. Both these extensions make the type system
-- *significantly* more complex though.
data PType = Forall (Set Name) MType -- ^ ∀{α}. τ
-- | >>> putPprLn (Forall ["a"] (TFun "a" "a"))
-- ∀a. a → a
instance Pretty PType where
ppr (Forall qs mType) = "" <> pprUniversals <> ". " <> ppr mType
where
pprUniversals
| S.null qs = ""
| otherwise = (T.intercalate " " . map ppr . S.toList) qs
-- | The free variables of a 'PType' are the free variables of the contained
-- 'MType', except those universally quantified.
--
-- >>> let sigma = Forall ["a"] (TFun "a" (TFun (TTuple "b" "a") "c"))
-- >>> putPprLn sigma
-- ∀a. a → (b, a) → c
-- >>> let display = T.putStrLn . T.intercalate ", " . foldMap (\x -> [ppr x])
-- >>> display (freePType sigma)
-- b, c
freePType :: PType -> Set Name
freePType (Forall qs mType) = freeMType mType `S.difference` qs
-- | Substitute all the free type variables.
instance Substitutable PType where
applySubst (Subst subst) (Forall qs mType) =
let qs' = M.fromSet (const ()) qs
subst' = Subst (subst `M.difference` qs')
in Forall qs (applySubst subst' mType)
-- #############################################################################
-- ** The environment
-- #############################################################################
-- | The environment consists of all the values available in scope, and their
-- associated polytypes. Other common names for it include "(typing) context",
-- and because of the commonly used symbol for it sometimes directly
-- \"Gamma"/@"Γ"@.
--
-- There are two kinds of membership in an environment,
--
-- - @∈@: an environment @Γ@ can be viewed as a set of @(value, type)@ pairs,
-- and we can test whether something is /literally contained/ by it via
-- x:σ ∈ Γ
-- - @⊢@, pronounced /entails/, describes all the things that are well-typed,
-- given an environment @Γ@. @Γ ⊢ x:τ@ can thus be seen as a judgement that
-- @x:τ@ is /figuratively contained/ in @Γ@.
--
-- For example, the environment @{x:Int}@ literally contains @x@, but given
-- this, it also entails @λy. x@, @λy z. x@, @let id = λy. y in id x@ and so on.
--
-- In Haskell terms, the environment consists of all the things you currently
-- have available, or that can be built by comining them. If you import the
-- Prelude, your environment entails
--
-- @
-- id → ∀a. a→a
-- map → ∀a b. (a→b) → [a] → [b]
-- putStrLn → ∀∅. String → IO ()
-- …
-- id map → ∀a b. (a→b) → [a] → [b]
-- map putStrLn → ∀∅. [String] -> [IO ()]
-- …
-- @
newtype Env = Env (Map Name PType)
-- | >>> :{
-- putPprLn (Env
-- [ ("id", Forall ["a"] (TFun "a" "a"))
-- , ("const", Forall ["a", "b"] (TFun "a" (TFun "b" "a"))) ])
-- :}
-- Γ = { const : ∀a b. a → b → a
-- , id : ∀a. a → a }
instance Pretty Env where
ppr (Env env) = "Γ = { " <> T.intercalate "\n , " pprBindings <> " }"
where
bindings = M.assocs env
pprBinding (name, pType) = ppr name <> " : " <> ppr pType
pprBindings = map pprBinding bindings
-- | The free variables of an 'Env'ironment are all the free variables of the
-- 'PType's it contains.
freeEnv :: Env -> Set Name
freeEnv (Env env) = let allPTypes = M.elems env
in S.unions (map freePType allPTypes)
-- | Performing a 'Subst'itution in an 'Env'ironment means performing that
-- substituion on all the contained 'PType's.
instance Substitutable Env where
applySubst s (Env env) = Env (M.map (applySubst s) env)
-- #############################################################################
-- ** Substitutions
-- #############################################################################
-- | A substitution is a mapping from type variables to 'MType's. Applying a
-- substitution means applying those replacements. For example, the substitution
-- @a -> Int@ applied to @a -> a@ yields the result @Int -> Int@.
--
-- A key concept behind Hindley-Milner is that once we dive deeper into an
-- expression, we learn more about our type variables. We might learn that @a@
-- has to be specialized to @b -> b@, and then later on that @b@ is actually
-- @Int@. Substitutions are an organized way of carrying this information along.
newtype Subst = Subst (Map Name MType)
-- | We're going to apply substitutions to a variety of other values that
-- somehow contain type variables, so we overload this application operation in
-- a class here.
--
-- Laws:
--
-- @
-- 'applySubst' 'mempty' ≡ 'id'
-- 'applySubst' (s1 '<>' s2) ≡ 'applySubst' s1 . 'applySubst' s2
-- @
class Substitutable a where
applySubst :: Subst -> a -> a
instance (Substitutable a, Substitutable b) => Substitutable (a,b) where
applySubst s (x,y) = (applySubst s x, applySubst s y)
-- | @'applySubst' s1 s2@ applies one substitution to another, replacing all the
-- bindings in the second argument @s2@ with their values mentioned in the first
-- one (@s1@).
instance Substitutable Subst where
applySubst s (Subst target) = Subst (fmap (applySubst s) target)
-- | >>> :{
-- putPprLn (Subst
-- [ ("a", TFun "b" "b")
-- , ("b", TEither "c" "d") ])
-- :}
-- { a > b → b
-- , b > Either c d }
instance Pretty Subst where
ppr (Subst s) = "{ " <> T.intercalate "\n, " [ ppr k <> " > " <> ppr v | (k,v) <- M.toList s ] <> " }"
-- | Combine two substitutions by applying all substitutions mentioned in the
-- first argument to the type variables contained in the second.
instance Monoid Subst where
-- Considering that all we can really do with a substitution is apply it, we
-- can use the one of 'Substitutable's laws to show that substitutions
-- combine associatively,
--
-- @
-- applySubst (compose s1 (compose s2 s3))
-- = applySubst s1 . applySubst (compose s2 s3)
-- = applySubst s1 . applySubst s2 . applySubst s3
-- = applySubst (compose s1 s2) . applySubst s3
-- = applySubst (compose (compose s1 s2) s3)
-- @
mappend subst1 subst2 = Subst (s1 `M.union` s2)
where
Subst s1 = subst1
Subst s2 = applySubst subst1 subst2
mempty = Subst M.empty
-- #############################################################################
-- #############################################################################
-- * Typechecking
-- #############################################################################
-- #############################################################################
-- $ Typechecking does two things:
--
-- 1. If two types are not immediately identical, attempt to 'unify' them
-- to get a type compatible with both of them
-- 2. 'infer' the most general type of a value by comparing the values in its
-- definition with the 'Env'ironment
-- #############################################################################
-- ** Inference context
-- #############################################################################
-- | The inference type holds a supply of unique names, and can fail with a
-- descriptive error if something goes wrong.
--
-- /Invariant:/ the supply must be infinite, or we might run out of names to
-- give to things.
newtype Infer a = Infer (ExceptT InferError (State [Name]) a)
deriving (Functor, Applicative, Monad)
-- | Errors that can happen during the type inference process.
data InferError =
-- | Two types that don't match were attempted to be unified.
--
-- For example, @a -> a@ and @Int@ do not unify.
--
-- >>> putPprLn (CannotUnify (TFun "a" "a") (TConst "Int"))
-- Cannot unify a → a with Int
CannotUnify MType MType
-- | A 'TVar' is bound to an 'MType' that already contains it.
--
-- The canonical example of this is @λx. x x@, where the first @x@
-- in the body has to have type @a -> b@, and the second one @a@. Since
-- they're both the same @x@, this requires unification of @a@ with
-- @a -> b@, which only works if @a = a -> b = (a -> b) -> b = …@, yielding
-- an infinite type.
--
-- >>> putPprLn (OccursCheckFailed "a" (TFun "a" "a"))
-- Occurs check failed: a already appears in a → a
| OccursCheckFailed Name MType
-- | The value of an unknown identifier was read.
--
-- >>> putPprLn (UnknownIdentifier "a")
-- Unknown identifier: a
| UnknownIdentifier Name
deriving Show
-- | >>> putPprLn (CannotUnify (TEither "a" "b") (TTuple "a" "b"))
-- Cannot unify Either a b with (a, b)
instance Pretty InferError where
ppr = \case
CannotUnify t1 t2 ->
"Cannot unify " <> ppr t1 <> " with " <> ppr t2
OccursCheckFailed name ty ->
"Occurs check failed: " <> ppr name <> " already appears in " <> ppr ty
UnknownIdentifier name ->
"Unknown identifier: " <> ppr name
-- | Evaluate a value in an 'Infer'ence context.
--
-- >>> let expr = EAbs "f" (EAbs "g" (EAbs "x" (EApp (EApp "f" "x") (EApp "g" "x"))))
-- >>> putPprLn expr
-- λf g x. f x (g x)
-- >>> let inferred = runInfer (infer (Env []) expr)
-- >>> let demonstrate = \case Right (_, ty) -> T.putStrLn (":: " <> ppr ty)
-- >>> demonstrate inferred
-- :: (c → e → f) → (c → e) → c → f
runInfer :: Infer a -- ^ Inference data
-> Either InferError a
runInfer (Infer inf) =
evalState (runExceptT inf) (map Name (infiniteSupply alphabet))
where
alphabet = map T.singleton ['a'..'z']
-- [a, b, c] ==> [a,b,c, a1,b1,c1, a2,b2,c2, …]
infiniteSupply supply = supply <> addSuffixes supply (1 :: Integer)
where
addSuffixes xs n = map (\x -> addSuffix x n) xs <> addSuffixes xs (n+1)
addSuffix x n = x <> T.pack (show n)
-- | Throw an 'InferError' in an 'Infer'ence context.
--
-- >>> case runInfer (throw (UnknownIdentifier "var")) of Left err -> putPprLn err
-- Unknown identifier: var
throw :: InferError -> Infer a
throw = Infer . throwE
-- #############################################################################
-- ** Unification
-- #############################################################################
-- $ Unification describes the process of making two different types compatible
-- by specializing them where needed. A desirable property to have here is being
-- able to find the most general unifier. Luckily, we'll be able to do that in
-- our type system.
-- | The unification of two 'MType's is the most general substituion that can be
-- applied to both of them in order to yield the same result.
--
-- >>> let m1 = TFun "a" "b"
-- >>> putPprLn m1
-- a → b
-- >>> let m2 = TFun "c" (TEither "d" "e")
-- >>> putPprLn m2
-- c → Either d e
-- >>> let inferSubst = unify (m1, m2)
-- >>> case runInfer inferSubst of Right subst -> putPprLn subst
-- { a > c
-- , b > Either d e }
unify :: (MType, MType) -> Infer Subst
unify = \case
(TFun a b, TFun x y) -> unifyBinary (a,b) (x,y)
(TVar v, x) -> v `bindVariableTo` x
(x, TVar v) -> v `bindVariableTo` x
(TConst a, TConst b) | a == b -> pure mempty
(TList a, TList b) -> unify (a,b)
(TEither a b, TEither x y) -> unifyBinary (a,b) (x,y)
(TTuple a b, TTuple x y) -> unifyBinary (a,b) (x,y)
(a, b) -> throw (CannotUnify a b)
where
-- Unification of binary type constructors, such as functions and Either.
-- Unification is first done for the first operand, and assuming the
-- required substitution, for the second one.
unifyBinary :: (MType, MType) -> (MType, MType) -> Infer Subst
unifyBinary (a,b) (x,y) = do
s1 <- unify (a, x)
s2 <- unify (applySubst s1 (b, y))
pure (s1 <> s2)
-- | Build a 'Subst'itution that binds a 'Name' of a 'TVar' to an 'MType'. The
-- resulting substitution should be idempotent, i.e. applying it more than once
-- to something should not be any different from applying it only once.
--
-- - In the simplest case, this just means building a substitution that just
-- does that.
-- - Substituting a 'Name' with a 'TVar' with the same name unifies a type
-- variable with itself, and the resulting substitution does nothing new.
-- - If the 'Name' we're trying to bind to an 'MType' already occurs in that
-- 'MType', the resulting substitution would not be idempotent: the 'MType'
-- would be replaced again, yielding a different result. This is known as the
-- Occurs Check.
bindVariableTo :: Name -> MType -> Infer Subst
bindVariableTo name (TVar v) | boundToSelf = pure mempty
where
boundToSelf = name == v
bindVariableTo name mType | name `occursIn` mType = throw (OccursCheckFailed name mType)
where
n `occursIn` ty = n `S.member` freeMType ty
bindVariableTo name mType = pure (Subst (M.singleton name mType))
-- #############################################################################
-- ** Type inference
-- #############################################################################
-- $ Type inference is the act of finding out a value's type by looking at the
-- environment it is in, in order to make it compatible with it.
--
-- In literature, the Hindley-Damas-Milner inference algorithm ("Algorithm W")
-- is often presented in the style of logical formulas, and below you'll find
-- that version along with code that actually does what they say.
--
-- These formulas look a bit like fractions, where the "numerator" is a
-- collection of premises, and the denominator is the consequence if all of them
-- hold.
--
-- __Example:__
--
-- @
-- Γ ⊢ even : Int → Bool Γ ⊢ 1 : Int
--
-- Γ ⊢ even 1 : Bool
-- @
--
-- means that if we have a value of type @Int -> Bool@ called "even" and a value
-- of type @Int@ called @1@, then we also have a value of type @Bool@ via
-- @even 1@ available to us.
--
-- The actual inference rules are polymorphic versions of this example, and
-- the code comments will explain each step in detail.
-- -----------------------------------------------------------------------------
-- *** The language: typed lambda calculus
-- -----------------------------------------------------------------------------
-- | The syntax tree of the language we'd like to typecheck. You can view it as
-- a close relative to simply typed lambda calculus, having only the most
-- necessary syntax elements.
--
-- Since 'ELet' is non-recursive, the usual fixed-point function
-- @fix : (a → a) → a@ can be introduced to allow recursive definitions.
data Exp = ELit Lit -- ^ True, 1
| EVar Name -- ^ @x@
| EApp Exp Exp -- ^ @f x@
| EAbs Name Exp -- ^ @λx. e@
| ELet Name Exp Exp -- ^ @let x = e in e'@ (non-recursive)
deriving Show
-- | Literals we'd like to support. Since we can't define new data types in our
-- simple type system, we'll have to hard-code the possible ones here.
data Lit = LBool Bool
| LInteger Integer
deriving Show
-- | >>> putPprLn (EAbs "f" (EAbs "g" (EAbs "x" (EApp (EApp "f" "x") (EApp "g" "x")))))
-- λf g x. f x (g x)
instance Pretty Exp where
ppr (ELit lit) = ppr lit
ppr (EVar name) = ppr name
ppr (EApp f x) = pprApp1 f <> " " <> pprApp2 x
where
pprApp1 = \case
eLet@ELet{} -> "(" <> ppr eLet <> ")"
eLet@EAbs{} -> "(" <> ppr eLet <> ")"
e -> ppr e
pprApp2 = \case
eApp@EApp{} -> "(" <> ppr eApp <> ")"
e -> pprApp1 e
ppr x@EAbs{} = pprAbs True x
where
pprAbs True (EAbs name expr) = "λ" <> ppr name <> pprAbs False expr
pprAbs False (EAbs name expr) = " " <> ppr name <> pprAbs False expr
pprAbs _ expr = ". " <> ppr expr
ppr (ELet name value body) =
"let " <> ppr name <> " = " <> ppr value <> " in " <> ppr body
-- | >>> putPprLn (LBool True)
-- True
--
-- >>> putPprLn (LInteger 127)
-- 127
instance Pretty Lit where
ppr = \case
LBool b -> showT b
LInteger i -> showT i
where
showT :: Show a => a -> Text
showT = T.pack . show
-- | >>> "var" :: Exp
-- EVar (Name "var")
instance IsString Exp where
fromString = EVar . fromString
-- -----------------------------------------------------------------------------
-- *** Some useful definitions
-- -----------------------------------------------------------------------------
-- | Generate a fresh 'Name' in a type 'Infer'ence context. An example use case
-- of this is η expansion, which transforms @f@ into @λx. f x@, where "x" is a
-- new name, i.e. unbound in the current context.
fresh :: Infer MType
fresh = drawFromSupply >>= \case
Right name -> pure (TVar name)
Left err -> throw err
where
drawFromSupply :: Infer (Either InferError Name)
drawFromSupply = Infer (do
s:upply <- lift get
lift (put upply)
pure (Right s) )
-- | Add a new binding to the environment.
--
-- The Haskell equivalent would be defining a new value, for example in module
-- scope or in a @let@ block. This corresponds to the "comma" operation used in
-- formal notation,
--
-- @
-- Γ, x:σ ≡ extendEnv Γ (x,σ)
-- @
extendEnv :: Env -> (Name, PType) -> Env
extendEnv (Env env) (name, pType) = Env (M.insert name pType env)
-- -----------------------------------------------------------------------------
-- *** Inferring the types of all language constructs
-- -----------------------------------------------------------------------------
-- | Infer the type of an 'Exp'ression in an 'Env'ironment, resulting in the
-- 'Exp's 'MType' along with a substitution that has to be done in order to reach
-- this goal.
--
-- This is widely known as /Algorithm W/.
infer :: Env -> Exp -> Infer (Subst, MType)
infer env = \case
ELit lit -> inferLit lit
EVar name -> inferVar env name
EApp f x -> inferApp env f x
EAbs x e -> inferAbs env x e
ELet x e e' -> inferLet env x e e'
-- | Literals such as 'True' and '1' have their types hard-coded.
inferLit :: Lit -> Infer (Subst, MType)
inferLit lit = pure (mempty, TConst litTy)
where
litTy = case lit of
LBool {} -> "Bool"
LInteger {} -> "Integer"
-- | Inferring the type of a variable is done via
--
-- @
-- x:σ ∈ Γ τ = instantiate(σ)
-- [Var]
-- Γ ⊢ x:τ
-- @
--
-- This means that if @Γ@ /literally contains/ (@∈@) a value, then it also
-- /entails it/ (@⊢@) in all its instantiations.
inferVar :: Env -> Name -> Infer (Subst, MType)
inferVar env name = do
sigma <- lookupEnv env name -- x:σ ∈ Γ
tau <- instantiate sigma -- τ = instantiate(σ)
-- ------------------
pure (mempty, tau) -- Γ ⊢ x:τ
-- | Look up the 'PType' of a 'Name' in the 'Env'ironment.
--
-- This checks whether @x:σ@ is /literally contained/ in @Γ@. For more details
-- about this, see the documentation of 'Env'.
--
-- To give a Haskell analogon, looking up @id@ when @Prelude@ is loaded, the
-- resulting 'PType' would be @id@'s type, namely @forall a. a -> a@.
lookupEnv :: Env -> Name -> Infer PType
lookupEnv (Env env) name = case M.lookup name env of
Just x -> pure x
Nothing -> throw (UnknownIdentifier name)
-- | Bind all quantified variables of a 'PType' to 'fresh' type variables.
--
-- __Example:__ instantiating @forall a. a -> b -> a@ results in the 'MType'
-- @c -> b -> c@, where @c@ is a fresh name (to avoid shadowing issues).
--
-- You can picture the 'PType' to be the prototype converted to an instantiated
-- 'MType', which can now be used in the unification process.
--
-- Another way of looking at it is by simply forgetting which variables were
-- quantified, carefully avoiding name clashes when doing so.
--
-- 'instantiate' can also be seen as the opposite of 'generalize', which we'll
-- need later to convert an 'MType' to a 'PType'.
instantiate :: PType -> Infer MType
instantiate (Forall qs t) = do
subst <- substituteAllWithFresh qs
pure (applySubst subst t)
where
-- For each given name, add a substitution from that name to a fresh type
-- variable to the result.
substituteAllWithFresh :: Set Name -> Infer Subst
substituteAllWithFresh xs = do
let freshSubstActions = M.fromSet (const fresh) xs
freshSubsts <- sequenceA freshSubstActions
pure (Subst freshSubsts)
-- | Function application captures the fact that if we have a function and an
-- argument we can give to that function, we also have the result value of the
-- result type available to us.
--
-- @
-- Γ ⊢ f : fτ Γ ⊢ x : xτ fxτ = fresh unify(fτ, xτ → fxτ)
-- [App]
-- Γ ⊢ f x : fxτ
-- @
--
-- This rule says that given a function and a value with a type, the function
-- type has to unify with a function type that allows the value type to be its
-- argument.
inferApp
:: Env
-> Exp -- ^ __f__ x
-> Exp -- ^ f __x__
-> Infer (Subst, MType)
inferApp env f x = do
(s1, fTau) <- infer env f -- f : fτ
(s2, xTau) <- infer (applySubst s1 env) x -- x : xτ
fxTau <- fresh -- fxτ = fresh
s3 <- unify (applySubst s2 fTau, TFun xTau fxTau) -- unify (fτ, xτ → fxτ)
let s = s3 <> s2 <> s1 -- --------------------
pure (s, applySubst s3 fxTau) -- f x : fxτ
-- | Lambda abstraction is based on the fact that when we introduce a new
-- variable, the resulting lambda maps from that variable's type to the type of
-- the body.
--
-- @
-- τ = fresh σ = ∀∅. τ Γ, x:σ ⊢ e:τ'
-- [Abs]
-- Γ ⊢ λx.e : τ→τ'
-- @
--
-- Here, @Γ, x:τ@ is @Γ@ extended by one additional mapping, namely @x:τ@.
--
-- Abstraction is typed by extending the environment by a new 'MType', and if
-- under this assumption we can construct a function mapping to a value of that
-- type, we can say that the lambda takes a value and maps to it.
inferAbs
:: Env
-> Name -- ^ λ__x__. e
-> Exp -- ^ λx. __e__
-> Infer (Subst, MType)
inferAbs env x e = do
tau <- fresh -- τ = fresh
let sigma = Forall [] tau -- σ = ∀∅. τ
env' = extendEnv env (x, sigma) -- Γ, x:σ
(s, tau') <- infer env' e -- … ⊢ e:τ'
-- ---------------
pure (s, TFun (applySubst s tau) tau') -- λx.e : τ→τ'
-- | A let binding allows extending the environment with new bindings in a
-- principled manner. To do this, we first have to typecheck the expression to
-- be introduced. The result of this is then generalized to a 'PType', since let
-- bindings introduce new polymorphic values, which are then added to the
-- environment. Now we can finally typecheck the body of the "in" part of the
-- let binding.
--
-- Note that in our simple language, let is non-recursive, but recursion can be
-- introduced as usual by adding a primitive @fix : (a → a) → a@ if desired.
--
-- @
-- Γ ⊢ e:τ σ = gen(Γ,τ) Γ, x:σ ⊢ e':τ'
-- [Let]
-- Γ ⊢ let x = e in e' : τ'
-- @
inferLet
:: Env
-> Name -- ^ let __x__ = e in e'
-> Exp -- ^ let x = __e__ in e'
-> Exp -- ^ let x = e in __e'__
-> Infer (Subst, MType)
inferLet env x e e' = do
(s1, tau) <- infer env e -- Γ ⊢ e:τ
let env' = applySubst s1 env
let sigma = generalize env' tau -- σ = gen(Γ,τ)
let env'' = extendEnv env' (x, sigma) -- Γ, x:σ
(s2, tau') <- infer env'' e' -- Γ ⊢ …
-- --------------------------
pure (s2 <> s1, tau') -- … let x = e in e' : τ'
-- | Generalize an 'MType' to a 'PType' by universally quantifying over all the
-- type variables contained in it, except those already free in the environment.
--
-- >>> let tau = TFun "a" (TFun "b" "a")
-- >>> putPprLn tau
-- a → b → a
-- >>> putPprLn (generalize (Env [("x", Forall [] "b")]) tau)
-- ∀a. a → b → a
--
-- In more formal notation,
--
-- @
-- gen(Γ,τ) = ∀{α}. τ
-- where {α} = free(τ) free(Γ)
-- @
--
-- 'generalize' can also be seen as the opposite of 'instantiate', which
-- converts a 'PType' to an 'MType'.
generalize :: Env -> MType -> PType
generalize env mType = Forall qs mType
where
qs = freeMType mType `S.difference` freeEnv env

185
Main.hs Normal file
View File

@@ -0,0 +1,185 @@
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where
import qualified Data.Map as M
import Data.Monoid
import Data.Text (Text)
import qualified Data.Text.IO as T
import HindleyMilner
-- #############################################################################
-- #############################################################################
-- * Testing
-- #############################################################################
-- #############################################################################
-- #############################################################################
-- ** A small custom Prelude
-- #############################################################################
prelude :: Env
prelude = Env (M.fromList
[ ("(*)", Forall [] (tInteger ~> tInteger ~> tInteger))
, ("(+)", Forall [] (tInteger ~> tInteger ~> tInteger))
, ("(,)", Forall ["a","b"] ("a" ~> "b" ~> TTuple "a" "b"))
, ("(-)", Forall [] (tInteger ~> tInteger ~> tInteger))
, ("(.)", Forall ["a", "b", "c"] (("b" ~> "c") ~> ("a" ~> "b") ~> "a" ~> "c"))
, ("(<)", Forall [] (tInteger ~> tInteger ~> tBool))
, ("(<=)", Forall [] (tInteger ~> tInteger ~> tBool))
, ("(>)", Forall [] (tInteger ~> tInteger ~> tBool))
, ("(>=)", Forall [] (tInteger ~> tInteger ~> tBool))
, ("const", Forall ["a","b"] ("a" ~> "b" ~> "a"))
, ("Cont/>>=", Forall ["a"] ((("a" ~> "r") ~> "r") ~> ("a" ~> (("b" ~> "r") ~> "r")) ~> (("b" ~> "r") ~> "r")))
, ("find", Forall ["a","b"] (("a" ~> tBool) ~> TList "a" ~> tMaybe "a"))
, ("fix", Forall ["a"] (("a" ~> "a") ~> "a"))
, ("foldr", Forall ["a","b"] (("a" ~> "b" ~> "b") ~> "b" ~> TList "a" ~> "b"))
, ("id", Forall ["a"] ("a" ~> "a"))
, ("ifThenElse", Forall ["a"] (tBool ~> "a" ~> "a" ~> "a"))
, ("Left", Forall ["a","b"] ("a" ~> TEither "a" "b"))
, ("length", Forall ["a"] (TList "a" ~> tInteger))
, ("map", Forall ["a","b"] (("a" ~> "b") ~> TList "a" ~> TList "b"))
, ("reverse", Forall ["a"] (TList "a" ~> TList "a"))
, ("Right", Forall ["a","b"] ("b" ~> TEither "a" "b"))
, ("[]", Forall ["a"] (TList "a"))
, ("(:)", Forall ["a"] ("a" ~> TList "a" ~> TList "a"))
])
where
tBool = TConst "Bool"
tInteger = TConst "Integer"
tMaybe = TEither (TConst "()")
-- | Synonym for 'TFun' to make writing type signatures easier.
--
-- Instead of
--
-- @
-- Forall ["a","b"] (TFun "a" (TFun "b" "a"))
-- @
--
-- we can write
--
-- @
-- Forall ["a","b"] ("a" ~> "b" ~> "a")
-- @
(~>) :: MType -> MType -> MType
(~>) = TFun
infixr 9 ~>
-- #############################################################################
-- ** Run it!
-- #############################################################################
-- | Run type inference on a cuple of values
main :: IO ()
main = do
let inferAndPrint = T.putStrLn . (" " <>) . showType prelude
T.putStrLn "Well-typed:"
do
inferAndPrint (lambda ["x"] "x")
inferAndPrint (lambda ["f","g","x"] (apply "f" ["x", apply "g" ["x"]]))
inferAndPrint (lambda ["f","g","x"] (apply "f" [apply "g" ["x"]]))
inferAndPrint (lambda ["m", "k", "c"] (apply "m" [lambda ["x"] (apply "k" ["x", "c"])])) -- >>= for Cont
inferAndPrint (lambda ["f"] (apply "(.)" ["reverse", apply "map" ["f"]]))
inferAndPrint (apply "find" [lambda ["x"] (apply "(>)" ["x", int 0])])
inferAndPrint (apply "map" [apply "map" ["map"]])
inferAndPrint (apply "(*)" [int 1, int 2])
inferAndPrint (apply "foldr" ["(+)", int 0])
inferAndPrint (apply "map" ["length"])
inferAndPrint (apply "map" ["map"])
inferAndPrint (lambda ["x"] (apply "ifThenElse" [apply "(<)" ["x", int 0], int 0, "x"]))
inferAndPrint (lambda ["x"] (apply "fix" [lambda ["xs"] (apply "(:)" ["x", "xs"])]))
T.putStrLn "Ill-typed:"
do
inferAndPrint (apply "(*)" [int 1, bool True])
inferAndPrint (apply "foldr" [int 1])
inferAndPrint (lambda ["x"] (apply "x" ["x"]))
inferAndPrint (lambda ["x"] (ELet "xs" (apply "(:)" ["x", "xs"]) "xs"))
-- | Build multiple lambda bindings.
--
-- Instead of
--
-- @
-- EAbs "f" (EAbs "x" (EApp "f" "x"))
-- @
--
-- we can write
--
-- @
-- lambda ["f", "x"] (EApp "f" "x")
-- @
--
-- for
--
-- @
-- λf x. f x
-- @
lambda :: [Name] -> Exp -> Exp
lambda names expr = foldr EAbs expr names
-- | Apply a function to multiple arguments.
--
-- Instead of
--
-- @
-- EApp (EApp (EApp "f" "x") "y") "z")
-- @
--
-- we can write
--
-- @
-- apply "f" ["x", "y", "z"]
-- @
--
-- for
--
-- @
-- f x y z
-- @
apply :: Exp -> [Exp] -> Exp
apply = foldl EApp
-- | Construct an integer literal.
int :: Integer -> Exp
int = ELit . LInteger
-- | Construct a boolean literal.
bool :: Bool -> Exp
bool = ELit . LBool
-- | Convenience function to run type inference algorithm
showType :: Env -- ^ Starting environment, e.g. 'prelude'.
-> Exp -- ^ Expression to typecheck
-> Text -- ^ Text representation of the result. Contains an error
-- message on failure.
showType env expr =
case (runInfer . fmap (generalize (Env mempty) . uncurry applySubst) . infer env) expr of
Left err -> "Error inferring type of " <> ppr expr <>": " <> ppr err
Right ty -> ppr expr <> " :: " <> ppr ty

View File

@@ -1,18 +1,22 @@
TODO:
-null-only language should be called Maaru
-haskell-ish langauge should be called Robo
-typeful scripting language should be called schala
rename accordingly!
# Schala - a programming language meta-interpreter
Schala is a Rust-language framework written to make it easy to
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 methods
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
@@ -29,6 +33,8 @@ 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,
@@ -43,6 +49,8 @@ system.
* *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
@@ -50,6 +58,8 @@ of learning how to write a programming language.
### Type-checking
https://skillsmatter.com/skillscasts/10868-inside-the-rust-compiler
https://www.youtube.com/watch?v=il3gD7XMdmA
http://dev.stephendiehl.com/fun/006_hindley_milner.html
### Evaluation
*Understanding Computation*, Tom Stuart, O'Reilly 2013
@@ -58,6 +68,7 @@ https://skillsmatter.com/skillscasts/10868-inside-the-rust-compiler
### Parsing
http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
https://soc.github.io/languages/unified-condition-syntax
[Crafting Interpreters](http://www.craftinginterpreters.com/)

126
TODO.md Normal file
View File

@@ -0,0 +1,126 @@
# TODO Items
-make sure to include a :doc command at the REPL that can interface with a lang in a generic way
- a subtype is a situation where the compiler is entitled to add a type conversion in the type-checking process
b/c that type conversion doesn't correspond to a computation
-Sergei W.
*A neat idea for pattern matching optimization would be if you could match on one of several things in a list
ex:
if x {
is (comp, LHSPat, RHSPat) if comp in ["==, "<"] -> ...
}
- https://nshipster.com/never/
-https://cranelift.readthedocs.io/en/latest/?badge=latest<Paste>
-consult http://gluon-lang.org/book/embedding-api.html
- if/match playground
simple if
`if x == 1.0 { "a" } else { "b" }`
one comparison multiple targets:
`if x == { 1.0 -> "a", 2.0 -> "b", else -> "c" }`
different comparison operators/ method calls:
`if x { == 1.0 -> "a", eq NaN -> "n", .hella() -> "h", else -> "z" }`
pattern matching/introducing bindings:
`if alice { .age < 18 -> "18", is Person("Alice", age) -> "${age}", else -> "none" }`
pattern matching w/ if-let:
`if person is Person("Alice", age) { "${age}" } else { "nope" }`
-https://soc.github.io/languages/unified-condition-syntax syntax:
`if <cond-expr>" then <then-expr> else <else-expr>`
`if <half-expr> \n <rest-expr1> then <result1-expr> \n <rest-expr2> then <result-expr2> else <result3-expr>`
-and rest-exprs (or "targets") can have 'is' for pattern-matching, actually so can a full cond-expr
UNIFIED IF EXPRESSIONS FINAL WORK:
basic syntax:
`if_expr := if discriminator '{' (guard_expr)* '}'`
`guard_expr := pattern 'then' block_or_expr'`
`pattern := rhs | is_pattern`
`is_pattern := 'is' ???`
`rhs := expression | ???`
if the only two guard patterns are true and false, then the abbreviated syntax:
`'if' discriminator 'then' block_or_expr 'else' block_or_expr`
can replace `'if' discriminator '{' 'true' 'then' block_or_expr; 'false' 'then' block_or_expr '}'`
- Next priorities: - get ADTs working, get matches working
- inclusive/exclusive range syntax like .. vs ..=
- 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" }

View File

@@ -5,10 +5,10 @@ use std::collections::HashMap;
use self::llvm_sys::prelude::*;
use self::llvm_sys::{LLVMIntPredicate};
use maaru_lang::parser::{AST, Statement, Function, Prototype, Expression, BinOp};
use language::LLVMCodeString;
use parser::{AST, Statement, Function, Prototype, Expression, BinOp};
use schala_repl::LLVMCodeString;
use llvm_wrap as LLVMWrap;
use schala_repl::llvm_wrap as LLVMWrap;
type VariableMap = HashMap<String, LLVMValueRef>;

View File

@@ -2,13 +2,13 @@ extern crate take_mut;
use std::collections::HashMap;
use std::collections::VecDeque;
use maaru_lang::parser::{AST, Statement, Expression, Function, Callable, BinOp};
use parser::{AST, Statement, Expression, Function, Callable, BinOp};
use std::rc::Rc;
use std::io::{Write, Stdout, BufWriter};
use std::convert::From;
use maaru_lang::parser::Expression::*;
use maaru_lang::parser::Statement::*;
use parser::Expression::*;
use parser::Statement::*;
type Reduction<T> = (T, Option<SideEffect>);

View File

@@ -1,9 +1,24 @@
pub mod tokenizer;
pub mod parser;
pub mod eval;
pub mod compilation;
#![feature(box_patterns)]
use language::{ProgrammingLanguageInterface, EvalOptions, ReplOutput, TraceArtifact, LLVMCodeString};
extern crate schala_repl;
mod tokenizer;
mod parser;
mod eval;
mod compilation;
use schala_repl::{ProgrammingLanguageInterface, EvalOptions, UnfinishedComputation, FinishedComputation, 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;
@@ -27,42 +42,41 @@ impl<'a> ProgrammingLanguageInterface for Maaru<'a> {
format!("maaru")
}
fn evaluate_in_repl(&mut self, input: &str, options: &EvalOptions) -> ReplOutput {
let mut output = ReplOutput::default();
fn execute_pipeline(&mut self, input: &str, options: &EvalOptions) -> FinishedComputation {
let mut output = UnfinishedComputation::default();
let tokens = match tokenizer::tokenize(input) {
Ok(tokens) => {
if options.debug_tokens {
if let Some(_) = options.debug_passes.get("tokens") {
output.add_artifact(TraceArtifact::new("tokens", format!("{:?}", tokens)));
}
tokens
},
Err(err) => {
output.add_output(format!("Tokenization error: {:?}\n", err.msg));
return output;
return output.finish(Err(format!("Tokenization error: {:?}\n", err.msg)))
}
};
let ast = match parser::parse(&tokens, &[]) {
Ok(ast) => {
if options.debug_parse {
if let Some(_) = options.debug_passes.get("ast") {
output.add_artifact(TraceArtifact::new("ast", format!("{:?}", ast)));
}
ast
},
Err(err) => {
output.add_output(format!("Parse error: {:?}\n", err.msg));
return output;
return output.finish(Err(format!("Parse error: {:?}\n", err.msg)))
}
};
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;
output.finish(Ok(evaluation_output))
}
/* TODO make this work with new framework */
/*
fn can_compile(&self) -> bool {
true
}
@@ -85,4 +99,5 @@ impl<'a> ProgrammingLanguageInterface for Maaru<'a> {
};
compilation::compile_ast(ast)
}
*/
}

View File

@@ -1,5 +1,5 @@
use maaru_lang::tokenizer::{Token, Kw, OpTok};
use maaru_lang::tokenizer::Token::*;
use tokenizer::{Token, Kw, OpTok};
use tokenizer::Token::*;
use std::fmt;
use std::collections::VecDeque;

View File

@@ -5,7 +5,7 @@ use std::str::Chars;
use self::itertools::Itertools;
use std::rc::Rc;
use language::TokenError;
use TokenError;
#[derive(Debug, Clone, PartialEq)]
pub enum Token {

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" }

View File

@@ -1,6 +1,10 @@
use itertools::Itertools;
#![feature(box_patterns)]
use language::{ProgrammingLanguageInterface, EvalOptions, ReplOutput, TokenError};
extern crate itertools;
extern crate schala_repl;
use itertools::Itertools;
use schala_repl::{ProgrammingLanguageInterface, EvalOptions, FinishedComputation, UnfinishedComputation};
pub struct Robo {
}
@@ -11,6 +15,17 @@ impl 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 {
@@ -140,18 +155,16 @@ impl ProgrammingLanguageInterface for Robo {
format!("robo")
}
fn evaluate_in_repl(&mut self, input: &str, _eval_options: &EvalOptions) -> ReplOutput {
let mut output = ReplOutput::default();
fn execute_pipeline(&mut self, input: &str, _eval_options: &EvalOptions) -> FinishedComputation {
let output = UnfinishedComputation::default();
let tokens = match tokenize(input) {
Ok(tokens) => tokens,
Err(e) => {
output.add_output(format!("Tokenize error: {:?}", e));
return output;
return output.finish(Err(format!("Tokenize error: {:?}", e)));
}
};
output.add_output(format!("{:?}", tokens));
output
output.finish(Ok(format!("{:?}", tokens)))
}
}

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" }

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

@@ -0,0 +1,435 @@
#![feature(box_patterns)]
extern crate itertools;
extern crate schala_repl;
use itertools::Itertools;
use schala_repl::{ProgrammingLanguageInterface, EvalOptions, UnfinishedComputation, FinishedComputation};
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 execute_pipeline(&mut self, input: &str, _eval_options: &EvalOptions) -> FinishedComputation {
let output = UnfinishedComputation::default();
let sexps = match read(input) {
Err(err) => {
return output.finish(Err(format!("Error: {}", err)));
},
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.finish(Ok(output_str))
}
}
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]
syn = { version = "0.13.1", features = ["full", "extra-traits"] }
quote = "0.5"
schala-repl = { path = "../schala-repl" }
[lib]
proc-macro = true

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

@@ -0,0 +1,102 @@
#![feature(trace_macros)]
#![feature(proc_macro)]
extern crate proc_macro;
#[macro_use]
extern crate quote;
extern crate syn;
extern crate schala_repl;
use proc_macro::TokenStream;
use syn::{Ident, Attribute, DeriveInput};
fn extract_attribute_arg_by_name(name: &str, attrs: &Vec<Attribute>) -> Option<String> {
use syn::{Meta, Lit, MetaNameValue};
attrs.iter().map(|attr| attr.interpret_meta()).find(|meta| {
match meta {
&Some(Meta::NameValue(MetaNameValue { ident, .. })) if ident.as_ref() == name => true,
_ => false
}
}).and_then(|meta| {
match meta {
Some(Meta::NameValue(MetaNameValue { lit: Lit::Str(litstr), .. })) => Some(litstr.value()),
_ => None,
}
})
}
fn extract_attribute_list(name: &str, attrs: &Vec<Attribute>) -> Option<Vec<(Ident, Option<Vec<Ident>>)>> {
use syn::{Meta, MetaList, NestedMeta};
attrs.iter().find(|attr| {
match attr.path.segments.iter().nth(0) {
Some(segment) if segment.ident.as_ref() == name => true,
_ => false
}
}).and_then(|attr| {
match attr.interpret_meta() {
Some(Meta::List(MetaList { nested, .. })) => {
Some(nested.iter().map(|nested_meta| match nested_meta {
&NestedMeta::Meta(Meta::Word(ident)) => (ident, None),
&NestedMeta::Meta(Meta::List(MetaList { ident, nested: ref nested2, .. })) => {
let own_args = nested2.iter().map(|nested_meta2| match nested_meta2 {
&NestedMeta::Meta(Meta::Word(ident)) => ident,
_ => panic!("Bad format for doubly-nested attribute list")
}).collect();
(ident, Some(own_args))
},
_ => panic!("Bad format for nested list")
}).collect())
},
_ => panic!("{} must be a comma-delimited list surrounded by parens", name)
}
})
}
#[proc_macro_derive(ProgrammingLanguageInterface, attributes(LanguageName, SourceFileExtension, PipelineSteps))]
pub fn derive_programming_language_interface(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;
let attrs = &ast.attrs;
let language_name: String = extract_attribute_arg_by_name("LanguageName", attrs).expect("LanguageName is required");
let file_ext = extract_attribute_arg_by_name("SourceFileExtension", attrs).expect("SourceFileExtension is required");
let passes = extract_attribute_list("PipelineSteps", attrs).expect("PipelineSteps are required");
let pass_idents = passes.iter().map(|x| x.0);
//let pass_names: Vec<String> = passes.iter().map(|pass| pass.0.to_string()).collect();
let pass_descriptors = passes.iter().map(|pass| {
let name = pass.0.to_string();
let opts: Vec<String> = match &pass.1 {
None => vec![],
Some(opts) => opts.iter().map(|o| o.to_string()).collect(),
};
quote! {
PassDescriptor {
name: #name.to_string(),
debug_options: vec![#(format!(#opts)),*]
}
}
});
let tokens = quote! {
use schala_repl::PassDescriptor;
impl ProgrammingLanguageInterface for #name {
fn get_language_name(&self) -> String {
#language_name.to_string()
}
fn get_source_file_suffix(&self) -> String {
#file_ext.to_string()
}
fn execute_pipeline(&mut self, input: &str, options: &EvalOptions) -> FinishedComputation {
let mut chain = pass_chain![self, options; #(#pass_idents),* ];
chain(input)
}
fn get_passes(&self) -> Vec<PassDescriptor> {
vec![ #(#pass_descriptors),* ]
//vec![ #(PassDescriptor { name: #pass_names.to_string(), debug_options: vec![] }),* ]
}
}
};
tokens.into()
}

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" }

180
schala-lang/src/ast.rs Normal file
View File

@@ -0,0 +1,180 @@
use std::rc::Rc;
use builtin::{BinOp, PrefixOp};
#[derive(Debug, PartialEq)]
pub struct AST(pub Vec<Statement>);
#[derive(Debug, PartialEq, Clone)]
pub enum Statement {
ExpressionStatement(Expression),
Declaration(Declaration),
}
pub type Block = Vec<Statement>;
pub type ParamName = Rc<String>;
pub type InterfaceName = Rc<String>; //should be a singleton I think??
pub type FormalParam = (ParamName, Option<TypeName>);
#[derive(Debug, PartialEq, Clone)]
pub enum Declaration {
FuncSig(Signature),
FuncDecl(Signature, Block),
TypeDecl {
name: TypeSingletonName,
body: TypeBody,
mutable: bool
},
TypeAlias(Rc<String>, Rc<String>), //should have TypeSingletonName in it, or maybe just String, not sure
Binding {
name: Rc<String>,
constant: bool,
expr: Expression,
},
Impl {
type_name: TypeName,
interface_name: Option<InterfaceName>,
block: Vec<Declaration>,
},
Interface {
name: Rc<String>,
signatures: Vec<Signature>
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct Signature {
pub name: Rc<String>,
pub operator: bool,
pub params: Vec<FormalParam>,
pub type_anno: Option<TypeName>,
}
#[derive(Debug, PartialEq, Clone)]
pub struct TypeBody(pub Vec<Variant>);
#[derive(Debug, PartialEq, Clone)]
pub enum Variant {
UnitStruct(Rc<String>),
TupleStruct(Rc<String>, Vec<TypeName>),
Record(Rc<String>, Vec<(Rc<String>, TypeName)>),
}
#[derive(Debug, PartialEq, Clone)]
pub struct Expression(pub ExpressionType, pub Option<TypeName>);
#[derive(Debug, PartialEq, Clone)]
pub enum TypeName {
Tuple(Vec<TypeName>),
Singleton(TypeSingletonName)
}
#[derive(Debug, PartialEq, Clone)]
pub struct TypeSingletonName {
pub name: Rc<String>,
pub params: Vec<TypeName>,
}
#[derive(Debug, PartialEq, Clone)]
pub enum ExpressionType {
NatLiteral(u64),
FloatLiteral(f64),
StringLiteral(Rc<String>),
BoolLiteral(bool),
BinExp(BinOp, Box<Expression>, Box<Expression>),
PrefixExp(PrefixOp, Box<Expression>),
TupleLiteral(Vec<Expression>),
Value(Rc<String>),
NamedStruct {
name: Rc<String>,
fields: Vec<(Rc<String>, Expression)>,
},
Call {
f: Box<Expression>,
arguments: Vec<Expression>,
},
Index {
indexee: Box<Expression>,
indexers: Vec<Expression>,
},
IfExpression {
discriminator: Box<Discriminator>,
body: Box<IfExpressionBody>,
},
WhileExpression {
condition: Option<Box<Expression>>,
body: Block,
},
ForExpression {
enumerators: Vec<Enumerator>,
body: Box<ForBody>,
},
Lambda {
params: Vec<FormalParam>,
body: Block,
},
ListLiteral(Vec<Expression>),
}
#[derive(Debug, PartialEq, Clone)]
pub enum Discriminator {
Simple(Expression),
BinOp(Expression, BinOp)
}
#[derive(Debug, PartialEq, Clone)]
pub enum IfExpressionBody {
SimpleConditional(Block, Option<Block>),
SimplePatternMatch(Pattern, Block, Option<Block>),
GuardList(Vec<GuardArm>)
}
#[derive(Debug, PartialEq, Clone)]
pub struct GuardArm {
pub guard: Guard,
pub body: Block,
}
#[derive(Debug, PartialEq, Clone)]
pub enum Guard {
Pat(Pattern),
HalfExpr(HalfExpr)
}
#[derive(Debug, PartialEq, Clone)]
pub struct HalfExpr {
pub op: Option<BinOp>,
pub expr: ExpressionType,
}
#[derive(Debug, PartialEq, Clone)]
pub enum Pattern {
Ignored,
TuplePattern(Vec<Pattern>),
Literal(PatternLiteral),
TupleStruct(Rc<String>, Vec<Pattern>),
Record(Rc<String>, Vec<(Rc<String>, Pattern)>),
}
#[derive(Debug, PartialEq, Clone)]
pub enum PatternLiteral {
NumPattern {
neg: bool,
num: ExpressionType,
},
StringPattern(Rc<String>),
BoolPattern(bool),
VarPattern(Rc<String>)
}
#[derive(Debug, PartialEq, Clone)]
pub struct Enumerator {
pub id: Rc<String>,
pub generator: Expression,
}
#[derive(Debug, PartialEq, Clone)]
pub enum ForBody {
MonadicReturn(Expression),
StatementBlock(Block),
}

138
schala-lang/src/builtin.rs Normal file
View File

@@ -0,0 +1,138 @@
use std::rc::Rc;
use std::collections::HashMap;
use std::fmt;
use tokenizing::TokenType;
use self::BuiltinTypeSpecifier::*;
use self::BuiltinTConst::*;
#[derive(Debug, PartialEq, Clone)]
pub enum BuiltinTypeSpecifier {
Const(BuiltinTConst),
Func(Box<BuiltinTypeSpecifier>, Box<BuiltinTypeSpecifier>),
}
#[derive(Debug, PartialEq, Clone)]
pub enum BuiltinTConst {
Nat,
Int,
Float,
StringT,
Bool,
}
impl fmt::Display for BuiltinTypeSpecifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
#[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 from_sigil_token(tok: &TokenType) -> Option<BinOp> {
use self::TokenType::*;
let s = match tok {
Operator(op) => op,
Period => ".",
Pipe => "|",
Slash => "/",
LAngleBracket => "<",
RAngleBracket => ">",
_ => return None
};
Some(BinOp::from_sigil(s))
}
/*
pub fn get_type(&self) -> Result<Type, String> {
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_from_token(op: &TokenType) -> Option<i32> {
use self::TokenType::*;
let s = match op {
Operator(op) => op,
Period => ".",
Pipe => "|",
Slash => "/",
LAngleBracket => "<",
RAngleBracket => ">",
_ => return None
};
let default = 10_000_000;
Some(BINOPS.get(s).map(|x| x.2.clone()).unwrap_or_else(|| {
println!("Warning: operator {} not defined", s);
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) -> Result<Type, String> {
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, (BuiltinTypeSpecifier, ())> =
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, (BuiltinTypeSpecifier, (), i32)> =
hashmap! {
"+" => (Func(bx!(Const(Nat)), bx!(Func(bx!(Const(Nat)), bx!(Const(Nat))))), (), 10),
"-" => (Func(bx!(Const(Nat)), bx!(Func(bx!(Const(Nat)), bx!(Const(Nat))))), (), 10),
"*" => (Func(bx!(Const(Nat)), bx!(Func(bx!(Const(Nat)), bx!(Const(Nat))))), (), 20),
"/" => (Func(bx!(Const(Nat)), bx!(Func(bx!(Const(Nat)), bx!(Const(Float))))), (), 20),
"//" => (Func(bx!(Const(Nat)), bx!(Func(bx!(Const(Nat)), bx!(Const(Nat))))), (), 20), //TODO change this to `quot`
"%" => (Func(bx!(Const(Nat)), bx!(Func(bx!(Const(Nat)), bx!(Const(Nat))))), (), 20),
"++" => (Func(bx!(Const(StringT)), bx!(Func(bx!(Const(StringT)), bx!(Const(StringT))))), (), 30),
"^" => (Func(bx!(Const(Nat)), bx!(Func(bx!(Const(Nat)), bx!(Const(Nat))))), (), 20),
"&" => (Func(bx!(Const(Nat)), bx!(Func(bx!(Const(Nat)), bx!(Const(Nat))))), (), 20),
"|" => (Func(bx!(Const(Nat)), bx!(Func(bx!(Const(Nat)), bx!(Const(Nat))))), (), 20),
">" => (Func(bx!(Const(Nat)), bx!(Func(bx!(Const(Nat)), bx!(Const(Nat))))), (), 20),
">=" => (Func(bx!(Const(Nat)), bx!(Func(bx!(Const(Nat)), bx!(Const(Nat))))), (), 20),
"<" => (Func(bx!(Const(Nat)), bx!(Func(bx!(Const(Nat)), bx!(Const(Nat))))), (), 20),
"<=" => (Func(bx!(Const(Nat)), bx!(Func(bx!(Const(Nat)), bx!(Const(Nat))))), (), 20),
"==" => (Func(bx!(Const(Nat)), bx!(Func(bx!(Const(Nat)), bx!(Const(Nat))))), (), 20),
"=" => (Func(bx!(Const(Nat)), bx!(Func(bx!(Const(Nat)), bx!(Const(Nat))))), (), 20),
"<=>" => (Func(bx!(Const(Nat)), bx!(Func(bx!(Const(Nat)), bx!(Const(Nat))))), (), 20),
};
}

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

@@ -0,0 +1,482 @@
use std::cell::RefCell;
use std::rc::Rc;
use std::fmt::Write;
use std::io;
use itertools::Itertools;
use util::ScopeStack;
use reduced_ast::{ReducedAST, Stmt, Expr, Lit, Func};
use symbol_table::{SymbolSpec, Symbol, SymbolTable};
pub struct State<'a> {
values: ScopeStack<'a, Rc<String>, ValueEntry>,
symbol_table_handle: Rc<RefCell<SymbolTable>>,
}
macro_rules! builtin_binding {
($name:expr, $values:expr) => {
$values.insert(Rc::new(format!($name)), ValueEntry::Binding { constant: true, val: Node::Expr(Expr::Func(Func::BuiltIn(Rc::new(format!($name))))) });
}
}
//TODO add a more concise way of getting a new frame
impl<'a> State<'a> {
pub fn new(symbol_table_handle: Rc<RefCell<SymbolTable>>) -> State<'a> {
let mut values = ScopeStack::new(Some(format!("global")));
builtin_binding!("print", values);
builtin_binding!("println", values);
builtin_binding!("getline", values);
State { values, symbol_table_handle }
}
pub fn debug_print(&self) -> String {
format!("Values: {:?}", self.values)
}
}
#[derive(Debug, Clone)]
enum Node {
Expr(Expr),
PrimObject {
name: Rc<String>,
tag: usize,
items: Vec<Node>,
},
PrimTuple {
items: Vec<Node>
}
}
fn paren_wrapped_vec(terms: impl Iterator<Item=String>) -> String {
let mut buf = String::new();
write!(buf, "(").unwrap();
for term in terms.map(|e| Some(e)).intersperse(None) {
match term {
Some(e) => write!(buf, "{}", e).unwrap(),
None => write!(buf, ", ").unwrap(),
};
}
write!(buf, ")").unwrap();
buf
}
impl Node {
fn to_repl(&self) -> String {
match self {
Node::Expr(e) => e.to_repl(),
Node::PrimObject { name, items, .. } if items.len() == 0 => format!("{}", name),
Node::PrimObject { name, items, .. } => format!("{}{}", name, paren_wrapped_vec(items.iter().map(|x| x.to_repl()))),
Node::PrimTuple { items } => format!("{}", paren_wrapped_vec(items.iter().map(|x| x.to_repl()))),
}
}
}
#[derive(Debug)]
enum ValueEntry {
Binding {
constant: bool,
val: /*FullyEvaluatedExpr*/ Node, //TODO make this use a subtype to represent fully evaluatedness
}
}
type EvalResult<T> = Result<T, String>;
impl Expr {
fn to_node(self) -> Node {
Node::Expr(self)
}
fn to_repl(&self) -> String {
use self::Lit::*;
use self::Func::*;
match self {
Expr::Lit(ref l) => match l {
Nat(n) => format!("{}", n),
Int(i) => format!("{}", i),
Float(f) => format!("{}", f),
Bool(b) => format!("{}", b),
StringLit(s) => format!("\"{}\"", s),
},
Expr::Func(f) => match f {
BuiltIn(name) => format!("<built-in function '{}'>", name),
UserDefined { name: None, .. } => format!("<function>"),
UserDefined { name: Some(name), .. } => format!("<function '{}'>", name),
},
Expr::Constructor {
type_name: _, name, tag, arity,
} => if *arity == 0 {
format!("{}", name)
} else {
format!("<data constructor '{}'>", name)
},
Expr::Tuple(exprs) => paren_wrapped_vec(exprs.iter().map(|x| x.to_repl())),
_ => format!("{:?}", self),
}
}
}
impl<'a> State<'a> {
pub fn evaluate(&mut self, ast: ReducedAST, repl: bool) -> Vec<Result<String, String>> {
let mut acc = vec![];
// handle prebindings
for statement in ast.0.iter() {
self.prebinding(statement);
}
for statement in ast.0 {
match self.statement(statement) {
Ok(Some(ref output)) if repl => acc.push(Ok(output.to_repl())),
Ok(_) => (),
Err(error) => {
acc.push(Err(format!("Runtime error: {}", error)));
return acc;
},
}
}
acc
}
fn prebinding(&mut self, stmt: &Stmt) {
match stmt {
Stmt::PreBinding { name, func } => {
let v_entry = ValueEntry::Binding { constant: true, val: Node::Expr(Expr::Func(func.clone())) };
self.values.insert(name.clone(), v_entry);
},
Stmt::Expr(_expr) => {
//TODO have this support things like nested function defs
},
_ => ()
}
}
fn statement(&mut self, stmt: Stmt) -> EvalResult<Option<Node>> {
match stmt {
Stmt::Binding { name, constant, expr } => {
let val = self.expression(Node::Expr(expr))?;
self.values.insert(name.clone(), ValueEntry::Binding { constant, val });
Ok(None)
},
Stmt::Expr(expr) => Ok(Some(self.expression(expr.to_node())?)),
Stmt::PreBinding {..} | Stmt::Noop => Ok(None),
}
}
fn block(&mut self, stmts: Vec<Stmt>) -> EvalResult<Node> {
let mut ret = None;
for stmt in stmts {
ret = self.statement(stmt)?;
}
Ok(ret.unwrap_or(Node::Expr(Expr::Unit)))
}
fn expression(&mut self, node: Node) -> EvalResult<Node> {
use self::Expr::*;
match node {
t @ Node::PrimTuple { .. } => Ok(t),
obj @ Node::PrimObject { .. } => Ok(obj),
Node::Expr(expr) => match expr {
literal @ Lit(_) => Ok(Node::Expr(literal)),
Call { box f, args } => match self.expression(Node::Expr(f))? {
Node::Expr(Constructor { type_name, name, tag, arity }) => self.apply_data_constructor(type_name, name, tag, arity, args),
Node::Expr(Func(f)) => self.apply_function(f, args),
other => return Err(format!("Tried to call {:?} which is not a function or data constructor", other)),
},
Val(v) => self.value(v),
Constructor { arity, ref name, tag, .. } if arity == 0 => Ok(Node::PrimObject { name: name.clone(), tag, items: vec![] }),
constructor @ Constructor { .. } => Ok(Node::Expr(constructor)),
func @ Func(_) => Ok(Node::Expr(func)),
Tuple(exprs) => {
let nodes = exprs.into_iter().map(|expr| self.expression(Node::Expr(expr))).collect::<Result<Vec<Node>,_>>()?;
Ok(Node::PrimTuple { items: nodes })
},
Conditional { box cond, then_clause, else_clause } => self.conditional(cond, then_clause, else_clause),
Assign { box val, box expr } => {
let name = match val {
Expr::Val(name) => name,
_ => return Err(format!("Trying to assign to a non-value")),
};
let constant = match self.values.lookup(&name) {
None => return Err(format!("{} is undefined", name)),
Some(ValueEntry::Binding { constant, .. }) => constant.clone(),
};
if constant {
return Err(format!("trying to update {}, a non-mutable binding", name));
}
let val = self.expression(Node::Expr(expr))?;
self.values.insert(name.clone(), ValueEntry::Binding { constant: false, val });
Ok(Node::Expr(Expr::Unit))
},
Unit => Ok(Node::Expr(Unit)),
CaseMatch { box cond, alternatives } => match self.expression(Node::Expr(cond))? {
Node::PrimObject { name, tag, items } => {
for alt in alternatives {
if alt.tag.map(|t| t == tag).unwrap_or(true) {
let mut inner_state = State {
values: self.values.new_scope(None),
symbol_table_handle: self.symbol_table_handle.clone(),
};
for (bound_var, val) in alt.bound_vars.iter().zip(items.iter()) {
if let Some(bv) = bound_var.as_ref() {
inner_state.values.insert(bv.clone(), ValueEntry::Binding { constant: true, val: val.clone() });
}
}
return inner_state.block(alt.item)
}
}
return Err(format!("No matches found"));
},
Node::PrimTuple { .. } => Err(format!("Tuples don't work")),
Node::Expr(e) => Err(format!("Exprs don't work {:?}", e))
},
UnimplementedSigilValue => Err(format!("Sigil value eval not implemented"))
}
}
}
fn apply_data_constructor(&mut self, type_name: Rc<String>, name: Rc<String>, tag: usize, arity: usize, args: Vec<Expr>) -> EvalResult<Node> {
if arity != args.len() {
return Err(format!("Data constructor {} requires {} args", name, arity));
}
let evaled_args = args.into_iter().map(|expr| self.expression(Node::Expr(expr))).collect::<Result<Vec<Node>,_>>()?;
//let evaled_args = vec![];
Ok(Node::PrimObject {
name: name.clone(),
items: evaled_args,
tag
})
}
fn apply_function(&mut self, f: Func, args: Vec<Expr>) -> EvalResult<Node> {
match f {
Func::BuiltIn(sigil) => Ok(Node::Expr(self.apply_builtin(sigil, args)?)),
Func::UserDefined { params, body, name } => {
if params.len() != args.len() {
return Err(format!("calling a {}-argument function with {} args", params.len(), args.len()))
}
let mut func_state = State {
values: self.values.new_scope(name.map(|n| format!("{}", n))),
symbol_table_handle: self.symbol_table_handle.clone(),
};
for (param, val) in params.into_iter().zip(args.into_iter()) {
let val = func_state.expression(Node::Expr(val))?;
func_state.values.insert(param, ValueEntry::Binding { constant: true, val });
}
// TODO figure out function return semantics
func_state.block(body)
}
}
}
fn apply_builtin(&mut self, name: Rc<String>, args: Vec<Expr>) -> EvalResult<Expr> {
use self::Expr::*;
use self::Lit::*;
let evaled_args: Result<Vec<Expr>, String> = args.into_iter().map(|arg| {
match self.expression(Node::Expr(arg)) {
Ok(Node::Expr(e)) => Ok(e),
Ok(Node::PrimTuple { .. }) => Err(format!("Trying to apply a builtin to a tuple")),
Ok(Node::PrimObject { .. }) => Err(format!("Trying to apply a builtin to a primitive object")),
Err(e) => Err(e)
}
}).collect();
let evaled_args = evaled_args?;
Ok(match (name.as_str(), evaled_args.as_slice()) {
/* binops */
("+", &[Lit(Nat(l)), Lit(Nat(r))]) => Lit(Nat(l + r)),
("++", &[Lit(StringLit(ref s1)), Lit(StringLit(ref s2))]) => Lit(StringLit(Rc::new(format!("{}{}", s1, s2)))),
("-", &[Lit(Nat(l)), Lit(Nat(r))]) => Lit(Nat(l - r)),
("*", &[Lit(Nat(l)), Lit(Nat(r))]) => Lit(Nat(l * r)),
("/", &[Lit(Nat(l)), Lit(Nat(r))]) => Lit(Float((l as f64)/ (r as f64))),
("//", &[Lit(Nat(l)), Lit(Nat(r))]) => if r == 0 {
return Err(format!("divide by zero"));
} else {
Lit(Nat(l / r))
},
("%", &[Lit(Nat(l)), Lit(Nat(r))]) => Lit(Nat(l % r)),
("^", &[Lit(Nat(l)), Lit(Nat(r))]) => Lit(Nat(l ^ r)),
("&", &[Lit(Nat(l)), Lit(Nat(r))]) => Lit(Nat(l & r)),
("|", &[Lit(Nat(l)), Lit(Nat(r))]) => Lit(Nat(l | r)),
/* comparisons */
("==", &[Lit(Nat(l)), Lit(Nat(r))]) => Lit(Bool(l == r)),
("==", &[Lit(Int(l)), Lit(Int(r))]) => Lit(Bool(l == r)),
("==", &[Lit(Float(l)), Lit(Float(r))]) => Lit(Bool(l == r)),
("==", &[Lit(Bool(l)), Lit(Bool(r))]) => Lit(Bool(l == r)),
("==", &[Lit(StringLit(ref l)), Lit(StringLit(ref r))]) => Lit(Bool(l == r)),
("<", &[Lit(Nat(l)), Lit(Nat(r))]) => Lit(Bool(l < r)),
("<", &[Lit(Int(l)), Lit(Int(r))]) => Lit(Bool(l < r)),
("<", &[Lit(Float(l)), Lit(Float(r))]) => Lit(Bool(l < r)),
("<=", &[Lit(Nat(l)), Lit(Nat(r))]) => Lit(Bool(l <= r)),
("<=", &[Lit(Int(l)), Lit(Int(r))]) => Lit(Bool(l <= r)),
("<=", &[Lit(Float(l)), Lit(Float(r))]) => Lit(Bool(l <= r)),
(">", &[Lit(Nat(l)), Lit(Nat(r))]) => Lit(Bool(l > r)),
(">", &[Lit(Int(l)), Lit(Int(r))]) => Lit(Bool(l > r)),
(">", &[Lit(Float(l)), Lit(Float(r))]) => Lit(Bool(l > r)),
(">=", &[Lit(Nat(l)), Lit(Nat(r))]) => Lit(Bool(l >= r)),
(">=", &[Lit(Int(l)), Lit(Int(r))]) => Lit(Bool(l >= r)),
(">=", &[Lit(Float(l)), Lit(Float(r))]) => Lit(Bool(l >= r)),
/* prefix ops */
("!", &[Lit(Bool(true))]) => Lit(Bool(false)),
("!", &[Lit(Bool(false))]) => Lit(Bool(true)),
("-", &[Lit(Nat(n))]) => Lit(Int(-1*(n as i64))),
("-", &[Lit(Int(n))]) => Lit(Int(-1*(n as i64))),
("+", &[Lit(Int(n))]) => Lit(Int(n)),
("+", &[Lit(Nat(n))]) => Lit(Nat(n)),
/* builtin functions */
("print", &[ref anything]) => {
print!("{}", anything.to_repl());
Expr::Unit
},
("println", &[ref anything]) => {
println!("{}", anything.to_repl());
Expr::Unit
},
("getline", &[]) => {
let mut buf = String::new();
io::stdin().read_line(&mut buf).expect("Error readling line in 'getline'");
Lit(StringLit(Rc::new(buf.trim().to_string())))
},
(x, args) => return Err(format!("bad or unimplemented builtin {:?} | {:?}", x, args)),
})
}
fn conditional(&mut self, cond: Expr, then_clause: Vec<Stmt>, else_clause: Vec<Stmt>) -> EvalResult<Node> {
let cond = self.expression(Node::Expr(cond))?;
Ok(match cond {
Node::Expr(Expr::Lit(Lit::Bool(true))) => self.block(then_clause)?,
Node::Expr(Expr::Lit(Lit::Bool(false))) => self.block(else_clause)?,
_ => return Err(format!("Conditional with non-boolean condition"))
})
}
fn value(&mut self, name: Rc<String>) -> EvalResult<Node> {
use self::ValueEntry::*;
use self::Func::*;
//TODO add a layer of indirection here to talk to the symbol table first, and only then look up
//in the values table
let symbol_table = self.symbol_table_handle.borrow();
let value = symbol_table.lookup_by_name(&name);
Ok(match value {
Some(Symbol { name, spec }) => match spec {
//TODO I'll need this type_name later to do a table lookup
SymbolSpec::DataConstructor { type_name: _type_name, type_args, .. } => {
if type_args.len() == 0 {
Node::PrimObject { name: name.clone(), tag: 0, items: vec![] }
} else {
return Err(format!("This data constructor thing not done"))
}
},
SymbolSpec::Func(_) => match self.values.lookup(&name) {
Some(Binding { val: Node::Expr(Expr::Func(UserDefined { name, params, body })), .. }) => {
Node::Expr(Expr::Func(UserDefined { name: name.clone(), params: params.clone(), body: body.clone() }))
},
_ => unreachable!(),
},
},
/* see if it's an ordinary variable TODO make variables go in symbol table */
None => match self.values.lookup(&name) {
Some(Binding { val, .. }) => val.clone(),
None => return Err(format!("Couldn't find value {}", name)),
}
})
}
}
#[cfg(test)]
mod eval_tests {
use std::cell::RefCell;
use std::rc::Rc;
use symbol_table::SymbolTable;
use tokenizing::tokenize;
use parsing::parse;
use eval::State;
macro_rules! all_output {
($string:expr) => {
{
let symbol_table = Rc::new(RefCell::new(SymbolTable::new()));
let mut state = State::new(symbol_table);
let ast = parse(tokenize($string)).0.unwrap();
state.symbol_table_handle.borrow_mut().add_top_level_symbols(&ast).unwrap();
let reduced = ast.reduce(&state.symbol_table_handle.borrow());
let all_output = state.evaluate(reduced, true);
all_output
}
}
}
macro_rules! fresh_env {
($string:expr, $correct:expr) => {
{
let all_output = all_output!($string);
let ref output = all_output.last().unwrap();
assert_eq!(**output, Ok($correct.to_string()));
}
}
}
#[test]
fn test_basic_eval() {
fresh_env!("1 + 2", "3");
fresh_env!("let mut a = 1; a = 2", "Unit");
fresh_env!("let mut a = 1; a = 2; a", "2");
fresh_env!(r#"("a", 1 + 2)"#, r#"("a", 3)"#);
}
#[test]
fn function_eval() {
fresh_env!("fn oi(x) { x + 1 }; oi(4)", "5");
fresh_env!("fn oi(x) { x + 1 }; oi(1+2)", "4");
}
#[test]
fn scopes() {
let scope_ok = r#"
let a = 20
fn haha() {
let a = 10
a
}
haha()
"#;
fresh_env!(scope_ok, "10");
let scope_ok = r#"
let a = 20
fn haha() {
let a = 10
a
}
a
"#;
fresh_env!(scope_ok, "20");
}
#[test]
fn basic_patterns() {
let source = r#"
type Option<T> = Some(T) | None
let x = Some(9); if x is Some(q) then { q } else { 0 }"#;
fresh_env!(source, "9");
let source = r#"
type Option<T> = Some(T) | None
let x = None; if x is Some(q) then { q } else { 0 }"#;
fresh_env!(source, "0");
}
}

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

@@ -0,0 +1,148 @@
#![feature(trace_macros)]
#![feature(slice_patterns, box_patterns, box_syntax)]
extern crate itertools;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate maplit;
#[macro_use]
extern crate schala_repl;
#[macro_use]
extern crate schala_codegen;
use std::cell::RefCell;
use std::rc::Rc;
use itertools::Itertools;
use schala_repl::{ProgrammingLanguageInterface, EvalOptions, TraceArtifact, UnfinishedComputation, FinishedComputation};
macro_rules! bx {
($e:expr) => { Box::new($e) }
}
mod util;
mod builtin;
mod tokenizing;
mod ast;
mod parsing;
mod symbol_table;
mod typechecking;
mod reduced_ast;
mod eval;
//trace_macros!(true);
#[derive(ProgrammingLanguageInterface)]
#[LanguageName = "Schala"]
#[SourceFileExtension = "schala"]
#[PipelineSteps(tokenizing, parsing(compact,expanded,trace), symbol_table, typechecking, ast_reducing, eval)]
pub struct Schala {
state: eval::State<'static>,
symbol_table: Rc<RefCell<symbol_table::SymbolTable>>,
type_context: typechecking::TypeContext<'static>,
}
impl Schala {
fn new_blank_env() -> Schala {
let symbols = Rc::new(RefCell::new(symbol_table::SymbolTable::new()));
Schala {
symbol_table: symbols.clone(),
type_context: typechecking::TypeContext::new(symbols.clone()),
state: eval::State::new(symbols),
}
}
pub fn new() -> Schala {
let prelude = r#"
type Option<T> = Some(T) | None
type Color = Red | Green | Blue
type Ord = LT | EQ | GT
"#;
let mut s = Schala::new_blank_env();
s.execute_pipeline(prelude, &EvalOptions::default());
s
}
}
fn tokenizing(_handle: &mut Schala, input: &str, comp: Option<&mut UnfinishedComputation>) -> Result<Vec<tokenizing::Token>, String> {
let tokens = tokenizing::tokenize(input);
comp.map(|comp| {
let token_string = tokens.iter().map(|t| format!("{:?}<L:{},C:{}>", t.token_type, t.offset.0, t.offset.1)).join(", ");
comp.add_artifact(TraceArtifact::new("tokens", token_string));
});
let errors: Vec<String> = tokens.iter().filter_map(|t| t.get_error()).collect();
if errors.len() == 0 {
Ok(tokens)
} else {
Err(format!("{:?}", errors))
}
}
fn parsing(_handle: &mut Schala, input: Vec<tokenizing::Token>, comp: Option<&mut UnfinishedComputation>) -> Result<ast::AST, String> {
let (ast, trace) = parsing::parse(input);
comp.map(|comp| {
//TODO need to control which of these debug stages get added
let opt = comp.cur_debug_options.get(0).map(|s| s.clone());
match opt {
None => comp.add_artifact(TraceArtifact::new("ast", format!("{:?}", ast))),
Some(ref s) if s == "compact" => comp.add_artifact(TraceArtifact::new("ast", format!("{:?}", ast))),
Some(ref s) if s == "expanded" => comp.add_artifact(TraceArtifact::new("ast", format!("{:#?}", ast))),
Some(ref s) if s == "trace" => comp.add_artifact(TraceArtifact::new_parse_trace(trace)),
Some(ref x) => println!("Bad parsing debug option: {}", x),
};
});
ast.map_err(|err| err.msg)
}
fn symbol_table(handle: &mut Schala, input: ast::AST, comp: Option<&mut UnfinishedComputation>) -> Result<ast::AST, String> {
let add = handle.symbol_table.borrow_mut().add_top_level_symbols(&input);
match add {
Ok(()) => {
let artifact = TraceArtifact::new("symbol_table", handle.symbol_table.borrow().debug_symbol_table());
comp.map(|comp| comp.add_artifact(artifact));
Ok(input)
},
Err(msg) => Err(msg)
}
}
fn typechecking(handle: &mut Schala, input: ast::AST, comp: Option<&mut UnfinishedComputation>) -> Result<ast::AST, String> {
match handle.type_context.type_check_ast(&input) {
Ok(ty) => {
comp.map(|c| {
c.add_artifact(TraceArtifact::new("type_table", format!("{}", handle.type_context.debug_types())));
c.add_artifact(TraceArtifact::new("type_check", format!("{:?}", ty)));
});
Ok(input)
},
Err(msg) => {
comp.map(|comp| {
comp.add_artifact(TraceArtifact::new("type_table", format!("{}", handle.type_context.debug_types())));
comp.add_artifact(TraceArtifact::new("type_check", format!("Type error: {:?}", msg)));
});
Ok(input)
}
}
}
fn ast_reducing(handle: &mut Schala, input: ast::AST, comp: Option<&mut UnfinishedComputation>) -> Result<reduced_ast::ReducedAST, String> {
let ref symbol_table = handle.symbol_table.borrow();
let output = input.reduce(symbol_table);
comp.map(|comp| comp.add_artifact(TraceArtifact::new("ast_reducing", format!("{:?}", output))));
Ok(output)
}
fn eval(handle: &mut Schala, input: reduced_ast::ReducedAST, comp: Option<&mut UnfinishedComputation>) -> Result<String, String> {
comp.map(|comp| comp.add_artifact(TraceArtifact::new("value_state", handle.state.debug_print())));
let evaluation_outputs = handle.state.evaluate(input, true);
let text_output: Result<Vec<String>, String> = evaluation_outputs
.into_iter()
.collect();
let eval_output: Result<String, String> = text_output
.map(|v| { v.into_iter().intersperse(format!("\n")).collect() });
eval_output
}

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,282 @@
use std::rc::Rc;
use ast::{AST, Statement, Expression, Declaration, Discriminator, IfExpressionBody, Pattern, PatternLiteral, Guard, HalfExpr};
use symbol_table::{Symbol, SymbolSpec, SymbolTable};
use builtin::{BinOp, PrefixOp};
#[derive(Debug)]
pub struct ReducedAST(pub Vec<Stmt>);
#[derive(Debug, Clone)]
pub enum Stmt {
PreBinding {
name: Rc<String>,
func: Func,
},
Binding {
name: Rc<String>,
constant: bool,
expr: Expr,
},
Expr(Expr),
Noop,
}
#[derive(Debug, Clone)]
pub enum Expr {
Unit,
Lit(Lit),
Tuple(Vec<Expr>),
Func(Func),
Val(Rc<String>),
Constructor {
type_name: Rc<String>,
name: Rc<String>,
tag: usize,
arity: usize,
},
Call {
f: Box<Expr>,
args: Vec<Expr>,
},
Assign {
val: Box<Expr>,
expr: Box<Expr>,
},
Conditional {
cond: Box<Expr>,
then_clause: Vec<Stmt>,
else_clause: Vec<Stmt>,
},
CaseMatch {
cond: Box<Expr>,
alternatives: Vec<Alternative>
},
UnimplementedSigilValue
}
#[derive(Debug, Clone)]
pub struct Alternative {
pub tag: Option<usize>,
pub guards: Vec<Expr>,
pub bound_vars: Vec<Option<Rc<String>>>, //order here is iconic to order in a tuple-like type, None is equivalent to ignored
pub item: Vec<Stmt>,
}
#[derive(Debug, Clone)]
pub enum Lit {
Nat(u64),
Int(i64),
Float(f64),
Bool(bool),
StringLit(Rc<String>),
}
#[derive(Debug, Clone)]
pub enum Func {
BuiltIn(Rc<String>),
UserDefined {
name: Option<Rc<String>>,
params: Vec<Rc<String>>,
body: Vec<Stmt>,
}
}
impl AST {
pub fn reduce(&self, symbol_table: &SymbolTable) -> ReducedAST {
let mut output = vec![];
for statement in self.0.iter() {
output.push(statement.reduce(symbol_table));
}
ReducedAST(output)
}
}
impl Statement {
fn reduce(&self, symbol_table: &SymbolTable) -> Stmt {
use ast::Statement::*;
match self {
ExpressionStatement(expr) => Stmt::Expr(expr.reduce(symbol_table)),
Declaration(decl) => decl.reduce(symbol_table),
}
}
}
impl Expression {
fn reduce(&self, symbol_table: &SymbolTable) -> Expr {
use ast::ExpressionType::*;
let ref input = self.0;
match input {
NatLiteral(n) => Expr::Lit(Lit::Nat(*n)),
FloatLiteral(f) => Expr::Lit(Lit::Float(*f)),
StringLiteral(s) => Expr::Lit(Lit::StringLit(s.clone())),
BoolLiteral(b) => Expr::Lit(Lit::Bool(*b)),
BinExp(binop, lhs, rhs) => binop.reduce(symbol_table, lhs, rhs),
PrefixExp(op, arg) => op.reduce(symbol_table, arg),
Value(name) => match symbol_table.lookup_by_name(name) {
Some(Symbol { spec: SymbolSpec::DataConstructor { index, type_args, type_name}, .. }) => Expr::Constructor {
type_name: type_name.clone(),
name: name.clone(),
tag: index.clone(),
arity: type_args.len(),
},
_ => Expr::Val(name.clone()),
},
Call { f, arguments } => Expr::Call {
f: Box::new(f.reduce(symbol_table)),
args: arguments.iter().map(|arg| arg.reduce(symbol_table)).collect(),
},
TupleLiteral(exprs) => Expr::Tuple(exprs.iter().map(|e| e.reduce(symbol_table)).collect()),
IfExpression { discriminator, body } => reduce_if_expression(discriminator, body, symbol_table),
_ => Expr::UnimplementedSigilValue,
}
}
}
fn reduce_if_expression(discriminator: &Discriminator, body: &IfExpressionBody, symbol_table: &SymbolTable) -> Expr {
let cond = Box::new(match *discriminator {
Discriminator::Simple(ref expr) => expr.reduce(symbol_table),
Discriminator::BinOp(ref expr, ref binop) => {
panic!()
}
});
match *body {
IfExpressionBody::SimpleConditional(ref then_clause, ref else_clause) => {
let then_clause = then_clause.iter().map(|expr| expr.reduce(symbol_table)).collect();
let else_clause = match else_clause {
None => vec![],
Some(stmts) => stmts.iter().map(|expr| expr.reduce(symbol_table)).collect(),
};
Expr::Conditional { cond, then_clause, else_clause }
},
IfExpressionBody::SimplePatternMatch(ref pat, ref then_clause, ref else_clause) => {
let then_clause = then_clause.iter().map(|expr| expr.reduce(symbol_table)).collect();
let else_clause = match else_clause {
None => vec![],
Some(stmts) => stmts.iter().map(|expr| expr.reduce(symbol_table)).collect(),
};
let alternatives = vec![
pat.to_alternative(then_clause, symbol_table),
Alternative {
tag: None,
guards: vec![],
bound_vars: vec![],
item: else_clause,
},
];
Expr::CaseMatch {
cond,
alternatives,
}
},
IfExpressionBody::GuardList(ref guard_arms) => {
let alternatives = guard_arms.iter().map(|arm| match arm.guard {
Guard::Pat(ref p) => {
let item = arm.body.iter().map(|expr| expr.reduce(symbol_table)).collect();
p.to_alternative(item, symbol_table)
},
Guard::HalfExpr(HalfExpr { op: _, expr: _ }) => {
unimplemented!()
}
}).collect();
Expr::CaseMatch { cond, alternatives }
}
}
}
impl Pattern {
fn to_alternative(&self, item: Vec<Stmt>, symbol_table: &SymbolTable) -> Alternative {
use self::Pattern::*;
match self {
TupleStruct(name, subpatterns) => {
let symbol = symbol_table.values.get(name).expect(&format!("Symbol {} not found", name));
let tag = match symbol.spec {
SymbolSpec::DataConstructor { index, .. } => index.clone(),
_ => panic!("Bad symbol"),
};
/*
let guards = patterns.iter().map(|p| match p {
});
*/
let guards = unimplemented!();
let bound_vars = subpatterns.iter().map(|p| match p {
Literal(PatternLiteral::VarPattern(var)) => Some(var.clone()),
Ignored => None,
_ => None,
}).collect();
Alternative {
tag: Some(tag),
guards,
bound_vars,
item,
}
},
TuplePattern(_items) => {
unimplemented!()
},
Record(_name, _pairs) => {
unimplemented!()
},
Ignored => unimplemented!(),
/* "a constant appearing in a pattern can easily be eliminated by replacing it with a variable
* and adding a guard to the equation instead" - Implementation of Functional Programming
* Languages Simon Peyton-Jones, p. 58 */
Literal(lit) => match lit {
PatternLiteral::NumPattern { neg, num } => unimplemented!(),
PatternLiteral::StringPattern(_s) => unimplemented!(),
PatternLiteral::BoolPattern(_b) => unimplemented!(),
PatternLiteral::VarPattern(_var) => unimplemented!(),
},
}
}
}
impl Declaration {
fn reduce(&self, symbol_table: &SymbolTable) -> Stmt {
use self::Declaration::*;
use ::ast::Signature;
match self {
Binding {name, constant, expr } => Stmt::Binding { name: name.clone(), constant: *constant, expr: expr.reduce(symbol_table) },
FuncDecl(Signature { name, params, .. }, statements) => Stmt::PreBinding {
name: name.clone(),
func: Func::UserDefined {
name: Some(name.clone()),
params: params.iter().map(|param| param.0.clone()).collect(),
body: statements.iter().map(|stmt| stmt.reduce(symbol_table)).collect(),
}
},
TypeDecl { .. } => Stmt::Noop,
TypeAlias(_, _) => Stmt::Noop,
Interface { .. } => Stmt::Noop,
Impl { .. } => Stmt::Expr(Expr::UnimplementedSigilValue),
_ => Stmt::Expr(Expr::UnimplementedSigilValue)
}
}
}
impl BinOp {
fn reduce(&self, symbol_table: &SymbolTable, lhs: &Box<Expression>, rhs: &Box<Expression>) -> Expr {
if **self.sigil() == "=" {
Expr::Assign {
val: Box::new(lhs.reduce(symbol_table)),
expr: Box::new(rhs.reduce(symbol_table)),
}
} else {
let f = Box::new(Expr::Func(Func::BuiltIn(self.sigil().clone())));
Expr::Call { f, args: vec![lhs.reduce(symbol_table), rhs.reduce(symbol_table)]}
}
}
}
impl PrefixOp {
fn reduce(&self, symbol_table: &SymbolTable, arg: &Box<Expression>) -> Expr {
let f = Box::new(Expr::Func(Func::BuiltIn(self.sigil().clone())));
Expr::Call { f, args: vec![arg.reduce(symbol_table)]}
}
}

View File

@@ -0,0 +1,131 @@
use std::collections::HashMap;
use std::rc::Rc;
use std::fmt;
use std::fmt::Write;
use ast;
use typechecking::TypeName;
//cf. p. 150 or so of Language Implementation Patterns
pub struct SymbolTable {
pub values: HashMap<Rc<String>, Symbol> //TODO this will eventually have real type information
}
//TODO add various types of lookups here, maybe multiple hash tables internally? also make values
//non-public
impl SymbolTable {
pub fn new() -> SymbolTable {
SymbolTable { values: HashMap::new() }
}
pub fn lookup_by_name(&self, name: &Rc<String>) -> Option<&Symbol> {
self.values.get(name)
}
}
#[derive(Debug)]
pub struct Symbol {
pub name: Rc<String>,
pub spec: SymbolSpec,
}
impl fmt::Display for Symbol {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<Name: {}, Spec: {}>", self.name, self.spec)
}
}
#[derive(Debug)]
pub enum SymbolSpec {
Func(Vec<TypeName>),
DataConstructor {
index: usize,
type_name: Rc<String>,
type_args: Vec<Rc<String>>,
},
}
impl fmt::Display for SymbolSpec {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::SymbolSpec::*;
match self {
Func(type_names) => write!(f, "Func({:?})", type_names),
DataConstructor { index, type_name, type_args } => write!(f, "DataConstructor({})({:?} -> {})", index, type_args, type_name),
}
}
}
impl SymbolTable {
/* note: this adds names for *forward reference* but doesn't actually create any types. solve that problem
* later */
pub fn add_top_level_symbols(&mut self, ast: &ast::AST) -> Result<(), String> {
use self::ast::{Statement, TypeName, Variant, TypeSingletonName, TypeBody};
use self::ast::Declaration::*;
for statement in ast.0.iter() {
if let Statement::Declaration(decl) = statement {
match decl {
FuncSig(signature) | FuncDecl(signature, _) => {
let mut ch: char = 'a';
let mut types = vec![];
for param in signature.params.iter() {
match param {
(_, Some(_ty)) => {
//TODO eventually handle this case different
types.push(Rc::new(format!("{}", ch)));
ch = ((ch as u8) + 1) as char;
},
(_, None) => {
types.push(Rc::new(format!("{}", ch)));
ch = ((ch as u8) + 1) as char;
}
}
}
let spec = SymbolSpec::Func(types);
self.values.insert(
signature.name.clone(),
Symbol { name: signature.name.clone(), spec }
);
},
//TODO figure out why _params isn't being used here
TypeDecl { name: TypeSingletonName { name, params: _params}, body: TypeBody(variants), mutable: _mutable, } => {
for (index, var) in variants.iter().enumerate() {
match var {
Variant::UnitStruct(variant_name) => {
let spec = SymbolSpec::DataConstructor {
index,
type_name: name.clone(),
type_args: vec![],
};
self.values.insert(variant_name.clone(), Symbol { name: variant_name.clone(), spec });
},
Variant::TupleStruct(variant_name, tuple_members) => {
let type_args = tuple_members.iter().map(|type_name| match type_name {
TypeName::Singleton(TypeSingletonName { name, ..}) => name.clone(),
TypeName::Tuple(_) => unimplemented!(),
}).collect();
let spec = SymbolSpec::DataConstructor {
index,
type_name: name.clone(),
type_args
};
let symbol = Symbol { name: variant_name.clone(), spec };
self.values.insert(variant_name.clone(), symbol);
},
e => return Err(format!("{:?} not supported in typing yet", e)),
}
}
},
_ => ()
}
}
}
Ok(())
}
pub fn debug_symbol_table(&self) -> String {
let mut output = format!("Symbol table\n");
for (name, sym) in &self.values {
write!(output, "{} -> {}\n", name, sym).unwrap();
}
output
}
}

View File

@@ -0,0 +1,315 @@
use itertools::Itertools;
use std::collections::HashMap;
use std::rc::Rc;
use std::iter::{Iterator, Peekable};
use std::fmt;
#[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, Then, Else,
Is,
Func,
For, While,
Const, Let, In,
Mut,
Return,
Alias, Type, SelfType, SelfIdent,
Interface, Impl,
True, False,
Module
}
lazy_static! {
static ref KEYWORDS: HashMap<&'static str, Kw> =
hashmap! {
"if" => Kw::If,
"then" => Kw::Then,
"else" => Kw::Else,
"is" => Kw::Is,
"fn" => Kw::Func,
"for" => Kw::For,
"while" => Kw::While,
"const" => Kw::Const,
"let" => Kw::Let,
"in" => Kw::In,
"mut" => Kw::Mut,
"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.clone()),
_ => 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 CharData = (usize, usize, char);
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(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>) -> 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(input: &mut Peekable<impl Iterator<Item=CharData>>) -> 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(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>) -> 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(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>) -> 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,493 @@
use std::cell::RefCell;
use std::rc::Rc;
use std::collections::HashMap;
use std::fmt;
use std::fmt::Write;
/*
use std::collections::hash_set::Union;
use std::iter::Iterator;
use itertools::Itertools;
*/
use ast;
use util::ScopeStack;
use symbol_table::{SymbolSpec, SymbolTable};
pub type TypeName = Rc<String>;
type TypeResult<T> = Result<T, String>;
#[derive(Debug, PartialEq, Clone)]
enum Type {
Const(TConst),
Var(TypeName),
Func(Vec<Type>),
}
#[derive(Debug, PartialEq, Clone)]
enum TConst {
Unit,
Nat,
StringT,
//Custom(String)
}
#[derive(Debug, PartialEq, Clone)]
struct Scheme {
names: Vec<TypeName>,
ty: Type,
}
impl fmt::Display for Scheme {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "∀{:?} . {:?}", self.names, self.ty)
}
}
#[derive(Debug, PartialEq, Clone)]
struct Substitution(HashMap<TypeName, Type>);
impl Substitution {
fn empty() -> Substitution {
Substitution(HashMap::new())
}
}
#[derive(Debug, PartialEq, Clone)]
struct TypeEnv(HashMap<TypeName, Scheme>);
impl TypeEnv {
fn default() -> TypeEnv {
TypeEnv(HashMap::new())
}
fn populate_from_symbols(&mut self, symbol_table: &SymbolTable) {
for (name, symbol) in symbol_table.values.iter() {
if let SymbolSpec::Func(ref type_names) = symbol.spec {
let mut ch: char = 'a';
let mut names = vec![];
for _ in type_names.iter() {
names.push(Rc::new(format!("{}", ch)));
ch = ((ch as u8) + 1) as char;
}
let sigma = Scheme {
names: names.clone(),
ty: Type::Func(names.into_iter().map(|n| Type::Var(n)).collect())
};
self.0.insert(name.clone(), sigma);
}
}
}
}
pub struct TypeContext<'a> {
values: ScopeStack<'a, TypeName, Type>,
symbol_table_handle: Rc<RefCell<SymbolTable>>,
global_env: TypeEnv
}
impl<'a> TypeContext<'a> {
pub fn new(symbol_table_handle: Rc<RefCell<SymbolTable>>) -> TypeContext<'static> {
TypeContext { values: ScopeStack::new(None), global_env: TypeEnv::default(), symbol_table_handle }
}
pub fn debug_types(&self) -> String {
let mut output = format!("Type environment\n");
for (name, scheme) in &self.global_env.0 {
write!(output, "{} -> {}\n", name, scheme).unwrap();
}
output
}
pub fn type_check_ast(&mut self, input: &ast::AST) -> Result<String, String> {
let ref symbol_table = self.symbol_table_handle.borrow();
self.global_env.populate_from_symbols(symbol_table);
let output = self.global_env.infer_block(&input.0)?;
Ok(format!("{:?}", output))
}
}
impl TypeEnv {
fn instantiate(&mut self, sigma: Scheme) -> Type {
match sigma {
Scheme { ty, .. } => ty,
}
}
fn generate(&mut self, ty: Type) -> Scheme {
Scheme {
names: vec![], //TODO incomplete
ty
}
}
fn infer_block(&mut self, block: &Vec<ast::Statement>) -> TypeResult<Type> {
let mut output = Type::Const(TConst::Unit);
for statement in block {
output = self.infer_statement(statement)?;
}
Ok(output)
}
fn infer_statement(&mut self, statement: &ast::Statement) -> TypeResult<Type> {
match statement {
ast::Statement::ExpressionStatement(expr) => self.infer_expr(expr),
ast::Statement::Declaration(decl) => self.infer_decl(decl)
}
}
fn infer_decl(&mut self, decl: &ast::Declaration) -> TypeResult<Type> {
use ast::Declaration::*;
match decl {
Binding { name, expr, .. } => {
let ty = self.infer_expr(expr)?;
let sigma = self.generate(ty);
self.0.insert(name.clone(), sigma);
},
_ => (),
}
Ok(Type::Const(TConst::Unit))
}
fn infer_expr(&mut self, expr: &ast::Expression) -> TypeResult<Type> {
match expr {
ast::Expression(expr, Some(anno)) => {
self.infer_exprtype(expr)
},
ast::Expression(expr, None) => {
self.infer_exprtype(expr)
}
}
}
fn infer_exprtype(&mut self, expr: &ast::ExpressionType) -> TypeResult<Type> {
use self::TConst::*;
use ast::ExpressionType::*;
Ok(match expr {
NatLiteral(_) => Type::Const(Nat),
StringLiteral(_) => Type::Const(StringT),
BinExp(op, lhs, rhs) => {
return Err(format!("NOTDONE"))
},
Call { f, arguments } => {
return Err(format!("NOTDONE"))
},
Value(name) => {
let s = match self.0.get(name) {
Some(sigma) => sigma.clone(),
None => return Err(format!("Unknown variable: {}", name))
};
self.instantiate(s)
},
_ => Type::Const(Unit)
})
}
}
/* GIANT TODO - use the rust im crate, unless I make this code way less haskell-ish after it's done
*/
/*
pub type TypeResult<T> = Result<T, String>;
*/
/* TODO this should just check the name against a map, and that map should be pre-populated with
* types */
/*
impl parsing::TypeName {
fn to_type(&self) -> TypeResult<Type> {
use self::parsing::TypeSingletonName;
use self::parsing::TypeName::*;
use self::Type::*; use self::TConstOld::*;
Ok(match self {
Tuple(_) => return Err(format!("Tuples not yet implemented")),
Singleton(name) => match name {
TypeSingletonName { name, .. } => match &name[..] {
/*
"Nat" => Const(Nat),
"Int" => Const(Int),
"Float" => Const(Float),
"Bool" => Const(Bool),
"String" => Const(StringT),
*/
n => Const(Custom(n.to_string()))
}
}
})
}
}
*/
/*
impl TypeContext {
pub fn type_check_ast(&mut self, ast: &parsing::AST) -> TypeResult<String> {
let ref block = ast.0;
let mut infer = Infer::default();
let env = TypeEnvironment::default();
let output = infer.infer_block(block, &env);
match output {
Ok(s) => Ok(format!("{:?}", s)),
Err(s) => Err(format!("Error: {:?}", s))
}
}
}
// this is the equivalent of the Haskell Infer monad
#[derive(Debug, Default)]
struct Infer {
_idents: u32,
}
#[derive(Debug)]
enum InferError {
CannotUnify(MonoType, MonoType),
OccursCheckFailed(Rc<String>, MonoType),
UnknownIdentifier(Rc<String>),
Custom(String),
}
type InferResult<T> = Result<T, InferError>;
impl Infer {
fn fresh(&mut self) -> MonoType {
let i = self._idents;
self._idents += 1;
let name = Rc::new(format!("{}", ('a' as u8 + 1) as char));
MonoType::Var(name)
}
fn unify(&mut self, a: MonoType, b: MonoType) -> InferResult<Substitution> {
use self::InferError::*; use self::MonoType::*;
Ok(match (a, b) {
(Const(ref a), Const(ref b)) if a == b => Substitution::new(),
(Var(ref name), ref var) => Substitution::bind_variable(name, var),
(ref var, Var(ref name)) => Substitution::bind_variable(name, var),
(Function(box a1, box b1), Function(box a2, box b2)) => {
let s1 = self.unify(a1, a2)?;
let s2 = self.unify(b1.apply_substitution(&s1), b2.apply_substitution(&s1))?;
s1.merge(s2)
},
(a, b) => return Err(CannotUnify(a, b))
})
}
fn infer_block(&mut self, block: &Vec<parsing::Statement>, env: &TypeEnvironment) -> InferResult<MonoType> {
use self::parsing::Statement;
let mut ret = MonoType::Const(TypeConst::Unit);
for statement in block.iter() {
ret = match statement {
Statement::ExpressionStatement(expr) => {
let (sub, ty) = self.infer_expr(expr, env)?;
//TODO handle substitution monadically
ty
}
Statement::Declaration(decl) => MonoType::Const(TypeConst::Unit),
}
}
Ok(ret)
}
fn infer_expr(&mut self, expr: &parsing::Expression, env: &TypeEnvironment) -> InferResult<(Substitution, MonoType)> {
use self::parsing::Expression;
match expr {
Expression(e, Some(anno)) => self.infer_annotated_expr(e, anno, env),
/*
let anno_ty = anno.to_type()?;
let ty = self.infer_exprtype(&e)?;
self.unify(ty, anno_ty)
},
*/
Expression(e, None) => self.infer_exprtype(e, env)
}
}
fn infer_annotated_expr(&mut self, expr: &parsing::ExpressionType, anno: &parsing::TypeName, env: &TypeEnvironment) -> InferResult<(Substitution, MonoType)> {
Err(InferError::Custom(format!("exprtype not done: {:?}", expr)))
}
fn infer_exprtype(&mut self, expr: &parsing::ExpressionType, env: &TypeEnvironment) -> InferResult<(Substitution, MonoType)> {
use self::parsing::ExpressionType::*;
use self::TypeConst::*;
Ok(match expr {
NatLiteral(_) => (Substitution::new(), MonoType::Const(Nat)),
FloatLiteral(_) => (Substitution::new(), MonoType::Const(Float)),
StringLiteral(_) => (Substitution::new(), MonoType::Const(StringT)),
BoolLiteral(_) => (Substitution::new(), MonoType::Const(Bool)),
Value(name) => match env.lookup(name) {
Some(sigma) => {
let tau = self.instantiate(&sigma);
(Substitution::new(), tau)
},
None => return Err(InferError::UnknownIdentifier(name.clone())),
},
e => return Err(InferError::Custom(format!("Type inference for {:?} not done", e)))
})
}
fn instantiate(&mut self, sigma: &PolyType) -> MonoType {
let ref ty: MonoType = sigma.1;
let mut subst = Substitution::new();
for name in sigma.0.iter() {
let fresh_mvar = self.fresh();
let new = Substitution::bind_variable(name, &fresh_mvar);
subst = subst.merge(new);
}
ty.apply_substitution(&subst)
}
}
*/
/* OLD STUFF DOWN HERE */
/*
impl TypeContext {
fn infer_block(&mut self, statements: &Vec<parsing::Statement>) -> TypeResult<Type> {
let mut ret_type = Type::Const(TConst::Unit);
for statement in statements {
ret_type = self.infer_statement(statement)?;
}
Ok(ret_type)
}
fn infer_statement(&mut self, statement: &parsing::Statement) -> TypeResult<Type> {
use self::parsing::Statement::*;
match statement {
ExpressionStatement(expr) => self.infer(expr),
Declaration(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 { name, 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(e, Some(anno)) => {
let anno_ty = anno.to_type()?;
let ty = self.infer_exprtype(&e)?;
self.unify(ty, anno_ty)
},
Expression(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 {
NatLiteral(_) => Ok(Const(Nat)),
FloatLiteral(_) => Ok(Const(Float)),
StringLiteral(_) => Ok(Const(StringT)),
BoolLiteral(_) => Ok(Const(Bool)),
BinExp(op, lhs, 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(op, 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(name) => {
match self.bindings.get(name) {
Some(ty) => Ok(ty.clone()),
None => Err(format!("No binding found for variable: {}", name)),
}
},
Call { f, 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(expressions) => {
let mut types = vec![];
for expr in expressions {
types.push(self.infer(expr)?);
}
Ok(Sum(types))
},
_ => 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))
}
}
}
*/
#[cfg(test)]
mod tests {
/*
use super::{Type, TConst, TypeContext};
use super::Type::*;
use super::TConst::*;
use std::rc::Rc;
use std::cell::RefCell;
macro_rules! type_test {
($input:expr, $correct:expr) => {
{
let symbol_table = Rc::new(RefCell::new(SymbolTable::new()));
let mut tc = TypeContext::new(symbol_table);
let ast = ::ast::parse(::tokenizing::tokenize($input)).0.unwrap() ;
//tc.add_symbols(&ast);
assert_eq!($correct, tc.infer_block(&ast.0).unwrap())
}
}
}
#[test]
fn basic_inference() {
type_test!("30", Const(Nat));
//type_test!("fn x(a: Int): Bool {}; x(1)", TConst(Boolean));
}
*/
}

42
schala-lang/src/util.rs Normal file
View File

@@ -0,0 +1,42 @@
use std::collections::HashMap;
use std::hash::Hash;
use std::cmp::Eq;
#[derive(Default, Debug)]
pub struct ScopeStack<'a, T: 'a, V: 'a> where T: Hash + Eq {
parent: Option<&'a ScopeStack<'a, T, V>>,
values: HashMap<T, V>,
scope_name: Option<String>
}
impl<'a, T, V> ScopeStack<'a, T, V> where T: Hash + Eq {
pub fn new(name: Option<String>) -> ScopeStack<'a, T, V> where T: Hash + Eq {
ScopeStack {
parent: None,
values: HashMap::new(),
scope_name: name
}
}
pub fn insert(&mut self, key: T, value: V) where T: Hash + Eq {
self.values.insert(key, value);
}
pub fn lookup(&self, key: &T) -> Option<&V> where T: Hash + Eq {
match (self.values.get(key), self.parent) {
(None, None) => None,
(None, Some(parent)) => parent.lookup(key),
(Some(value), _) => Some(value),
}
}
//TODO rename new_scope
pub fn new_scope(&'a self, name: Option<String>) -> ScopeStack<'a, T, V> where T: Hash + Eq {
ScopeStack {
parent: Some(self),
values: HashMap::default(),
scope_name: name,
}
}
pub fn get_name(&self) -> Option<&String> {
self.scope_name.as_ref()
}
}

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

@@ -0,0 +1,26 @@
[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.13"
rocket_codegen = "0.3.13"
rocket_contrib = "0.3.13"
phf = "0.7.12"
includedir = "0.2.0"
linefeed = "0.5.0"
regex = "0.2"
[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();
}

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

@@ -0,0 +1,215 @@
use std::collections::HashMap;
use colored::*;
use std::fmt::Write;
use std::time;
pub struct LLVMCodeString(pub String);
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct EvalOptions {
pub execution_method: ExecutionMethod,
pub debug_passes: HashMap<String, PassDebugOptionsDescriptor>,
}
#[derive(Debug, Hash, PartialEq)]
pub struct PassDescriptor {
pub name: String,
pub debug_options: Vec<String>
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PassDebugOptionsDescriptor {
pub opts: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum ExecutionMethod {
Compile,
Interpret,
}
impl Default for ExecutionMethod {
fn default() -> ExecutionMethod {
ExecutionMethod::Interpret
}
}
#[derive(Debug, Default)]
pub struct UnfinishedComputation {
artifacts: Vec<(String, TraceArtifact)>,
pub durations: Vec<time::Duration>,
pub cur_debug_options: Vec<String>,
}
#[derive(Debug)]
pub struct FinishedComputation {
artifacts: Vec<(String, TraceArtifact)>,
durations: Vec<time::Duration>,
text_output: Result<String, String>,
}
impl UnfinishedComputation {
pub fn add_artifact(&mut self, artifact: TraceArtifact) {
self.artifacts.push((artifact.stage_name.clone(), artifact));
}
pub fn finish(self, text_output: Result<String, String>) -> FinishedComputation {
FinishedComputation {
artifacts: self.artifacts,
text_output,
durations: self.durations
}
}
pub fn output(self, output: Result<String, String>) -> FinishedComputation {
FinishedComputation {
artifacts: self.artifacts,
text_output: output,
durations: self.durations,
}
}
}
impl FinishedComputation {
pub fn to_repl(&self) -> String {
let mut buf = String::new();
for (stage, artifact) in self.artifacts.iter() {
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();
}
let debug_timing = true;
if debug_timing {
write!(&mut buf, "Timing: ").unwrap();
for duration in self.durations.iter() {
let timing = (duration.as_secs() as f64) + (duration.subsec_nanos() as f64 * 1e-9);
write!(&mut buf, "{}s, ", timing).unwrap()
}
write!(&mut buf, "\n").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, artifact) in self.artifacts.iter() {
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",
"ast_reducing" => "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 {
fn execute_pipeline(&mut self, _input: &str, _eval_options: &EvalOptions) -> FinishedComputation {
FinishedComputation { artifacts: vec![], text_output: Err(format!("Execution pipeline not done")), durations: vec![] }
}
fn get_language_name(&self) -> String;
fn get_source_file_suffix(&self) -> String;
fn get_passes(&self) -> Vec<PassDescriptor> {
vec![]
}
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 <<")
}
}
/* a pass_chain function signature looks like:
* fn(&mut ProgrammingLanguageInterface, A, Option<&mut DebugHandler>) -> Result<B, String>
*
* TODO use some kind of failure-handling library to make this better
*/
#[macro_export]
macro_rules! pass_chain {
($state:expr, $options:expr; $($pass:path), *) => {
|text_input| {
let mut comp = UnfinishedComputation::default();
pass_chain_helper! { ($state, comp, $options); text_input $(, $pass)* }
}
};
}
#[macro_export]
macro_rules! pass_chain_helper {
(($state:expr, $comp:expr, $options:expr); $input:expr, $pass:path $(, $rest:path)*) => {
{
use std::time;
use schala_repl::PassDebugOptionsDescriptor;
let pass_name = stringify!($pass);
let (output, duration) = {
let ref debug_map = $options.debug_passes;
let debug_handle = match debug_map.get(pass_name) {
Some(PassDebugOptionsDescriptor { opts }) => {
let ptr = &mut $comp;
ptr.cur_debug_options = opts.clone();
Some(ptr)
}
_ => None
};
let start = time::Instant::now();
let pass_output = $pass($state, $input, debug_handle);
let elapsed = start.elapsed();
(pass_output, elapsed)
};
$comp.durations.push(duration);
match output {
Ok(result) => pass_chain_helper! { ($state, $comp, $options); result $(, $rest)* },
Err(err) => {
$comp.output(Err(format!("Pass {} failed with {:?}", pass_name, err)))
}
}
}
};
// Done
(($state:expr, $comp:expr, $options:expr); $final_output:expr) => {
{
let final_output: FinishedComputation = $comp.finish(Ok($final_output));
final_output
}
};
}

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

@@ -0,0 +1,561 @@
#![feature(link_args)]
#![feature(slice_patterns, box_patterns, box_syntax)]
#![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate getopts;
extern crate linefeed;
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 colored::*;
use itertools::Itertools;
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, FinishedComputation, UnfinishedComputation, PassDebugOptionsDescriptor, PassDescriptor};
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();
let debug_passes = if let Some(opts) = option_matches.opt_str("debug") {
let output: Vec<String> = opts.split_terminator(",").map(|s| s.to_string()).collect();
output
} else {
vec![]
};
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, debug_passes);
}
};
}
fn run_noninteractive(filename: &str, languages: Vec<Box<ProgrammingLanguageInterface>>, mut options: EvalOptions, debug_passes: Vec<String>) {
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();
for pass in debug_passes.into_iter() {
if let Some(_) = language.get_passes().iter().find(|desc| desc.name == pass) {
options.debug_passes.insert(pass, PassDebugOptionsDescriptor { opts: vec![] });
}
}
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_pipeline(&buffer, &options);
output.to_noninteractive().map(|text| println!("{}", text));
}
}
}
#[derive(Clone)]
enum CommandTree {
Terminal(String, Option<String>),
NonTerminal(String, Vec<CommandTree>, Option<String>),
Top(Vec<CommandTree>),
}
impl CommandTree {
fn term(s: &str, help: Option<&str>) -> CommandTree {
CommandTree::Terminal(s.to_string(), help.map(|x| x.to_string()))
}
fn get_cmd(&self) -> String {
match self {
CommandTree::Terminal(s, _) => s.to_string(),
CommandTree::NonTerminal(s, _, _) => s.to_string(),
CommandTree::Top(_) => "".to_string(),
}
}
fn get_help(&self) -> String {
match self {
CommandTree::Terminal(_, h) => h.as_ref().map(|h| h.clone()).unwrap_or(format!("")),
CommandTree::NonTerminal(_, _, h) => h.as_ref().map(|h| h.clone()).unwrap_or(format!("")),
CommandTree::Top(_) => "".to_string(),
}
}
fn get_children(&self) -> Vec<String> {
match self {
CommandTree::Terminal(_, _) => vec![],
CommandTree::NonTerminal(_, children, _) => children.iter().map(|x| x.get_cmd()).collect(),
CommandTree::Top(children) => children.iter().map(|x| x.get_cmd()).collect(),
}
}
}
struct TabCompleteHandler {
sigil: char,
top_level_commands: CommandTree,
}
use linefeed::complete::{Completion, Completer};
use linefeed::terminal::Terminal;
impl TabCompleteHandler {
fn new(sigil: char, top_level_commands: CommandTree) -> TabCompleteHandler {
TabCompleteHandler {
top_level_commands,
sigil,
}
}
}
impl<T: Terminal> Completer<T> for TabCompleteHandler {
fn complete(&self, word: &str, prompter: &linefeed::prompter::Prompter<T>, start: usize, _end: usize) -> Option<Vec<Completion>> {
let line = prompter.buffer();
if line.starts_with(&format!("{}", self.sigil)) {
let mut words = line[1..(if start == 0 { 1 } else { start })].split_whitespace();
let mut completions = Vec::new();
let mut command_tree: Option<&CommandTree> = Some(&self.top_level_commands);
loop {
match words.next() {
None => {
let top = match command_tree {
Some(CommandTree::Top(_)) => true,
_ => false
};
let word = if top { word.get(1..).unwrap() } else { word };
for cmd in command_tree.map(|x| x.get_children()).unwrap_or(vec![]).into_iter() {
if cmd.starts_with(word) {
completions.push(Completion {
completion: format!("{}{}", if top { ":" } else { "" }, cmd),
display: Some(cmd.clone()),
suffix: linefeed::complete::Suffix::Some(' ')
})
}
}
break;
},
Some(s) => {
let new_ptr: Option<&CommandTree> = command_tree.and_then(|cm| match cm {
CommandTree::Top(children) => children.iter().find(|c| c.get_cmd() == s),
CommandTree::NonTerminal(_, children, _) => children.iter().find(|c| c.get_cmd() == s),
CommandTree::Terminal(_, _) => None,
});
command_tree = new_ptr;
}
}
}
Some(completions)
} else {
None
}
}
}
struct Repl {
options: EvalOptions,
languages: Vec<Box<ProgrammingLanguageInterface>>,
current_language_index: usize,
interpreter_directive_sigil: char,
line_reader: linefeed::interface::Interface<linefeed::terminal::DefaultTerminal>,
}
impl Repl {
fn new(languages: Vec<Box<ProgrammingLanguageInterface>>, initial_index: usize) -> Repl {
use linefeed::Interface;
let i = if initial_index < languages.len() { initial_index } else { 0 };
let line_reader = Interface::new("schala-repl").unwrap();
Repl {
options: Repl::get_options(),
languages: languages,
current_language_index: i,
interpreter_directive_sigil: ':',
line_reader
}
}
fn get_cur_language(&self) -> &ProgrammingLanguageInterface {
self.languages[self.current_language_index].as_ref()
}
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) {
use linefeed::ReadResult;
println!("Schala MetaInterpreter version {}", VERSION_STRING);
println!("Type {}help for help with the REPL", self.interpreter_directive_sigil);
self.line_reader.load_history(".schala_history").unwrap_or(());
loop {
let language_name = self.languages[self.current_language_index].get_language_name();
let directives = self.get_directives();
let tab_complete_handler = TabCompleteHandler::new(self.interpreter_directive_sigil, directives);
self.line_reader.set_completer(std::sync::Arc::new(tab_complete_handler));
let prompt_str = format!("{} >> ", language_name);
self.line_reader.set_prompt(&prompt_str);
match self.line_reader.read_line() {
Err(e) => {
println!("Terminal read error: {}", e);
},
Ok(ReadResult::Eof) => break,
Ok(ReadResult::Signal(_)) => break,
Ok(ReadResult::Input(ref input)) => {
self.line_reader.add_history_unique(input.to_string());
let output = match input.chars().nth(0) {
Some(ch) if ch == self.interpreter_directive_sigil => self.handle_interpreter_directive(input),
_ => Some(self.input_handler(input)),
};
if let Some(o) = output {
println!("=> {}", o);
}
}
}
}
self.line_reader.save_history(".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.execute_pipeline(input, &self.options);
interpreter_output.to_repl()
}
fn get_directives(&self) -> CommandTree {
let ref passes = self.get_cur_language().get_passes();
let passes_directives: Vec<CommandTree> = passes.iter()
.map(|pass_descriptor| {
let name = &pass_descriptor.name;
if pass_descriptor.debug_options.len() == 0 {
CommandTree::term(name, None)
} else {
let sub_opts: Vec<CommandTree> = pass_descriptor.debug_options.iter()
.map(|o| CommandTree::term(o, None)).collect();
CommandTree::NonTerminal(
name.clone(),
sub_opts,
None
)
}
}).collect();
CommandTree::Top(vec![
CommandTree::term("exit", Some("exit the REPL")),
CommandTree::term("quit", Some("exit the REPL")),
CommandTree::term("help", Some("Print this help message")),
CommandTree::NonTerminal(format!("debug"), vec![
CommandTree::term("passes", None),
CommandTree::NonTerminal(format!("show"), passes_directives.clone(), None),
CommandTree::NonTerminal(format!("hide"), passes_directives.clone(), None),
], Some(format!("show or hide pass info for a given pass, or display the names of all passes"))),
CommandTree::NonTerminal(format!("lang"), vec![
CommandTree::term("next", None),
CommandTree::term("prev", None),
CommandTree::NonTerminal(format!("go"), vec![], None)//TODO
], Some(format!("switch between languages, or go directly to a langauge by name"))),
])
}
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];
let directives = match self.get_directives() {
CommandTree::Top(children) => children,
_ => panic!("Top-level CommandTree not Top")
};
writeln!(buf, "MetaInterpreter options").unwrap();
writeln!(buf, "-----------------------").unwrap();
for directive in directives {
let trailer = " ";
writeln!(buf, "{}{}- {}", directive.get_cmd(), trailer, directive.get_help()).unwrap();
}
writeln!(buf, "").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)
},
"debug" => self.handle_debug(commands),
e => self.languages[self.current_language_index]
.handle_custom_interpreter_directives(&commands)
.or(Some(format!("Unknown command: {}", e)))
}
}
fn handle_debug(&mut self, commands: Vec<&str>) -> Option<String> {
let passes = self.get_cur_language().get_passes();
match commands.get(1) {
Some(&"passes") => Some(
passes.into_iter()
.map(|desc| {
if self.options.debug_passes.contains_key(&desc.name) {
let color = "green";
format!("*{}", desc.name.color(color))
} else {
desc.name
}
})
.intersperse(format!(" -> "))
.collect()),
b @ Some(&"show") | b @ Some(&"hide") => {
let show = b == Some(&"show");
let debug_pass: String = match commands.get(2) {
Some(s) => s.to_string(),
None => return Some(format!("Must specify a stage to debug")),
};
let pass_opt = commands.get(3);
if let Some(desc) = passes.iter().find(|desc| desc.name == debug_pass) {
let mut opts = vec![];
if let Some(opt) = pass_opt {
opts.push(opt.to_string());
}
let msg = format!("{} debug for pass {}", if show { "Enabling" } else { "Disabling" }, debug_pass);
if show {
self.options.debug_passes.insert(desc.name.clone(), PassDebugOptionsDescriptor { opts });
} else {
self.options.debug_passes.remove(&desc.name);
}
Some(msg)
} else {
Some(format!("Couldn't find stage: {}", debug_pass))
}
},
_ => Some(format!("Unknown debug command"))
}
}
}
/*
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
}

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.execute_pipeline(&input.source, &EvalOptions::default());
Json(Output { text: code_output.to_repl() })
}
pub fn web_main(language_generators: Vec<PLIGenerator>) {
rocket::ignite().manage(language_generators).mount("/", routes![index, js_bundle, interpreter_input]).launch();
}

View File

@@ -1,2 +0,0 @@
1 + 2

View File

@@ -0,0 +1,11 @@
let c = 10
fn add(a, b) {
let c = a + b
c
}
let mut b = 20
println(add(1,2))
println(c + b)

View File

@@ -0,0 +1,17 @@
fn main() {
let a = 10
let b = 20
a + b
}
//this is a one-line comment
/* this is
a multiline
comment
*/
print(main())

View File

@@ -0,0 +1,12 @@
for n <- 1..=100 {
if n % 15 == 0 {
print("FizzBuzz")
} else if n % 5 == 0 {
print("Buzz")
} else if n % 3 == 0 {
print("Fizz")
} else {
print(n.to_string())
}
}

View File

@@ -0,0 +1,114 @@
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/while loop topics */
//infinite loop
while {
if x() { break }
...
}
//conditional loop
while conditionHolds() {
...
}
//iteration over a variable
for i <- [1..1000] {
} //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>
/* end of for loops */
/* conditionals/pattern matching */
// "is" operator for "does this pattern match"
x is Some(t) // type bool
if x {
is Some(t) => {
},
is 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| }

View File

@@ -0,0 +1,17 @@
println(sua(4))
fn sua(x): Int {
x + 10
}
//let a = getline()
/*
if a == "true" {
println("You typed true")
} else {
println("You typed something else")
}
*/

3
source_files/test.rukka Normal file
View File

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

View File

@@ -1,98 +0,0 @@
fn main() {
# comments are scripting-style
#{ but can also be
}# blocks
@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 ?? maybe? not sure how this should work
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>)
}
}

View File

@@ -1,110 +0,0 @@
extern crate colored;
use self::colored::*;
#[derive(Debug)]
pub struct TokenError {
pub msg: String,
}
impl TokenError {
pub fn new(msg: &str) -> TokenError {
TokenError { msg: msg.to_string() }
}
}
pub struct LLVMCodeString(pub String);
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct EvalOptions {
pub debug_tokens: bool,
pub debug_parse: bool,
pub debug_type: bool,
pub debug_symbol_table: bool,
pub show_llvm_ir: bool,
pub trace_evaluation: bool,
pub compile: bool,
}
#[derive(Debug, Default)]
pub struct ReplOutput {
output: String,
artifacts: Vec<TraceArtifact>
}
impl ReplOutput {
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() {
println!("{}: {}", line.stage_name, line.debug_output);
}
println!("{}", self.output);
}
}
/*
//TODO I'll probably wanna implement this later
#[derive(Debug)]
pub struct CompilationOutput {
output: LLVMCodeString,
artifacts: Vec<TraceArtifact>,
}
*/
#[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 {
fn evaluate_in_repl(&mut self, input: &str, eval_options: &EvalOptions) -> ReplOutput;
fn get_language_name(&self) -> String;
fn get_source_file_suffix(&self) -> String;
fn compile(&mut self, _input: &str) -> LLVMCodeString {
LLVMCodeString("".to_string())
}
fn can_compile(&self) -> bool {
false
}
}

View File

@@ -1,364 +1,20 @@
#![feature(advanced_slice_patterns, slice_patterns, box_patterns, box_syntax)]
#![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate getopts;
extern crate linefeed;
extern crate itertools;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate maplit;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate rocket;
extern crate rocket_contrib;
extern crate schala_repl;
use std::path::Path;
use std::fs::File;
use std::io::{Read, Write};
use std::process::exit;
use std::default::Default;
extern crate maaru_lang;
extern crate rukka_lang;
extern crate robo_lang;
extern crate schala_lang;
use schala_repl::{PLIGenerator, repl_main};
mod schala_lang;
mod maaru_lang;
mod robo_lang;
mod language;
use language::{ProgrammingLanguageInterface, EvalOptions, LLVMCodeString};
mod webapp;
mod llvm_wrap;
extern { }
fn main() {
let languages: Vec<Box<ProgrammingLanguageInterface>> =
vec![
Box::new(schala_lang::Schala::new()),
Box::new(maaru_lang::Maaru::new()),
Box::new(robo_lang::Robo::new()),
];
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();
exit(0);
}
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 == lang }) })
.unwrap_or(0);
let mut options = EvalOptions::default();
options.compile = match option_matches.opt_str("eval-style") {
Some(ref s) if s == "compile" => true,
_ => false
};
match option_matches.free[..] {
[] | [_] => {
let mut repl = Repl::new(languages, initial_index);
repl.options.show_llvm_ir = true; //TODO make this be configurable
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();
if options.compile {
if !language.can_compile() {
panic!("Trying to compile a non-compileable language");
} else {
let llvm_bytecode = language.compile(&buffer);
compilation_sequence(llvm_bytecode, filename);
}
} else {
let interpretor_output = language.evaluate_in_repl(&buffer, &options);
interpretor_output.print_to_screen();
}
}
type LineReader = linefeed::Reader<linefeed::terminal::DefaultTerminal>;
struct Repl {
options: EvalOptions,
languages: Vec<Box<ProgrammingLanguageInterface>>,
current_language_index: usize,
interpreter_directive_sigil: char,
reader: LineReader,
}
impl Repl {
fn new(languages: Vec<Box<ProgrammingLanguageInterface>>, initial_index: usize) -> Repl {
let mut reader: linefeed::Reader<_> = linefeed::Reader::new("Metainterpreter").unwrap();
reader.set_prompt(">> ");
let i = if initial_index < languages.len() { initial_index } else { 0 };
Repl {
options: Repl::get_options(),
languages: languages,
current_language_index: i,
interpreter_directive_sigil: '.',
reader: reader,
}
}
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) {
use linefeed::ReadResult::*;
println!("MetaInterpreter v 0.05");
println!("Using language: {}", self.languages[self.current_language_index].get_language_name());
loop {
match self.reader.read_line() {
Err(e) => {
println!("Terminal read error: {}", e);
},
Ok(Eof) => {
break;
}
Ok(Input(ref input)) => {
self.reader.add_history(input.clone());
if self.handle_interpreter_directive(input) {
continue;
}
let output = self.input_handler(input);
println!("=> {}", output);
}
_ => (),
}
}
println!("Exiting...");
}
fn input_handler(&mut self, input: &str) -> String {
let ref mut language = self.languages[self.current_language_index];
let interpretor_output = language.evaluate_in_repl(input, &self.options);
interpretor_output.to_string()
}
fn handle_interpreter_directive(&mut self, input: &str) -> bool {
match input.chars().nth(0) {
Some(ch) if ch == self.interpreter_directive_sigil => (),
_ => return false
}
let mut iter = input.chars();
iter.next();
let trimmed_sigil: &str = iter.as_str();
let commands: Vec<&str> = trimmed_sigil
.split_whitespace()
.collect();
let cmd: &str = match commands.get(0).clone() {
None => return true,
Some(s) => s
};
match cmd {
"exit" | "quit" => {
self.save_options();
exit(0)
},
"history" => {
for item in self.reader.history() {
println!("{}", item);
}
},
"help" => {
println!("Commands:");
println!("exit | quit");
println!("lang [show|next|previous]");
println!("set [show|hide] [tokens|parse|symbols|eval|llvm]");
}
"lang" => {
match commands.get(1) {
Some(&"show") => {
for (i, lang) in self.languages.iter().enumerate() {
if i == self.current_language_index {
println!("* {}", lang.get_language_name());
} else {
println!("{}", lang.get_language_name());
}
}
},
Some(&"next") => {
self.current_language_index = (self.current_language_index + 1) % self.languages.len();
println!("Switching to {}", self.languages[self.current_language_index].get_language_name());
}
Some(&"prev") | Some(&"previous") => {
self.current_language_index = if self.current_language_index == 0 { self.languages.len() - 1 } else { self.current_language_index - 1 };
println!("Switching to {}", self.languages[self.current_language_index].get_language_name());
},
Some(e) => println!("Bad `lang` argument: {}", e),
None => println!("`lang` - valid arguments `show`, `next`, `prev`|`previous`"),
}
},
"set" => {
let show = match commands.get(1) {
Some(&"show") => true,
Some(&"hide") => false,
Some(e) => {
println!("Bad `set` argument: {}", e);
return true;
}
None => {
println!("`set` - valid arguments `show {{option}}`, `hide {{option}}`");
return true;
}
};
match commands.get(2) {
Some(&"tokens") => self.options.debug_tokens = show,
Some(&"parse") => self.options.debug_parse = show,
Some(&"symbols") => self.options.debug_symbol_table = show,
Some(&"eval") => {
//let ref mut language = self.languages[self.current_language_index];
//language.set_option("trace_evaluation", show);
},
Some(&"llvm") => self.options.show_llvm_ir = show,
Some(e) => {
println!("Bad `show`/`hide` argument: {}", e);
return true;
}
None => {
println!("`show`/`hide` requires an argument");
return true;
}
}
},
e => println!("Unknown command: {}", e)
}
return true;
}
}
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
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);
}

View File

@@ -1,109 +0,0 @@
use schala_lang::parsing::{AST, Statement, Declaration, Expression, ExpressionType, Operation};
pub struct ReplState {
}
type EvalResult<T> = Result<T, String>;
enum FullyEvaluatedExpr {
UnsignedInt(u64),
SignedInt(i64),
Float(f64),
Str(String),
Bool(bool),
}
impl ReplState {
pub fn new() -> ReplState {
ReplState { }
}
pub fn evaluate(&mut self, ast: AST) -> Vec<String> {
let mut acc = vec![];
for statement in ast.0 {
match self.eval_statement(statement) {
Ok(output) => {
if let Some(s) = output {
acc.push(s);
}
},
Err(error) => {
acc.push(format!("Error: {}", error));
return acc;
},
}
}
acc
}
}
impl ReplState {
fn eval_statement(&mut self, statement: Statement) -> EvalResult<Option<String>> {
use self::FullyEvaluatedExpr::*;
match statement {
Statement::ExpressionStatement(expr) => {
self.eval_expr(expr).map( |eval| {
match eval {
UnsignedInt(n) => Some(format!("{}", n)),
SignedInt(n) => Some(format!("{}", n)),
Float(f) => Some(format!("{}", f)),
Str(s) => Some(format!("\"{}\"", s)),
Bool(b) => Some(format!("{}", b)),
}
})
},
Statement::Declaration(decl) => {
self.eval_decl(decl).map(|_| None)
}
}
}
fn eval_decl(&mut self, _decl: Declaration) -> EvalResult<()> {
Err("Not implmemented".to_string())
}
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),
_ => Err(format!("Unimplemented")),
}
}
fn eval_binexp(&mut self, op: Operation, 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 opstr: &str = &op.0;
Ok(match (opstr, evaled_lhs, evaled_rhs) {
("+", 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),
("%", UnsignedInt(l), UnsignedInt(r)) => UnsignedInt(l % r),
_ => return Err(format!("Runtime error: not yet implemented")),
})
}
fn eval_prefix_exp(&mut self, op: Operation, expr: Box<Expression>) -> EvalResult<FullyEvaluatedExpr> {
use self::FullyEvaluatedExpr::*;
let evaled_expr = self.eval_expr(*expr)?;
let opstr: &str = &op.0;
Ok(match (opstr, evaled_expr) {
("!", Bool(true)) => Bool(false),
("!", Bool(false)) => Bool(true),
("-", UnsignedInt(n)) => SignedInt(-1*(n as i64)),
("-", SignedInt(n)) => SignedInt(-1*(n as i64)),
_ => return Err(format!("Runtime error: not yet implemented")),
})
}
}

View File

@@ -1,95 +0,0 @@
use itertools::Itertools;
use language::{ProgrammingLanguageInterface, EvalOptions, TraceArtifact, ReplOutput};
mod parsing;
mod type_check;
mod eval;
use self::type_check::{TypeContext};
pub struct Schala {
state: eval::ReplState,
type_context: TypeContext
}
impl Schala {
pub fn new() -> Schala {
Schala {
state: eval::ReplState::new(),
type_context: TypeContext::new(),
}
}
}
impl ProgrammingLanguageInterface for Schala {
fn get_language_name(&self) -> String {
"Schala".to_string()
}
fn get_source_file_suffix(&self) -> String {
format!("schala")
}
fn evaluate_in_repl(&mut self, input: &str, options: &EvalOptions) -> ReplOutput {
let mut output = ReplOutput::default();
let tokens = parsing::tokenize(input);
if options.debug_tokens {
let token_string = tokens.iter().map(|t| format!("{:?}<{}>", t.token_type, t.offset)).join(", ");
output.add_artifact(TraceArtifact::new("tokens", format!("{:?}", token_string)));
}
{
let token_errors: Vec<&String> = tokens.iter().filter_map(|t| t.get_error()).collect();
if token_errors.len() != 0 {
output.add_output(format!("Tokenization error: {:?}\n", token_errors));
return output;
}
}
let ast = match parsing::parse(tokens) {
(Ok(ast), trace) => {
if options.debug_parse {
output.add_artifact(TraceArtifact::new_parse_trace(trace));
output.add_artifact(TraceArtifact::new("ast", format!("{:?}", ast)));
}
ast
},
(Err(err), trace) => {
output.add_artifact(TraceArtifact::new_parse_trace(trace));
output.add_output(format!("Parse error: {:?}\n", err.msg));
return output;
}
};
self.type_context.add_symbols(&ast);
if options.debug_symbol_table {
let text = self.type_context.debug_symbol_table();
output.add_artifact(TraceArtifact::new("symbol_table", text));
}
match self.type_context.type_check(&ast) {
Ok(ty) => {
output.add_artifact(TraceArtifact::new("type_check", format!("type: {:?}", ty)));
},
Err(msg) => {
output.add_artifact(TraceArtifact::new("type_check", msg));
output.add_output(format!("Type error"));
return output;
}
}
let evaluation_output = self.state.evaluate(ast);
let mut acc = String::new();
let mut iter = evaluation_output.iter().peekable();
while let Some(s) = iter.next() {
acc.push_str(&s);
if let Some(_) = iter.peek() {
acc.push_str("\n");
}
}
output.add_output(acc);
return output;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,211 +0,0 @@
use std::collections::HashMap;
use std::rc::Rc;
use schala_lang::parsing::{AST, Statement, Declaration, Signature, Expression, ExpressionType, Operation, TypeName};
#[derive(Debug, PartialEq, Eq, Hash)]
struct PathSpecifier(Rc<String>);
#[derive(Debug, PartialEq, Clone)]
struct TypeContextEntry {
type_var: TypeVariable,
constant: bool
}
pub struct TypeContext {
symbol_table: HashMap<PathSpecifier, TypeContextEntry>,
existential_type_label_count: u64
}
impl TypeContext {
pub fn new() -> TypeContext {
TypeContext {
symbol_table: HashMap::new(),
existential_type_label_count: 0,
}
}
pub fn add_symbols(&mut self, ast: &AST) {
use self::Declaration::*;
for statement in ast.0.iter() {
match *statement {
Statement::ExpressionStatement(_) => (),
Statement::Declaration(ref decl) => {
match *decl {
FuncSig(_) => (),
Impl { .. } => (),
TypeDecl { .. } => (),
TypeAlias { .. } => (),
Binding {ref name, ref constant, ref expr} => {
let spec = PathSpecifier(name.clone());
let type_var = expr.1.as_ref()
.map(|ty| self.from_anno(ty))
.unwrap_or_else(|| { self.get_existential_type() });
let entry = TypeContextEntry { type_var, constant: *constant };
self.symbol_table.insert(spec, entry);
},
FuncDecl(ref signature, _) => {
let spec = PathSpecifier(signature.name.clone());
let type_var = self.from_signature(signature);
let entry = TypeContextEntry { type_var, 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 {:?}", self.symbol_table)
}
fn get_existential_type(&mut self) -> TypeVariable {
let ret = TypeVariable::Exist(self.existential_type_label_count);
self.existential_type_label_count += 1;
ret
}
fn from_anno(&mut self, anno: &TypeName) -> TypeVariable {
use self::TypeVariable::*;
use self::UVar::*;
match anno {
&TypeName::Singleton { ref name, .. } => {
match name.as_ref().as_ref() {
"Int" => Univ(Integer),
"Bool" => Univ(Boolean),
_ => self.get_existential_type()
}
},
_ => Univ(Bottom),
}
}
fn from_signature(&mut self, sig: &Signature) -> TypeVariable {
use self::TypeVariable::Univ;
use self::UVar::{Unit, Function};
let return_type = sig.type_anno.as_ref().map(|anno| self.from_anno(&anno)).unwrap_or_else(|| { self.get_existential_type() });
if sig.params.len() == 0 {
Univ(Function(Box::new(Univ(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(|| { self.get_existential_type() });
output_type = Univ(Function(Box::new(p_type), Box::new(output_type)));
}
output_type
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum TypeVariable {
Univ(UVar),
Exist(u64),
}
#[derive(Debug, PartialEq, Clone)]
pub enum UVar {
Integer,
Float,
Boolean,
Unit,
Function(Box<TypeVariable>, Box<TypeVariable>),
Bottom,
}
type TypeCheckResult = Result<TypeVariable, String>;
// 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 { ... }
*/
// from https://www.youtube.com/watch?v=il3gD7XMdmA
// typeInfer :: Expr a -> Matching (Type a)
// unify :: Type a -> Type b -> Matching (Type c)
impl TypeContext {
pub fn type_check(&mut self, ast: &AST) -> TypeCheckResult {
let mut last = TypeVariable::Univ(UVar::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 {
use self::ExpressionType::*;
use self::TypeVariable::*;
Ok(match (&expr.0, &expr.1) {
(ref _t, &Some(ref anno)) => {
//TODO make this better,
self.from_anno(anno)
},
(&IntLiteral(_), _) => Univ(UVar::Integer),
(&FloatLiteral(_), _) => Univ(UVar::Float),
(&BoolLiteral(_), _) => Univ(UVar::Boolean),
(&Variable(ref name), _) => self.lookup(name).map(|entry| entry.type_var)
.ok_or(format!("Couldn't find {}", name))?,
(&BinExp(ref op, box ref lhs, box ref rhs), _) => {
let _f_type = self.infer_op(op);
let _lhs_type = self.infer(&lhs);
let _rhs_type = self.infer(&rhs);
unimplemented!()
},
(&Call { ref f, ref arguments }, _) => {
let f_type = self.infer(&*f)?;
let arg_type = self.infer(arguments.get(0).unwrap())?; // TODO fix later
match f_type {
Univ(UVar::Function(box t1, box ret_type)) => {
let _ = self.unify(&t1, &arg_type)?;
ret_type
},
_ => return Err(format!("Type error"))
}
},
_ => Univ(UVar::Unit),
})
}
fn infer_op(&mut self, _op: &Operation) -> TypeCheckResult {
use self::TypeVariable::*;
Ok(
Univ(UVar::Function(
Box::new(Univ(UVar::Integer)),
Box::new(Univ(UVar::Function(
Box::new(Univ(UVar::Integer)),
Box::new(Univ(UVar::Integer))
)))
))
)
}
fn unify(&mut self, t1: &TypeVariable, t2: &TypeVariable) -> TypeCheckResult {
if t1 == t2 {
Ok(t1.clone())
} else {
Err(format!("Types {:?} and {:?} don't unify", t1, t2))
}
}
}

View File

@@ -1,36 +0,0 @@
use rocket;
use rocket::response::NamedFile;
use rocket_contrib::Json;
use schala_lang;
use language::{ProgrammingLanguageInterface, EvalOptions};
#[get("/")]
fn index() -> Result<NamedFile, ()> {
NamedFile::open("static/index.html").map_err(|_| ())
}
#[get("/bundle.js")]
fn js_bundle() -> Result<NamedFile, ()> {
NamedFile::open("static/bundle.js").map_err(|_| ())
}
#[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>) -> Json<Output> {
let mut schala = schala_lang::Schala::new();
let code_output = schala.evaluate_in_repl(&input.source, &EvalOptions::default());
Json(Output { text: code_output.to_string() })
}
pub fn web_main() {
rocket::ignite().mount("/", routes![index, js_bundle, interpreter_input]).launch();
}

View File

@@ -1,6 +1,6 @@
const React = require("react");
const ReactDOM = require("react-dom");
const request = require("request");
const superagent = require("superagent");
const serverAddress = "http://localhost:8000";
@@ -20,14 +20,17 @@ class CodeArea extends React.Component {
console.log("Event", this.state.value);
const source = this.state.value;
const options = {
url: `${serverAddress}/input`,
json: true,
body: { source }
};
request.post(options, (error, response, body) => {
this.setState({lastOutput: body.text})
});
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() {

View File

@@ -11,7 +11,7 @@
"browserify": "^14.4.0",
"react": "^15.6.1",
"react-dom": "^15.6.1",
"request": "^2.82.0",
"superagent": "^3.6.3",
"uglify-js": "^3.1.1"
},
"babel": {

View File

@@ -13,15 +13,6 @@ acorn@^4.0.3:
version "4.0.13"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
ajv@^5.1.0:
version "5.2.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.2.2.tgz#47c68d69e86f5d953103b0074a9430dc63da5e39"
dependencies:
co "^4.6.0"
fast-deep-equal "^1.0.0"
json-schema-traverse "^0.3.0"
json-stable-stringify "^1.0.1"
ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
@@ -54,14 +45,6 @@ asn1.js@^4.0.0:
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
asn1@~0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86"
assert-plus@1.0.0, assert-plus@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
assert@^1.4.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91"
@@ -78,14 +61,6 @@ asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
aws-sign2@~0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
aws4@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
babel-code-frame@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
@@ -571,28 +546,10 @@ base64-js@^1.0.2:
version "1.2.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.1.tgz#a91947da1f4a516ea38e5b4ec0ec3773675e0886"
bcrypt-pbkdf@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d"
dependencies:
tweetnacl "^0.14.3"
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
version "4.11.8"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
boom@4.x.x:
version "4.3.1"
resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31"
dependencies:
hoek "4.x.x"
boom@5.x.x:
version "5.2.0"
resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02"
dependencies:
hoek "4.x.x"
brace-expansion@^1.1.7:
version "1.1.8"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
@@ -743,10 +700,6 @@ cached-path-relative@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.1.tgz#d09c4b52800aa4c078e2dd81a869aac90d2e54e7"
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
@@ -764,10 +717,6 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
inherits "^2.0.1"
safe-buffer "^5.0.1"
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
combine-source-map@~0.7.1:
version "0.7.2"
resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.7.2.tgz#0870312856b307a87cc4ac486f3a9a62aeccc09e"
@@ -777,7 +726,7 @@ combine-source-map@~0.7.1:
lodash.memoize "~3.0.3"
source-map "~0.5.3"
combined-stream@^1.0.5, combined-stream@~1.0.5:
combined-stream@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009"
dependencies:
@@ -787,6 +736,10 @@ commander@~2.11.0:
version "2.11.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563"
component-emitter@^1.2.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@@ -817,6 +770,10 @@ convert-source-map@~1.1.0:
version "1.1.3"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860"
cookiejar@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.1.tgz#41ad57b1b555951ec171412a81942b1e8200d34a"
core-js@^1.0.0:
version "1.2.7"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
@@ -825,7 +782,7 @@ core-js@^2.4.0, core-js@^2.5.0:
version "2.5.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b"
core-util-is@1.0.2, core-util-is@~1.0.0:
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@@ -864,12 +821,6 @@ create-react-class@^15.6.0:
loose-envify "^1.3.1"
object-assign "^4.1.1"
cryptiles@3.x.x:
version "3.1.2"
resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe"
dependencies:
boom "5.x.x"
crypto-browserify@^3.0.0:
version "3.11.1"
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.1.tgz#948945efc6757a400d6e5e5af47194d10064279f"
@@ -885,12 +836,6 @@ crypto-browserify@^3.0.0:
public-encrypt "^4.0.0"
randombytes "^2.0.0"
dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
dependencies:
assert-plus "^1.0.0"
date-now@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
@@ -901,6 +846,12 @@ debug@^2.6.8:
dependencies:
ms "2.0.0"
debug@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
ms "2.0.0"
defined@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
@@ -956,12 +907,6 @@ duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2:
dependencies:
readable-stream "^2.0.2"
ecc-jsbn@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
dependencies:
jsbn "~0.1.0"
elliptic@^6.0.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df"
@@ -999,18 +944,10 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
md5.js "^1.3.4"
safe-buffer "^5.1.1"
extend@~3.0.1:
extend@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
extsprintf@1.3.0, extsprintf@^1.2.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
fast-deep-equal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff"
fbjs@^0.8.9:
version "0.8.15"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.15.tgz#4f0695fdfcc16c37c0b07facec8cb4c4091685b9"
@@ -1023,11 +960,7 @@ fbjs@^0.8.9:
setimmediate "^1.0.5"
ua-parser-js "^0.7.9"
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
form-data@~2.3.1:
form-data@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf"
dependencies:
@@ -1035,6 +968,10 @@ form-data@~2.3.1:
combined-stream "^1.0.5"
mime-types "^2.1.12"
formidable@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.1.1.tgz#96b8886f7c3c3508b932d6bd70c4d3a88f35f1a9"
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -1043,12 +980,6 @@ function-bind@^1.0.2:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
getpass@^0.1.1:
version "0.1.7"
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
dependencies:
assert-plus "^1.0.0"
glob@^7.1.0:
version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
@@ -1064,17 +995,6 @@ globals@^9.18.0:
version "9.18.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
har-schema@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
har-validator@~5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd"
dependencies:
ajv "^5.1.0"
har-schema "^2.0.0"
has-ansi@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
@@ -1107,15 +1027,6 @@ hash.js@^1.0.0, hash.js@^1.0.3:
inherits "^2.0.3"
minimalistic-assert "^1.0.0"
hawk@~6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038"
dependencies:
boom "4.x.x"
cryptiles "3.x.x"
hoek "4.x.x"
sntp "2.x.x"
hmac-drbg@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
@@ -1124,10 +1035,6 @@ hmac-drbg@^1.0.0:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
hoek@4.x.x:
version "4.2.0"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d"
home-or-tmp@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
@@ -1139,14 +1046,6 @@ htmlescape@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351"
http-signature@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
dependencies:
assert-plus "^1.0.0"
jsprim "^1.2.2"
sshpk "^1.7.0"
https-browserify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
@@ -1217,10 +1116,6 @@ is-stream@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
is-typedarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
isarray@~0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
@@ -1236,18 +1131,10 @@ isomorphic-fetch@^2.1.1:
node-fetch "^1.0.1"
whatwg-fetch ">=0.10.0"
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
js-tokens@^3.0.0, js-tokens@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
jsesc@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
@@ -1256,30 +1143,12 @@ jsesc@~0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
json-schema-traverse@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
json-schema@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
json-stable-stringify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af"
dependencies:
jsonify "~0.0.0"
json-stable-stringify@~0.0.0:
version "0.0.1"
resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz#611c23e814db375527df851193db59dd2af27f45"
dependencies:
jsonify "~0.0.0"
json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
json5@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
@@ -1292,15 +1161,6 @@ jsonparse@^1.2.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
jsprim@^1.2.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
dependencies:
assert-plus "1.0.0"
extsprintf "1.3.0"
json-schema "0.2.3"
verror "1.10.0"
labeled-stream-splicer@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz#a52e1d138024c00b86b1c0c91f677918b8ae0a59"
@@ -1336,6 +1196,10 @@ md5.js@^1.3.4:
hash-base "^3.0.0"
inherits "^2.0.1"
methods@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
miller-rabin@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.0.tgz#4a62fb1d42933c05583982f4c716f6fb9e6c6d3d"
@@ -1347,12 +1211,16 @@ mime-db@~1.30.0:
version "1.30.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01"
mime-types@^2.1.12, mime-types@~2.1.17:
mime-types@^2.1.12:
version "2.1.17"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a"
dependencies:
mime-db "~1.30.0"
mime@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6"
minimalistic-assert@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3"
@@ -1416,10 +1284,6 @@ number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
oauth-sign@~0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
object-assign@^4.0.0, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@@ -1488,10 +1352,6 @@ pbkdf2@^3.0.3:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
performance-now@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
private@^0.1.6, private@^0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1"
@@ -1531,11 +1391,11 @@ punycode@1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
punycode@^1.3.2, punycode@^1.4.1:
punycode@^1.3.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
qs@~6.5.1:
qs@^6.5.1:
version "6.5.1"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
@@ -1578,7 +1438,7 @@ read-only-stream@^2.0.0:
dependencies:
readable-stream "^2.0.2"
readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.6:
readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@^2.2.6:
version "2.3.3"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c"
dependencies:
@@ -1641,33 +1501,6 @@ repeating@^2.0.0:
dependencies:
is-finite "^1.0.0"
request@^2.82.0:
version "2.82.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.82.0.tgz#2ba8a92cd7ac45660ea2b10a53ae67cd247516ea"
dependencies:
aws-sign2 "~0.7.0"
aws4 "^1.6.0"
caseless "~0.12.0"
combined-stream "~1.0.5"
extend "~3.0.1"
forever-agent "~0.6.1"
form-data "~2.3.1"
har-validator "~5.0.3"
hawk "~6.0.2"
http-signature "~1.2.0"
is-typedarray "~1.0.0"
isstream "~0.1.2"
json-stringify-safe "~5.0.1"
mime-types "~2.1.17"
oauth-sign "~0.8.2"
performance-now "^2.1.0"
qs "~6.5.1"
safe-buffer "^5.1.1"
stringstream "~0.0.5"
tough-cookie "~2.3.2"
tunnel-agent "^0.6.0"
uuid "^3.1.0"
resolve@1.1.7:
version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
@@ -1719,12 +1552,6 @@ slash@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
sntp@2.x.x:
version "2.0.2"
resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.0.2.tgz#5064110f0af85f7cfdb7d6b67a40028ce52b4b2b"
dependencies:
hoek "4.x.x"
source-map-support@^0.4.15:
version "0.4.18"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f"
@@ -1735,20 +1562,6 @@ source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.3:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
sshpk@^1.7.0:
version "1.13.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3"
dependencies:
asn1 "~0.2.3"
assert-plus "^1.0.0"
dashdash "^1.12.0"
getpass "^0.1.1"
optionalDependencies:
bcrypt-pbkdf "^1.0.0"
ecc-jsbn "~0.1.1"
jsbn "~0.1.0"
tweetnacl "~0.14.0"
stream-browserify@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db"
@@ -1790,10 +1603,6 @@ string_decoder@~1.0.0, string_decoder@~1.0.3:
dependencies:
safe-buffer "~5.1.0"
stringstream@~0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878"
strip-ansi@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
@@ -1806,6 +1615,21 @@ subarg@^1.0.0:
dependencies:
minimist "^1.1.0"
superagent@^3.6.3:
version "3.6.3"
resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.6.3.tgz#eb95fcb576a9d23a730a9d0789731b5379a36cdc"
dependencies:
component-emitter "^1.2.0"
cookiejar "^2.1.0"
debug "^3.1.0"
extend "^3.0.0"
form-data "^2.3.1"
formidable "^1.1.1"
methods "^1.1.1"
mime "^1.4.1"
qs "^6.5.1"
readable-stream "^2.0.5"
supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
@@ -1841,12 +1665,6 @@ to-fast-properties@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
tough-cookie@~2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a"
dependencies:
punycode "^1.4.1"
trim-right@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
@@ -1855,16 +1673,6 @@ tty-browserify@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
dependencies:
safe-buffer "^5.0.1"
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
typedarray@~0.0.5:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
@@ -1901,18 +1709,6 @@ util@0.10.3, util@~0.10.1:
dependencies:
inherits "2.0.1"
uuid@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04"
verror@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
dependencies:
assert-plus "^1.0.0"
core-util-is "1.0.2"
extsprintf "^1.2.0"
vm-browserify@~0.0.1:
version "0.0.4"
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73"