115 Commits

Author SHA1 Message Date
greg
93e96acc66 Some initial work on a better repl 2019-02-10 01:00:40 -08:00
greg
be90a859db Try adding some libraries
Just playing around for now
2019-02-09 05:45:13 -08:00
greg
f3f1dcc0a4 Add some notes 2019-01-27 22:38:20 -08:00
greg
c0111e30bc SymbolTable: Add Record type 2019-01-25 00:57:01 -08:00
greg
c225e469ee Change Record variant representation 2019-01-24 20:47:20 -08:00
greg
1ce06bc0ef More symbol-table refactoring 2019-01-20 22:32:58 -08:00
greg
10c3a60515 Some symbol-table refactoring 2019-01-20 00:23:08 -08:00
greg
ff73ce7b36 Get rid of LLVM wrapper
Not using this code right now
2019-01-10 20:57:13 -08:00
greg
ede8a9076a Simplify some struct definitions 2019-01-10 20:57:13 -08:00
greg
a63dcf91b0 Replace // with quot 2019-01-10 20:57:13 -08:00
greg
479a098e0f Make note to fix parsing bug 2019-01-09 17:37:51 -08:00
greg
1085b528fe Don't care about case-sensitivity 2019-01-08 02:38:10 -08:00
greg
9b3b5c5541 Token offsets 2019-01-08 02:11:19 -08:00
greg
ab8e24a276 ParseError always has token now 2019-01-08 01:04:46 -08:00
greg
09e2d8579d Remove all ParseErrors that don't return the failed token 2019-01-08 01:00:40 -08:00
greg
ee7861cbd0 Fix how impl blocks work
This gets rid of a token-less parseerror
2019-01-08 00:51:56 -08:00
greg
b88def8a2e Make error msg better 2019-01-07 16:52:46 -08:00
greg
30676722a3 Transition to edition 2018 2019-01-07 13:00:37 -08:00
greg
801c90aaa7 Reorder parameters in pass functions 2019-01-07 02:43:31 -08:00
greg
02667b018c Kill most LLVM references
I'm probably going to refactor this so much, there's no point in keeping
this around
2019-01-07 02:38:15 -08:00
greg
1032c7c7a2 Add note 2019-01-07 01:59:12 -08:00
greg
fa295aab28 Show location of error in parse error 2019-01-07 01:52:54 -08:00
greg
a0f4abb9a3 Add SourceReference 2019-01-06 01:58:16 -08:00
greg
78b454fb32 Delete this line 2019-01-05 20:41:49 -08:00
greg
5491169d55 Refactor parsing structure
TokenHandler should contain all the methods for actually manipulating
tokens, Parser should only contain the recursive descent methods
2019-01-05 20:23:07 -08:00
greg
2b338fd3c9 Move .next() onto token_handler 2019-01-05 18:29:24 -08:00
greg
821f321261 More Node-wrapping of Expression 2019-01-05 18:11:51 -08:00
greg
846eeae04c More use of Token instead of TokenKind 2019-01-05 17:50:54 -08:00
greg
22f2750853 More use of Token in error messages 2019-01-05 17:40:05 -08:00
greg
f2f8ac7ee8 Make parserrors have token 2019-01-05 17:32:49 -08:00
greg
d0c5dce92b Use get_kind() 2019-01-05 17:28:35 -08:00
greg
8eda74c9a5 Starting to keep track of locations of errors in file 2019-01-05 17:18:10 -08:00
greg
2efac109ef Node for TupleLiteral 2019-01-05 16:06:55 -08:00
greg
215e2bbb0d PrefixOp have Node 2019-01-05 16:02:30 -08:00
greg
2590def3be More Node-wrapping 2019-01-05 15:54:03 -08:00
greg
879a7de83d Wrap Expression in Node 2019-01-05 15:47:44 -08:00
greg
4c2e0b8a21 Get rid of old code from old ideas 2019-01-05 15:35:51 -08:00
greg
282c42da3c Adding Node intermediate type to AST 2019-01-05 01:44:32 -08:00
greg
87e68988c8 Update some dependencies 2019-01-01 02:22:12 -08:00
greg
7ac97ca6e8 Kill ast_visitor 2018-11-26 01:57:44 -08:00
greg
26a8ff307f Add generic Node type 2018-11-20 03:21:10 -08:00
greg
6be208b51d Minor fix for parsing error messages 2018-11-20 03:03:08 -08:00
greg
e00948cad9 Add ast_visitor mod 2018-11-17 02:09:16 -08:00
greg
0af6fed505 Clear up some code a bit 2018-11-17 01:10:23 -08:00
greg
1f527f7949 Rename TokenType -> TokenKind 2018-11-16 23:17:34 -08:00
greg
8680c4faf6 Just some notes for myself about how to redesign the AST type 2018-11-16 15:53:27 -08:00
greg
b198984fc5 implement From for Expression-types 2018-11-16 14:06:04 -08:00
greg
58779f8470 Rename method, make sourcemap optional 2018-11-16 12:58:10 -08:00
greg
a0fa50392c Fix compile error 2018-11-16 12:46:29 -08:00
greg
d357876b16 WIP source map stuff 2018-11-16 04:12:07 -08:00
greg
e42f0c644c Introduce source map 2018-11-16 03:56:55 -08:00
greg
2ec7bf3b9a Some initial work on passing token metadata to AST 2018-11-16 03:51:03 -08:00
greg
5147e1a3eb Handle underscores in identifiers 2018-11-15 16:19:53 -08:00
greg
955c073174 Got typechecker unused errors down to one 2018-11-13 02:39:02 -08:00
greg
7c46a29141 Start adding doc comments 2018-11-11 18:04:44 -08:00
greg
0adc761e72 Kill an unimplemented!() 2018-11-11 02:48:51 -08:00
greg
b2039a7b67 Parameterize Type type over existential/universal 2018-11-10 16:33:42 -08:00
greg
b4c4531e4d Rename for more concision 2018-11-10 14:11:29 -08:00
greg
2d36ad44d6 Converting over types
WIP
2018-11-09 02:50:29 -08:00
greg
21132a369c Paramaterize Type 2018-11-09 02:05:59 -08:00
greg
ff0294c56e Typechecking shouldn't fail yet 2018-11-09 02:02:08 -08:00
greg
bc80c8f9ad Updated readme some 2018-11-09 01:51:25 -08:00
greg
e39356c0e5 Even more type work 2018-11-09 00:21:34 -08:00
greg
d44bb02d61 Even more types 2018-11-08 20:30:17 -08:00
greg
9056e9b0e1 More type work2 2018-11-08 02:29:54 -08:00
greg
e9b90412ce More type work 2018-11-08 02:12:01 -08:00
greg
65c47c20fc Change name of monad in which type inference happens 2018-11-07 17:01:07 -08:00
greg
fab3fb8ec2 More basic types + test 2018-11-07 16:39:32 -08:00
greg
0d5ccd21fe TConst 2018-11-07 15:39:40 -08:00
greg
69b7b9f528 Print out types to REPL 2018-11-07 13:44:28 -08:00
greg
9a09f40222 More typing work 2018-11-07 03:39:31 -08:00
greg
020819550b More typechecking infrastructure 2018-11-06 16:47:34 -08:00
greg
15f9dbe7a6 Typechecking infrastructure 2018-11-06 13:44:52 -08:00
greg
836bed1207 Added janky map to prelude 2018-11-06 03:02:32 -08:00
greg
cee5b085d5 Simpler syntax for single param in lambdas
This kind of implies that I might want -> for function types after all,
instead of :
2018-11-06 02:58:57 -08:00
greg
837a55c718 Test for nested function call 2018-11-06 02:42:28 -08:00
greg
f4f89b39b6 Handle nested function calls 2018-11-06 02:40:10 -08:00
greg
c6b4ed7ee4 Basic lambdas 2018-11-06 01:19:16 -08:00
greg
be425860af Starting on lambdas 2018-11-05 21:13:31 -08:00
greg
17e88b33f2 Eval test doesn't need to be a macro
Can be a fn
2018-11-05 21:07:06 -08:00
greg
47f7eb1ef6 Make prelude be separate file 2018-11-05 20:55:03 -08:00
greg
72d0cfe466 More macro test consolidation 2018-11-05 20:52:18 -08:00
greg
cea2f63b44 Use macros to make types more concise 2018-11-05 20:12:10 -08:00
greg
eec315dd58 Get rid of exprstatement! macro
For shorter exst! one
2018-11-05 19:58:55 -08:00
greg
1e9aa91c5d More concise test macros 2018-11-05 19:57:11 -08:00
greg
9813609ad7 Minor test refactoring 2018-11-05 19:17:53 -08:00
greg
5953d9d815 type annotations on lambdas 2018-11-05 19:10:34 -08:00
greg
a74e09c761 Change lambda syntax 2018-11-05 18:51:01 -08:00
greg
ad53d4394b Get rid of println 2018-11-05 14:52:51 -08:00
greg
151246e1c5 Test for pattern-matching 2018-11-05 14:11:49 -08:00
greg
77d2826918 Pattern-match on structured objects 2018-11-05 14:01:14 -08:00
greg
1bd48ed5db Fix problem with parsing commas
I should probably rethink how delimited block expressions like if-blocks
(and eventually for-blocks) work
2018-11-05 13:07:08 -08:00
greg
c394b81746 More pattern-matching 2018-11-05 04:02:04 -08:00
greg
ec29077247 More tuple-matching
Also discovered parser bug
2018-11-05 03:41:03 -08:00
greg
62043ac2d1 Starting on pattern-matching tuples
Lots of duplicated code here
2018-11-05 03:17:03 -08:00
greg
bada386979 More work on subpattern matching 2018-11-03 12:53:09 -07:00
greg
e71d404071 Finished this refactor 2018-11-02 19:54:04 -07:00
greg
cab4702bd6 Refactoring matching - WIP
doesn't work yet
2018-11-01 02:43:47 -07:00
greg
ec5a9d457e String patterns 2018-10-31 01:45:16 -07:00
greg
bfbc1580aa Make tag optional 2018-10-30 23:36:55 -07:00
greg
2d6c9010b9 More work here 2018-10-30 18:53:34 -07:00
greg
f4ff92302f Use subpattern abstraction 2018-10-30 18:46:06 -07:00
greg
e88ed97b06 Add subpattern struct 2018-10-30 18:39:25 -07:00
greg
b8df09e956 Change eval strategy to use conditional sigil 2018-10-29 01:50:43 -07:00
greg
d7f0147a4f Add conditional target placeholder expr 2018-10-28 12:45:45 -07:00
greg
f883512882 New abstraction layer in Schala-lang parser
Just for manipulating tokens
2018-10-21 16:33:21 -07:00
greg
37070a6b3e Move pass chain generation from macro to codegen 2018-10-20 18:00:05 -07:00
greg
ffe7deb00a Starting to move pass_chain logic into codegen 2018-10-20 15:54:46 -07:00
greg
d7baf065fb Changing what method to call to start parsing 2018-10-20 15:41:09 -07:00
greg
6b42f8b8de Change how parsing works 2018-10-20 14:27:00 -07:00
greg
d9e67a6341 Delete this old grammar file 2018-10-20 11:43:12 -07:00
greg
7de536ade0 Install failure crate 2018-10-20 11:17:18 -07:00
greg
f62b4c6906 Change format of error msg 2018-10-20 11:14:40 -07:00
greg
4679a9fc7f Remove compiler warnings 2018-10-20 00:55:37 -07:00
greg
c25354b2c7 Get rid of typechecking code (for now)
I'm tired of seeing the errors. See branch last_commit_with_typechecking
2018-10-20 00:41:56 -07:00
26 changed files with 1597 additions and 1947 deletions

31
Grammar
View File

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

View File

@@ -1,21 +1,21 @@
# Schala - a programming language meta-interpreter
Schala is a Rust framework written to make it easy to
create and experiment with toy programming languages. It provides
a common REPL, and a trait `ProgrammingLanguage` with provisions
for tokenizing text, parsing tokens, evaluating an abstract syntax tree,
and other tasks that are common to all programming languages.
Schala is a Rust framework written to make it easy to create and experiment
with toy programming languages. It provides a cross-language REPL and
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.
Schala is implemented as a Rust library `schala-repl`, which provides a
function `repl_main` meant to be used as the equivalent of main() for library
users. This function parses command-line arguments and either runs an interactive
REPL or interprets a program non-interactively.
## About
Individual programming language implementations are Rust types that implement
the `ProgrammingLanguageInterface` trait and store whatever persistent state is
relevant to that language. The ability to share state between different
programming languages is in the works.
## History
Schala started out life as an experiment in writing a Javascript-like
programming language that would never encounter any kind of runtime value
@@ -60,6 +60,7 @@ of learning how to write a programming language.
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
https://rust-lang-nursery.github.io/rustc-guide/type-inference.html
### Evaluation
*Understanding Computation*, Tom Stuart, O'Reilly 2013
@@ -77,4 +78,5 @@ http://blog.ulysse.io/2016/07/03/llvm-getting-started.html
###Rust resources
https://thefullsnack.com/en/rust-for-the-web.html
https://rocket.rs/guide/getting-started/

42
TODO.md
View File

@@ -1,6 +1,45 @@
#Typechecking Notes
(cf. cardelli paper)
Given a length function def:
````
fn length(x) {
if x.is_null {
0
} else {
succ(length(x.tail))
}
}
````
Constraints:
.null: List a -> bool
.tail: List a -> List a
0: Nat
succ: Nat -> Nat
# TODO Items
-make the REPL more advanced!
-Plan of attack:
-write a visitor pattern for AST
-convert AST type to including SourceMap'd wrappers (w/ .into())
-at the same time, amke sure the visitor pattern "skips over" the SourceMap'd stuff
so it can just care about AST structure
- 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!
-fuzz test schala
-look into Inkwell for LLVM
*A neat idea for pattern matching optimization would be if you could match on one of several things in a list
ex:
@@ -101,10 +140,7 @@ type enum {
- 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

View File

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

View File

@@ -5,7 +5,6 @@ extern crate schala_repl;
mod tokenizer;
mod parser;
mod eval;
mod compilation;
use schala_repl::{ProgrammingLanguageInterface, EvalOptions, UnfinishedComputation, FinishedComputation, TraceArtifact};
@@ -74,30 +73,4 @@ impl<'a> ProgrammingLanguageInterface for Maaru<'a> {
}
output.finish(Ok(evaluation_output))
}
/* TODO make this work with new framework */
/*
fn can_compile(&self) -> bool {
true
}
fn compile(&mut self, input: &str) -> LLVMCodeString {
let tokens = match tokenizer::tokenize(input) {
Ok(tokens) => tokens,
Err(err) => {
let msg = format!("Tokenization error: {:?}\n", err.msg);
panic!("{}", msg);
}
};
let ast = match parser::parse(&tokens, &[]) {
Ok(ast) => ast,
Err(err) => {
let msg = format!("Parse error: {:?}\n", err.msg);
panic!("{}", msg);
}
};
compilation::compile_ast(ast)
}
*/
}

View File

@@ -19,10 +19,10 @@ impl Fold for RecursiveDescentFn {
let new_block: syn::Block = parse_quote! {
{
let next_token = self.peek_with_token_offset();
let next_token_before_parse = self.token_handler.peek();
let record = ParseRecord {
production_name: stringify!(#ident).to_string(),
next_token: format!("{}", next_token.to_string_with_metadata()),
next_token: format!("{}", next_token_before_parse.to_string_with_metadata()),
level: self.parse_level,
};
self.parse_level += 1;
@@ -32,11 +32,7 @@ impl Fold for RecursiveDescentFn {
if self.parse_level != 0 {
self.parse_level -= 1;
}
match result {
Err(ParseError { token: None, msg }) =>
Err(ParseError { token: Some(next_token), msg }),
_ => result
}
result
}
};
i.block = Box::new(new_block);
@@ -45,7 +41,7 @@ impl Fold for RecursiveDescentFn {
}
#[proc_macro_attribute]
pub fn recursive_descent_method(attr: TokenStream, item: TokenStream) -> TokenStream {
pub fn recursive_descent_method(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input: syn::ItemFn = parse_macro_input!(item as syn::ItemFn);
let mut folder = RecursiveDescentFn {};

View File

@@ -2,12 +2,15 @@
name = "schala-lang"
version = "0.1.0"
authors = ["greg <greg.shuflin@protonmail.com>"]
edition = "2018"
[dependencies]
itertools = "0.5.8"
take_mut = "0.1.3"
maplit = "*"
lazy_static = "0.2.8"
failure = "0.1.2"
schala-lang-codegen = { path = "../codegen" }
schala-repl = { path = "../../schala-repl" }

View File

@@ -1,19 +1,46 @@
use std::rc::Rc;
use std::convert::From;
use builtin::{BinOp, PrefixOp};
use crate::builtin::{BinOp, PrefixOp};
#[derive(Clone, Debug, PartialEq)]
pub struct Node<T> {
n: T,
source_map: SourceMap
}
impl<T> Node<T> {
pub fn new(n: T) -> Node<T> {
Node { n, source_map: SourceMap::default() }
}
pub fn node(&self) -> &T {
&self.n
}
}
//TODO this PartialEq is here to make tests work - find a way to make it not necessary
#[derive(Clone, Debug, Default, PartialEq)]
struct SourceMap {
}
impl From<Expression> for Node<Expression> {
fn from(expr: Expression) -> Node<Expression> {
Node { n: expr, source_map: SourceMap::default() }
}
}
#[derive(Debug, PartialEq)]
pub struct AST(pub Vec<Statement>);
pub struct AST(pub Vec<Node<Statement>>);
#[derive(Debug, PartialEq, Clone)]
pub enum Statement {
ExpressionStatement(Expression),
ExpressionStatement(Node<Expression>),
Declaration(Declaration),
}
pub type Block = Vec<Statement>;
pub type Block = Vec<Node<Statement>>;
pub type ParamName = Rc<String>;
pub type InterfaceName = Rc<String>; //should be a singleton I think??
pub type FormalParam = (ParamName, Option<TypeIdentifier>);
#[derive(Debug, PartialEq, Clone)]
@@ -33,7 +60,7 @@ pub enum Declaration {
},
Impl {
type_name: TypeIdentifier,
interface_name: Option<InterfaceName>,
interface_name: Option<TypeSingletonName>,
block: Vec<Declaration>,
},
Interface {
@@ -57,12 +84,16 @@ pub struct TypeBody(pub Vec<Variant>);
pub enum Variant {
UnitStruct(Rc<String>),
TupleStruct(Rc<String>, Vec<TypeIdentifier>),
Record(Rc<String>, Vec<(Rc<String>, TypeIdentifier)>),
Record {
name: Rc<String>,
members: Vec<(Rc<String>, TypeIdentifier)>,
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct Expression(pub ExpressionType, pub Option<TypeIdentifier>);
#[derive(Debug, PartialEq, Clone)]
pub enum TypeIdentifier {
Tuple(Vec<TypeIdentifier>),
@@ -81,9 +112,9 @@ pub enum ExpressionType {
FloatLiteral(f64),
StringLiteral(Rc<String>),
BoolLiteral(bool),
BinExp(BinOp, Box<Expression>, Box<Expression>),
PrefixExp(PrefixOp, Box<Expression>),
TupleLiteral(Vec<Expression>),
BinExp(BinOp, Box<Node<Expression>>, Box<Node<Expression>>),
PrefixExp(PrefixOp, Box<Node<Expression>>),
TupleLiteral(Vec<Node<Expression>>),
Value(Rc<String>),
NamedStruct {
name: Rc<String>,
@@ -91,7 +122,7 @@ pub enum ExpressionType {
},
Call {
f: Box<Expression>,
arguments: Vec<Expression>,
arguments: Vec<Node<Expression>>,
},
Index {
indexee: Box<Expression>,
@@ -111,10 +142,12 @@ pub enum ExpressionType {
},
Lambda {
params: Vec<FormalParam>,
type_anno: Option<TypeIdentifier>,
body: Block,
},
ListLiteral(Vec<Expression>),
}
#[derive(Debug, PartialEq, Clone)]
pub enum Discriminator {
Simple(Expression),

View File

@@ -2,7 +2,7 @@ use std::rc::Rc;
use std::collections::HashMap;
use std::fmt;
use tokenizing::TokenType;
use crate::tokenizing::TokenKind;
use self::BuiltinTypeSpecifier::*;
use self::BuiltinTConst::*;
@@ -40,8 +40,8 @@ impl BinOp {
pub fn sigil(&self) -> &Rc<String> {
&self.sigil
}
pub fn from_sigil_token(tok: &TokenType) -> Option<BinOp> {
use self::TokenType::*;
pub fn from_sigil_token(tok: &TokenKind) -> Option<BinOp> {
use self::TokenKind::*;
let s = match tok {
Operator(op) => op,
Period => ".",
@@ -62,8 +62,8 @@ impl BinOp {
pub fn min_precedence() -> i32 {
i32::min_value()
}
pub fn get_precedence_from_token(op: &TokenType) -> Option<i32> {
use self::TokenType::*;
pub fn get_precedence_from_token(op: &TokenKind) -> Option<i32> {
use self::TokenKind::*;
let s = match op {
Operator(op) => op,
Period => ".",
@@ -75,7 +75,6 @@ impl BinOp {
};
let default = 10_000_000;
Some(BINOPS.get(s).map(|x| x.2.clone()).unwrap_or_else(|| {
println!("Warning: operator {} not defined", s);
default
}))
}
@@ -84,7 +83,6 @@ impl BinOp {
let s: &str = &self.sigil;
let default = 10_000_000;
BINOPS.get(s).map(|x| x.2.clone()).unwrap_or_else(|| {
println!("Warning: operator {} not defined", s);
default
})
}
@@ -130,7 +128,7 @@ lazy_static! {
"-" => (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`
"quot" => (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(StringT)), bx!(Func(bx!(Const(StringT)), bx!(Const(StringT))))), (), 30),
"^" => (Func(bx!(Const(Nat)), bx!(Func(bx!(Const(Nat)), bx!(Const(Nat))))), (), 20),

View File

@@ -6,9 +6,9 @@ use std::io;
use itertools::Itertools;
use util::ScopeStack;
use reduced_ast::{ReducedAST, Stmt, Expr, Lit, Func, Alternative};
use symbol_table::{SymbolSpec, Symbol, SymbolTable};
use crate::util::ScopeStack;
use crate::reduced_ast::{BoundVars, ReducedAST, Stmt, Expr, Lit, Func, Alternative, Subpattern};
use crate::symbol_table::{SymbolSpec, Symbol, SymbolTable};
pub struct State<'a> {
values: ScopeStack<'a, Rc<String>, ValueEntry>,
@@ -21,7 +21,6 @@ macro_rules! builtin_binding {
}
}
//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")));
@@ -34,6 +33,19 @@ impl<'a> State<'a> {
pub fn debug_print(&self) -> String {
format!("Values: {:?}", self.values)
}
fn new_frame(&'a self, items: &'a Vec<Node>, bound_vars: &BoundVars) -> State<'a> {
let mut inner_state = State {
values: self.values.new_scope(None),
symbol_table_handle: self.symbol_table_handle.clone(),
};
for (bound_var, val) in 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() });
}
}
inner_state
}
}
#[derive(Debug, Clone)]
@@ -72,6 +84,12 @@ impl Node {
Node::PrimTuple { items } => format!("{}", paren_wrapped_vec(items.iter().map(|x| x.to_repl()))),
}
}
fn is_true(&self) -> bool {
match self {
Node::Expr(Expr::Lit(crate::reduced_ast::Lit::Bool(true))) => true,
_ => false,
}
}
}
#[derive(Debug)]
@@ -106,7 +124,7 @@ impl Expr {
UserDefined { name: Some(name), .. } => format!("<function '{}'>", name),
},
Expr::Constructor {
type_name: _, name, tag, arity,
type_name: _, name, arity, ..
} => if *arity == 0 {
format!("{}", name)
} else {
@@ -116,6 +134,23 @@ impl Expr {
_ => format!("{:?}", self),
}
}
fn replace_conditional_target_sigil(self, replacement: &Expr) -> Expr {
use self::Expr::*;
match self {
ConditionalTargetSigilValue => replacement.clone(),
Unit | Lit(_) | Func(_) | Val(_) | Constructor { .. } |
CaseMatch { .. } | UnimplementedSigilValue => self,
Tuple(exprs) => Tuple(exprs.into_iter().map(|e| e.replace_conditional_target_sigil(replacement)).collect()),
Call { f, args } => {
let new_args = args.into_iter().map(|e| e.replace_conditional_target_sigil(replacement)).collect();
Call { f, args: new_args }
},
Conditional { .. } => panic!("Dunno if I need this, but if so implement"),
Assign { .. } => panic!("I'm pretty sure I don't need this"),
}
}
}
impl<'a> State<'a> {
@@ -194,7 +229,8 @@ impl<'a> State<'a> {
Assign { box val, box expr } => self.assign_expression(val, expr),
Unit => Ok(Node::Expr(Unit)),
CaseMatch { box cond, alternatives } => self.case_match_expression(cond, alternatives),
UnimplementedSigilValue => Err(format!("Sigil value eval not implemented"))
ConditionalTargetSigilValue => Ok(Node::Expr(ConditionalTargetSigilValue)),
UnimplementedSigilValue => Err(format!("Sigil value eval not implemented")),
}
}
}
@@ -208,7 +244,7 @@ impl<'a> State<'a> {
}
}
fn apply_data_constructor(&mut self, type_name: Rc<String>, name: Rc<String>, tag: usize, arity: usize, args: Vec<Expr>) -> EvalResult<Node> {
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));
}
@@ -264,7 +300,7 @@ impl<'a> State<'a> {
("-", &[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 {
("quot", &[Lit(Nat(l)), Lit(Nat(r))]) => if r == 0 {
return Err(format!("divide by zero"));
} else {
Lit(Nat(l / r))
@@ -351,52 +387,76 @@ impl<'a> State<'a> {
Ok(Node::Expr(Expr::Unit))
}
fn case_match_expression(&mut self, cond: Expr, alternatives: Vec<Alternative>) -> EvalResult<Node> {
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() });
}
}
if let Some(guard_expr) = alt.guard {
let evaled_guard = inner_state.expression(guard_expr.to_node());
println!("EVALED GUARD: {:?}", evaled_guard);
//continue
}
return inner_state.block(alt.item)
}
}
return Err(format!("PrimObject failed pattern match"));
},
Node::PrimTuple { .. } => Err(format!("Tuples not implemented")), //TODO make a distinction between not yet implemented and an actual runtime error
Node::Expr(e) => {
for alt in alternatives {
match (alt.guard, alt.tag) {
(Some(ref guard_expr), None) => {
match self.expression(guard_expr.clone().to_node())? {
Node::Expr(Expr::Lit(::reduced_ast::Lit::Bool(true))) =>
return self.block(alt.item),
_ => continue,
}
},
(None, None) => return self.block(alt.item),
_ => return Err(format!("Shouldn't match an expr against a pattern"))
}
}
return Err(format!("Expr Failed pattern match"));
fn guard_passes(&mut self, guard: &Option<Expr>, cond: &Node) -> EvalResult<bool> {
if let Some(ref guard_expr) = guard {
let guard_expr = match cond {
Node::Expr(ref e) => guard_expr.clone().replace_conditional_target_sigil(e),
_ => guard_expr.clone()
};
Ok(self.expression(guard_expr.to_node())?.is_true())
} else {
Ok(true)
}
}
}
fn case_match_expression(&mut self, cond: Expr, alternatives: Vec<Alternative>) -> EvalResult<Node> {
//TODO need to handle recursive subpatterns
let all_subpatterns_pass = |state: &mut State, subpatterns: &Vec<Option<Subpattern>>, items: &Vec<Node>| -> EvalResult<bool> {
if subpatterns.len() == 0 {
return Ok(true)
}
if items.len() != subpatterns.len() {
return Err(format!("Subpattern length isn't correct items {} subpatterns {}", items.len(), subpatterns.len()));
}
for (maybe_subp, cond) in subpatterns.iter().zip(items.iter()) {
if let Some(subp) = maybe_subp {
if !state.guard_passes(&subp.guard, &cond)? {
return Ok(false)
}
}
}
Ok(true)
};
let cond = self.expression(Node::Expr(cond))?;
for alt in alternatives {
// no matter what type of condition we have, ignore alternative if the guard evaluates false
if !self.guard_passes(&alt.guard, &cond)? {
continue;
}
match cond {
Node::PrimObject { ref tag, ref items, .. } => {
if alt.tag.map(|t| t == *tag).unwrap_or(true) {
let mut inner_state = self.new_frame(items, &alt.bound_vars);
if all_subpatterns_pass(&mut inner_state, &alt.subpatterns, items)? {
return inner_state.block(alt.item);
} else {
continue;
}
}
},
Node::PrimTuple { ref items } => {
let mut inner_state = self.new_frame(items, &alt.bound_vars);
if all_subpatterns_pass(&mut inner_state, &alt.subpatterns, items)? {
return inner_state.block(alt.item);
} else {
continue;
}
},
Node::Expr(ref _e) => {
if let None = alt.tag {
return self.block(alt.item)
}
}
}
}
Err(format!("{:?} failed pattern match", cond))
}
fn value(&mut self, name: Rc<String>) -> EvalResult<Node> {
use self::ValueEntry::*;
@@ -422,6 +482,7 @@ fn case_match_expression(&mut self, cond: Expr, alternatives: Vec<Alternative>)
},
_ => unreachable!(),
},
SymbolSpec::RecordConstructor { .. } => return Err(format!("This shouldn't be a record!")),
},
/* see if it's an ordinary variable TODO make variables go in symbol table */
None => match self.values.lookup(&name) {
@@ -436,29 +497,32 @@ fn case_match_expression(&mut self, cond: Expr, alternatives: Vec<Alternative>)
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
}
}
use crate::tokenizing::{Token, tokenize};
use crate::parsing::ParseResult;
use crate::ast::AST;
use crate::symbol_table::SymbolTable;
use crate::eval::State;
fn parse(tokens: Vec<Token>) -> ParseResult<AST> {
let mut parser = crate::parsing::Parser::new(tokens);
parser.parse()
}
fn evaluate_all_outputs(input: &str) -> Vec<Result<String, String>> {
let symbol_table = Rc::new(RefCell::new(SymbolTable::new()));
let mut state = State::new(symbol_table);
let ast = parse(tokenize(input)).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! test_in_fresh_env {
($string:expr, $correct:expr) => {
{
let all_output = all_output!($string);
let all_output = evaluate_all_outputs($string);
let ref output = all_output.last().unwrap();
assert_eq!(**output, Ok($correct.to_string()));
}
@@ -543,6 +607,15 @@ if a { is 15 -> "x", is 10 -> "y" }
test_in_fresh_env!(source, "\"y\"");
}
#[test]
fn string_pattern() {
let source = r#"
let a = "foo"
if a { is "foo" -> "x", is _ -> "y" }
"#;
test_in_fresh_env!(source, "\"x\"");
}
#[test]
fn boolean_pattern() {
let source = r#"
@@ -574,4 +647,102 @@ if Some(10) {
"#;
test_in_fresh_env!(source, "\"hella\"");
}
#[test]
fn tuple_pattern() {
let source = r#"
if (1, 2) {
is (1, x) -> x,
is _ -> 99
}
"#;
test_in_fresh_env!(source, 2);
}
#[test]
fn tuple_pattern_2() {
let source = r#"
if (1, 2) {
is (10, x) -> x,
is (y, x) -> x + y
}
"#;
test_in_fresh_env!(source, 3);
}
#[test]
fn tuple_pattern_3() {
let source = r#"
if (1, 5) {
is (10, x) -> x,
is (1, x) -> x
}
"#;
test_in_fresh_env!(source, 5);
}
#[test]
fn tuple_pattern_4() {
let source = r#"
if (1, 5) {
is (10, x) -> x,
is (1, x) -> x,
}
"#;
test_in_fresh_env!(source, 5);
}
#[test]
fn prim_obj_pattern() {
let source = r#"
type Stuff = Mulch(Nat) | Jugs(Nat, String) | Mardok
let a = Mulch(20)
let b = Jugs(1, "haha")
let c = Mardok
let x = if a {
is Mulch(20) -> "x",
is _ -> "ERR"
}
let y = if b {
is Mulch(n) -> "ERR",
is Jugs(2, _) -> "ERR",
is Jugs(1, s) -> s,
is _ -> "ERR",
}
let z = if c {
is Jugs(_, _) -> "ERR",
is Mardok -> "NIGH",
is _ -> "ERR",
}
(x, y, z)
"#;
test_in_fresh_env!(source, r#"("x", "haha", "NIGH")"#);
}
#[test]
fn basic_lambda_syntax() {
let source = r#"
let q = \(x, y) { x * y }
let x = q(5,2)
let y = \(m, n, o) { m + n + o }(1,2,3)
(x, y)
"#;
test_in_fresh_env!(source, r"(10, 6)");
}
#[test]
fn lambda_syntax_2() {
let source = r#"
fn milta() {
\(x) { x + 33 }
}
milta()(10)
"#;
test_in_fresh_env!(source, "43");
}
}

View File

@@ -2,12 +2,16 @@
#![feature(custom_attribute)]
#![feature(unrestricted_attribute_tokens)]
#![feature(slice_patterns, box_patterns, box_syntax)]
//! `schala-lang` is where the Schala programming language is actually implemented.
//! It defines the `Schala` type, which contains the state for a Schala REPL, and implements
//! `ProgrammingLanguageInterface` and the chain of compiler passes for it.
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_repl_codegen;
@@ -38,13 +42,18 @@ mod eval;
#[derive(ProgrammingLanguageInterface)]
#[LanguageName = "Schala"]
#[SourceFileExtension = "schala"]
#[PipelineSteps(tokenizing, parsing(compact,expanded,trace), symbol_table, typechecking, ast_reducing, eval)]
#[PipelineSteps(load_source, tokenizing, parsing(compact,expanded,trace), symbol_table, typechecking, ast_reducing, eval)]
#[DocMethod = get_doc]
#[HandleCustomInterpreterDirectives = handle_custom_interpreter_directives]
/// All bits of state necessary to parse and execute a Schala program are stored in this struct.
/// `state` represents the execution state for the AST-walking interpreter, the other fields
/// should be self-explanatory.
pub struct Schala {
source_reference: SourceReference,
state: eval::State<'static>,
symbol_table: Rc<RefCell<symbol_table::SymbolTable>>,
type_context: typechecking::TypeContext<'static>,
active_parser: Option<parsing::Parser>,
}
impl Schala {
@@ -53,36 +62,42 @@ impl Schala {
}
fn handle_custom_interpreter_directives(&mut self, commands: &Vec<&str>) -> Option<String> {
Some(format!("You typed a command but I can't handle it"))
Some(format!("Schala-lang command: {:?} not supported", commands.get(0)))
}
}
impl Schala {
/// Creates a new Schala environment *without* any prelude.
fn new_blank_env() -> Schala {
let symbols = Rc::new(RefCell::new(symbol_table::SymbolTable::new()));
Schala {
source_reference: SourceReference::new(),
symbol_table: symbols.clone(),
type_context: typechecking::TypeContext::new(symbols.clone()),
state: eval::State::new(symbols),
type_context: typechecking::TypeContext::new(),
active_parser: None,
}
}
/// Creates a new Schala environment with the standard prelude, which is defined as ordinary
/// Schala code in the file `prelude.schala`
pub fn new() -> Schala {
let prelude = r#"
type Option<T> = Some(T) | None
type Color = Red | Green | Blue
type Ord = LT | EQ | GT
"#;
let prelude = include_str!("prelude.schala");
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> {
fn load_source<'a>(input: &'a str, handle: &mut Schala, _comp: Option<&mut UnfinishedComputation>) -> Result<&'a str, String> {
handle.source_reference.load_new_source(input);
Ok(input)
}
fn tokenizing(input: &str, _handle: &mut Schala, 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(", ");
let token_string = tokens.iter().map(|t| t.to_string_with_metadata()).join(", ");
comp.add_artifact(TraceArtifact::new("tokens", token_string));
});
@@ -94,9 +109,17 @@ fn tokenizing(_handle: &mut Schala, input: &str, comp: Option<&mut UnfinishedCom
}
}
fn parsing(_handle: &mut Schala, input: Vec<tokenizing::Token>, comp: Option<&mut UnfinishedComputation>) -> Result<ast::AST, String> {
fn parsing(input: Vec<tokenizing::Token>, handle: &mut Schala, comp: Option<&mut UnfinishedComputation>) -> Result<ast::AST, String> {
use crate::parsing::Parser;
let mut parser = match handle.active_parser.take() {
None => Parser::new(input),
Some(parser) => parser
};
let ast = parser.parse();
let trace = parser.format_parse_trace();
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());
@@ -108,10 +131,27 @@ fn parsing(_handle: &mut Schala, input: Vec<tokenizing::Token>, comp: Option<&mu
Some(ref x) => println!("Bad parsing debug option: {}", x),
};
});
ast.map_err(|err| err.msg)
ast.map_err(|err| format_parse_error(err, handle))
}
fn symbol_table(handle: &mut Schala, input: ast::AST, comp: Option<&mut UnfinishedComputation>) -> Result<ast::AST, String> {
fn format_parse_error(error: parsing::ParseError, handle: &mut Schala) -> String {
let line_num = error.token.line_num;
let ch = error.token.char_num;
let line_from_program = handle.source_reference.get_line(line_num);
let location_pointer = format!("{}^", " ".repeat(ch));
let line_num_digits = format!("{}", line_num).chars().count();
let space_padding = " ".repeat(line_num_digits);
format!(r#"
{error_msg}
{space_padding} |
{line_num} | {}
{space_padding} | {}
"#, line_from_program, location_pointer, error_msg=error.msg, space_padding=space_padding, line_num=line_num)
}
fn symbol_table(input: ast::AST, handle: &mut Schala, comp: Option<&mut UnfinishedComputation>) -> Result<ast::AST, String> {
let add = handle.symbol_table.borrow_mut().add_top_level_symbols(&input);
match add {
Ok(()) => {
@@ -123,33 +163,25 @@ fn symbol_table(handle: &mut Schala, input: ast::AST, comp: Option<&mut Unfinish
}
}
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 typechecking(input: ast::AST, handle: &mut Schala, comp: Option<&mut UnfinishedComputation>) -> Result<ast::AST, String> {
let result = handle.type_context.typecheck(&input);
comp.map(|comp| {
let artifact = TraceArtifact::new("type", format!("{:?}", result));
comp.add_artifact(artifact);
});
Ok(input)
}
fn ast_reducing(handle: &mut Schala, input: ast::AST, comp: Option<&mut UnfinishedComputation>) -> Result<reduced_ast::ReducedAST, String> {
fn ast_reducing(input: ast::AST, handle: &mut Schala, 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> {
fn eval(input: reduced_ast::ReducedAST, handle: &mut Schala, 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
@@ -161,3 +193,21 @@ fn eval(handle: &mut Schala, input: reduced_ast::ReducedAST, comp: Option<&mut U
eval_output
}
/// Represents lines of source code
struct SourceReference {
lines: Option<Vec<String>>
}
impl SourceReference {
fn new() -> SourceReference {
SourceReference { lines: None }
}
fn load_new_source(&mut self, source: &str) {
//TODO this is a lot of heap allocations - maybe there's a way to make it more efficient?
self.lines = Some(source.lines().map(|s| s.to_string()).collect()); }
fn get_line(&self, line: usize) -> String {
self.lines.as_ref().and_then(|x| x.get(line).map(|s| s.to_string())).unwrap_or(format!("NO LINE FOUND"))
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
type Option<T> = Some(T) | None
type Color = Red | Green | Blue
type Ord = LT | EQ | GT
fn map(input: Option<T>, func: Func): Option<T> {
if input {
is Some(x) -> Some(func(x)),
is None -> None,
}
}

View File

@@ -1,8 +1,8 @@
use std::rc::Rc;
use ast::{AST, Statement, Expression, ExpressionType, Declaration, Discriminator, IfExpressionBody, Pattern, PatternLiteral, Guard, HalfExpr};
use symbol_table::{Symbol, SymbolSpec, SymbolTable};
use builtin::{BinOp, PrefixOp};
use crate::ast::*;
use crate::symbol_table::{Symbol, SymbolSpec, SymbolTable};
use crate::builtin::{BinOp, PrefixOp};
#[derive(Debug)]
pub struct ReducedAST(pub Vec<Stmt>);
@@ -48,6 +48,7 @@ pub enum Expr {
then_clause: Vec<Stmt>,
else_clause: Vec<Stmt>,
},
ConditionalTargetSigilValue,
CaseMatch {
cond: Box<Expr>,
alternatives: Vec<Alternative>
@@ -55,19 +56,23 @@ pub enum Expr {
UnimplementedSigilValue
}
pub type BoundVars = Vec<Option<Rc<String>>>; //remember that order matters here
#[derive(Debug, Clone)]
pub struct Alternative {
pub tag: Option<usize>,
pub subpatterns: Vec<Alternative>,
pub subpatterns: Vec<Option<Subpattern>>,
pub guard: Option<Expr>,
pub bound_vars: Vec<Option<Rc<String>>>, //remember that order matters here
pub bound_vars: BoundVars,
pub item: Vec<Stmt>,
}
impl Alternative {
fn default(item: Vec<Stmt>) -> Alternative {
Alternative { tag: None, subpatterns: vec![], guard: None, bound_vars: vec![], item }
}
#[derive(Debug, Clone)]
pub struct Subpattern {
pub tag: Option<usize>,
pub subpatterns: Vec<Option<Subpattern>>,
pub bound_vars: BoundVars,
pub guard: Option<Expr>,
}
#[derive(Debug, Clone)]
@@ -93,7 +98,7 @@ 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));
output.push(statement.node().reduce(symbol_table));
}
ReducedAST(output)
}
@@ -101,17 +106,21 @@ impl AST {
impl Statement {
fn reduce(&self, symbol_table: &SymbolTable) -> Stmt {
use ast::Statement::*;
use crate::ast::Statement::*;
match self {
ExpressionStatement(expr) => Stmt::Expr(expr.reduce(symbol_table)),
ExpressionStatement(expr) => Stmt::Expr(expr.node().reduce(symbol_table)),
Declaration(decl) => decl.reduce(symbol_table),
}
}
}
fn reduce_block(block: &Block, symbol_table: &SymbolTable) -> Vec<Stmt> {
block.iter().map(|stmt| stmt.node().reduce(symbol_table)).collect()
}
impl Expression {
fn reduce(&self, symbol_table: &SymbolTable) -> Expr {
use ast::ExpressionType::*;
use crate::ast::ExpressionType::*;
let ref input = self.0;
match input {
NatLiteral(n) => Expr::Lit(Lit::Nat(*n)),
@@ -131,39 +140,58 @@ impl Expression {
},
Call { f, arguments } => Expr::Call {
f: Box::new(f.reduce(symbol_table)),
args: arguments.iter().map(|arg| arg.reduce(symbol_table)).collect(),
args: arguments.iter().map(|arg| arg.node().reduce(symbol_table)).collect(),
},
TupleLiteral(exprs) => Expr::Tuple(exprs.iter().map(|e| e.reduce(symbol_table)).collect()),
TupleLiteral(exprs) => Expr::Tuple(exprs.iter().map(|e| e.node().reduce(symbol_table)).collect()),
IfExpression { discriminator, body } => reduce_if_expression(discriminator, body, symbol_table),
_ => Expr::UnimplementedSigilValue,
Lambda { params, body, .. } => reduce_lambda(params, body, symbol_table),
NamedStruct { .. } => Expr::UnimplementedSigilValue,
Index { .. } => Expr::UnimplementedSigilValue,
WhileExpression { .. } => Expr::UnimplementedSigilValue,
ForExpression { .. } => Expr::UnimplementedSigilValue,
ListLiteral { .. } => Expr::UnimplementedSigilValue,
}
}
}
fn reduce_lambda(params: &Vec<FormalParam>, body: &Block, symbol_table: &SymbolTable) -> Expr {
Expr::Func(Func::UserDefined {
name: None,
params: params.iter().map(|param| param.0.clone()).collect(),
body: reduce_block(body, symbol_table),
})
}
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!("Can't yet handle binop discriminators")
Discriminator::BinOp(ref _expr, ref _binop) => panic!("Can't yet handle binop discriminators")
});
match *body {
IfExpressionBody::SimpleConditional(ref then_clause, ref else_clause) => {
let then_clause = then_clause.iter().map(|expr| expr.reduce(symbol_table)).collect();
let then_clause = reduce_block(then_clause, symbol_table);
let else_clause = match else_clause {
None => vec![],
Some(stmts) => stmts.iter().map(|expr| expr.reduce(symbol_table)).collect(),
Some(stmts) => reduce_block(stmts, symbol_table),
};
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 then_clause = reduce_block(then_clause, symbol_table);
let else_clause = match else_clause {
None => vec![],
Some(stmts) => stmts.iter().map(|expr| expr.reduce(symbol_table)).collect(),
Some(stmts) => reduce_block(stmts, symbol_table),
};
let alternatives = vec![
pat.to_alternative(&cond, then_clause, symbol_table),
Alternative::default(else_clause),
pat.to_alternative(then_clause, symbol_table),
Alternative {
tag: None,
subpatterns: vec![],
bound_vars: vec![],
guard: None,
item: else_clause
},
];
Expr::CaseMatch {
@@ -176,8 +204,8 @@ fn reduce_if_expression(discriminator: &Discriminator, body: &IfExpressionBody,
for arm in guard_arms {
match arm.guard {
Guard::Pat(ref p) => {
let item = arm.body.iter().map(|expr| expr.reduce(symbol_table)).collect();
let alt = p.to_alternative(&cond, item, symbol_table);
let item = reduce_block(&arm.body, symbol_table);
let alt = p.to_alternative(item, symbol_table);
alternatives.push(alt);
},
Guard::HalfExpr(HalfExpr { op: _, expr: _ }) => {
@@ -193,104 +221,134 @@ fn reduce_if_expression(discriminator: &Discriminator, body: &IfExpressionBody,
* x is SomeBigOldEnum(_, x, Some(t))
*/
fn handle_symbol(symbol: Option<&Symbol>, inner_patterns: &Vec<Pattern>, symbol_table: &SymbolTable) -> Subpattern {
use self::Pattern::*;
let tag = symbol.map(|symbol| match symbol.spec {
SymbolSpec::DataConstructor { index, .. } => index.clone(),
_ => panic!("Symbol is not a data constructor - this should've been caught in type-checking"),
});
let bound_vars = inner_patterns.iter().map(|p| match p {
Literal(PatternLiteral::VarPattern(var)) => Some(var.clone()),
_ => None,
}).collect();
let subpatterns = inner_patterns.iter().map(|p| match p {
Ignored => None,
Literal(PatternLiteral::VarPattern(_)) => None,
Literal(other) => Some(other.to_subpattern(symbol_table)),
tp @ TuplePattern(_) => Some(tp.to_subpattern(symbol_table)),
ts @ TupleStruct(_, _) => Some(ts.to_subpattern(symbol_table)),
Record(..) => unimplemented!(),
}).collect();
let guard = None;
/*
let guard_equality_exprs: Vec<Expr> = subpatterns.iter().map(|p| match p {
Literal(lit) => match lit {
_ => unimplemented!()
},
_ => unimplemented!()
}).collect();
*/
Subpattern {
tag,
subpatterns,
guard,
bound_vars,
}
}
impl Pattern {
fn to_alternative(&self, cond: &Box<Expr>, item: Vec<Stmt>, symbol_table: &SymbolTable) -> Alternative {
use self::Pattern::*;
fn handle_symbol(symbol: &Symbol, subpatterns: &Vec<Pattern>, item: Vec<Stmt>) -> Alternative {
let tag = match symbol.spec {
SymbolSpec::DataConstructor { index, .. } => index.clone(),
_ => panic!("Symbol is not a data constructor - this should've been caught in type-checking"),
};
let bound_vars = subpatterns.iter().map(|p| match p {
Literal(PatternLiteral::VarPattern(var)) => Some(var.clone()),
_ => None,
}).collect();
/*
let guard_equality_exprs: Vec<Expr> = subpatterns.iter().map(|p| match p {
Literal(lit) => match lit {
_ => unimplemented!()
},
_ => unimplemented!()
}).collect();
*/
let guard = None;
let subpatterns = vec![];
Alternative {
tag: Some(tag),
subpatterns,
guard,
bound_vars,
item,
}
fn to_alternative(&self, item: Vec<Stmt>, symbol_table: &SymbolTable) -> Alternative {
let s = self.to_subpattern(symbol_table);
Alternative {
tag: s.tag,
subpatterns: s.subpatterns,
bound_vars: s.bound_vars,
guard: s.guard,
item
}
}
fn to_subpattern(&self, symbol_table: &SymbolTable) -> Subpattern {
use self::Pattern::*;
match self {
TupleStruct(name, subpatterns) => {
TupleStruct(name, inner_patterns) => {
let symbol = symbol_table.lookup_by_name(name).expect(&format!("Symbol {} not found", name));
handle_symbol(symbol, subpatterns, item)
},
TuplePattern(items) => {
unimplemented!()
handle_symbol(Some(symbol), inner_patterns, symbol_table)
},
TuplePattern(inner_patterns) => handle_symbol(None, inner_patterns, symbol_table),
Record(_name, _pairs) => {
unimplemented!()
},
Ignored => Alternative::default(item),
Literal(lit) => match lit {
PatternLiteral::NumPattern { neg, num } => {
let comparison = Expr::Lit(match (neg, num) {
(false, ExpressionType::NatLiteral(n)) => Lit::Nat(*n),
(false, ExpressionType::FloatLiteral(f)) => Lit::Float(*f),
(true, ExpressionType::NatLiteral(n)) => Lit::Int(-1*(*n as i64)),
(true, ExpressionType::FloatLiteral(f)) => Lit::Float(-1.0*f),
_ => panic!("This should never happen")
});
let guard = Some(Expr::Call {
f: Box::new(Expr::Func(Func::BuiltIn(Rc::new("==".to_string())))),
args: vec![comparison, *cond.clone()]
});
Alternative {
tag: None,
subpatterns: vec![],
guard,
bound_vars: vec![],
item
}
},
PatternLiteral::StringPattern(_s) => unimplemented!(),
PatternLiteral::BoolPattern(b) => {
let guard = Some(if *b {
*cond.clone()
} else {
Expr::Call {
f: Box::new(Expr::Func(Func::BuiltIn(Rc::new("!".to_string())))),
args: vec![*cond.clone()]
}
});
Alternative {
tag: None,
subpatterns: vec![],
guard,
bound_vars: vec![],
item
}
},
PatternLiteral::VarPattern(var) => match symbol_table.lookup_by_name(var) {
Some(symbol) => handle_symbol(symbol, &vec![], item),
None => Alternative {
tag: None,
subpatterns: vec![],
guard: None,
bound_vars: vec![Some(var.clone())],
item
}
Ignored => Subpattern { tag: None, subpatterns: vec![], guard: None, bound_vars: vec![] },
Literal(lit) => lit.to_subpattern(symbol_table),
}
}
}
impl PatternLiteral {
fn to_subpattern(&self, symbol_table: &SymbolTable) -> Subpattern {
use self::PatternLiteral::*;
match self {
NumPattern { neg, num } => {
let comparison = Expr::Lit(match (neg, num) {
(false, ExpressionType::NatLiteral(n)) => Lit::Nat(*n),
(false, ExpressionType::FloatLiteral(f)) => Lit::Float(*f),
(true, ExpressionType::NatLiteral(n)) => Lit::Int(-1*(*n as i64)),
(true, ExpressionType::FloatLiteral(f)) => Lit::Float(-1.0*f),
_ => panic!("This should never happen")
});
let guard = Some(Expr::Call {
f: Box::new(Expr::Func(Func::BuiltIn(Rc::new("==".to_string())))),
args: vec![comparison, Expr::ConditionalTargetSigilValue],
});
Subpattern {
tag: None,
subpatterns: vec![],
guard,
bound_vars: vec![],
}
},
StringPattern(s) => {
let guard = Some(Expr::Call {
f: Box::new(Expr::Func(Func::BuiltIn(Rc::new("==".to_string())))),
args: vec![Expr::Lit(Lit::StringLit(s.clone())), Expr::ConditionalTargetSigilValue]
});
Subpattern {
tag: None,
subpatterns: vec![],
guard,
bound_vars: vec![],
}
},
BoolPattern(b) => {
let guard = Some(if *b {
Expr::ConditionalTargetSigilValue
} else {
Expr::Call {
f: Box::new(Expr::Func(Func::BuiltIn(Rc::new("!".to_string())))),
args: vec![Expr::ConditionalTargetSigilValue]
}
});
Subpattern {
tag: None,
subpatterns: vec![],
guard,
bound_vars: vec![],
}
},
VarPattern(var) => match symbol_table.lookup_by_name(var) {
Some(symbol) => handle_symbol(Some(symbol), &vec![], symbol_table),
None => Subpattern {
tag: None,
subpatterns: vec![],
guard: None,
bound_vars: vec![Some(var.clone())],
}
}
}
}
}
@@ -298,7 +356,7 @@ impl Pattern {
impl Declaration {
fn reduce(&self, symbol_table: &SymbolTable) -> Stmt {
use self::Declaration::*;
use ::ast::Signature;
use crate::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 {
@@ -306,7 +364,7 @@ impl Declaration {
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(),
body: reduce_block(&statements, symbol_table),
}
},
TypeDecl { .. } => Stmt::Noop,
@@ -319,22 +377,22 @@ impl Declaration {
}
impl BinOp {
fn reduce(&self, symbol_table: &SymbolTable, lhs: &Box<Expression>, rhs: &Box<Expression>) -> Expr {
fn reduce(&self, symbol_table: &SymbolTable, lhs: &Box<Node<Expression>>, rhs: &Box<Node<Expression>>) -> Expr {
if **self.sigil() == "=" {
Expr::Assign {
val: Box::new(lhs.reduce(symbol_table)),
expr: Box::new(rhs.reduce(symbol_table)),
val: Box::new(lhs.node().reduce(symbol_table)),
expr: Box::new(rhs.node().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)]}
Expr::Call { f, args: vec![lhs.node().reduce(symbol_table), rhs.node().reduce(symbol_table)]}
}
}
}
impl PrefixOp {
fn reduce(&self, symbol_table: &SymbolTable, arg: &Box<Expression>) -> Expr {
fn reduce(&self, symbol_table: &SymbolTable, arg: &Box<Node<Expression>>) -> Expr {
let f = Box::new(Expr::Func(Func::BuiltIn(self.sigil().clone())));
Expr::Call { f, args: vec![arg.reduce(symbol_table)]}
Expr::Call { f, args: vec![arg.node().reduce(symbol_table)]}
}
}

View File

@@ -3,8 +3,9 @@ use std::rc::Rc;
use std::fmt;
use std::fmt::Write;
use ast;
use typechecking::TypeName;
use crate::ast;
use crate::ast::{TypeBody, TypeSingletonName, Signature};
use crate::typechecking::TypeName;
//cf. p. 150 or so of Language Implementation Patterns
pub struct SymbolTable {
@@ -43,6 +44,9 @@ pub enum SymbolSpec {
type_name: Rc<String>,
type_args: Vec<Rc<String>>,
},
RecordConstructor {
fields: HashMap<Rc<String>, Rc<String>>
}
}
impl fmt::Display for SymbolSpec {
@@ -51,6 +55,7 @@ impl fmt::Display for SymbolSpec {
match self {
Func(type_names) => write!(f, "Func({:?})", type_names),
DataConstructor { index, type_name, type_args } => write!(f, "DataConstructor(idx: {})({:?} -> {})", index, type_args, type_name),
RecordConstructor { fields } => write!(f, "RecordConstructor( <fields> )"),
}
}
}
@@ -59,62 +64,14 @@ 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, TypeIdentifier, Variant, TypeSingletonName, TypeBody};
use self::ast::Statement;
use self::ast::Declaration::*;
for statement in ast.0.iter() {
let statement = statement.node();
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 {
TypeIdentifier::Singleton(TypeSingletonName { name, ..}) => name.clone(),
TypeIdentifier::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)),
}
}
},
FuncSig(signature) | FuncDecl(signature, _) => self.add_function_signature(signature)?,
TypeDecl { name, body, mutable } => self.add_type_decl(name, body, mutable)?,
_ => ()
}
}
@@ -128,4 +85,74 @@ impl SymbolTable {
}
output
}
fn add_function_signature(&mut self, signature: &Signature) -> Result<(), String> {
let mut local_type_context = LocalTypeContext::new();
let types = signature.params.iter().map(|param| match param {
(_, Some(type_identifier)) => Rc::new(format!("{:?}", type_identifier)),
(_, None) => local_type_context.new_universal_type()
}).collect();
let spec = SymbolSpec::Func(types);
self.values.insert(
signature.name.clone(),
Symbol { name: signature.name.clone(), spec }
);
Ok(())
}
fn add_type_decl(&mut self, type_name: &TypeSingletonName, body: &TypeBody, _mutable: &bool) -> Result<(), String> {
use crate::ast::{TypeIdentifier, Variant};
let TypeBody(variants) = body;
let TypeSingletonName { name, .. } = type_name;
//TODO figure out why _params isn't being used here
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 {
TypeIdentifier::Singleton(TypeSingletonName { name, ..}) => name.clone(),
TypeIdentifier::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);
},
//TODO if there is only one variant, and it is a record, it doesn't need to have an
//explicit name
Variant::Record { name, members } => {
let fields = HashMap::new();
let spec = SymbolSpec::RecordConstructor { fields };
let symbol = Symbol { name: name.clone(), spec };
self.values.insert(name.clone(), symbol);
},
}
}
Ok(())
}
}
struct LocalTypeContext {
state: u8
}
impl LocalTypeContext {
fn new() -> LocalTypeContext {
LocalTypeContext { state: 0 }
}
fn new_universal_type(&mut self) -> TypeName {
let n = self.state;
self.state += 1;
Rc::new(format!("{}", (('a' as u8) + n) as char))
}
}

View File

@@ -5,14 +5,14 @@ use std::iter::{Iterator, Peekable};
use std::fmt;
#[derive(Debug, PartialEq, Clone)]
pub enum TokenType {
pub enum TokenKind {
Newline, Semicolon,
LParen, RParen,
LSquareBracket, RSquareBracket,
LAngleBracket, RAngleBracket,
LCurlyBrace, RCurlyBrace,
Pipe,
Pipe, Backslash,
Comma, Period, Colon, Underscore,
Slash,
@@ -27,9 +27,9 @@ pub enum TokenType {
Error(String),
}
use self::TokenType::*;
use self::TokenKind::*;
impl fmt::Display for TokenType {
impl fmt::Display for TokenKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&Operator(ref s) => write!(f, "Operator({})", **s),
@@ -87,19 +87,24 @@ lazy_static! {
#[derive(Debug, Clone)]
pub struct Token {
pub token_type: TokenType,
pub offset: (usize, usize),
pub kind: TokenKind,
pub line_num: usize,
pub char_num: usize
}
impl Token {
pub fn get_error(&self) -> Option<String> {
match self.token_type {
TokenType::Error(ref s) => Some(s.clone()),
match self.kind {
TokenKind::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)
format!("{}(L:{},c:{})", self.kind, self.line_num, self.char_num)
}
pub fn get_kind(&self) -> TokenKind {
self.kind.clone()
}
}
@@ -120,8 +125,8 @@ pub fn tokenize(input: &str) -> Vec<Token> {
})
.peekable();
while let Some((line_idx, ch_idx, c)) = input.next() {
let cur_tok_type = match c {
while let Some((line_num, char_num, c)) = input.next() {
let cur_tok_kind = match c {
'/' => match input.peek().map(|t| t.2) {
Some('/') => {
while let Some((_, _, c)) = input.next() {
@@ -157,17 +162,18 @@ pub fn tokenize(input: &str) -> Vec<Token> {
'{' => LCurlyBrace, '}' => RCurlyBrace,
'[' => LSquareBracket, ']' => RSquareBracket,
'"' => handle_quote(&mut input),
'\\' => Backslash,
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 c.is_alphabetic() || c == '_' => handle_alphabetic(c, &mut input),
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.push(Token { kind: cur_tok_kind, line_num, char_num });
}
tokens
}
fn handle_digit(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenType {
fn handle_digit(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenKind {
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();
@@ -182,7 +188,7 @@ fn handle_digit(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>) ->
}
}
fn handle_quote(input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenType {
fn handle_quote(input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenKind {
let mut buf = String::new();
loop {
match input.next().map(|(_, _, c)| { c }) {
@@ -201,22 +207,22 @@ fn handle_quote(input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenType
}
},
Some(c) => buf.push(c),
None => return TokenType::Error(format!("Unclosed string")),
None => return TokenKind::Error(format!("Unclosed string")),
}
}
TokenType::StrLiteral(Rc::new(buf))
TokenKind::StrLiteral(Rc::new(buf))
}
fn handle_alphabetic(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenType {
fn handle_alphabetic(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenKind {
let mut buf = String::new();
buf.push(c);
if c == '_' && input.peek().map(|&(_, _, c)| { !c.is_alphabetic() }).unwrap_or(true) {
return TokenType::Underscore
return TokenKind::Underscore
}
loop {
match input.peek().map(|&(_, _, c)| { c }) {
Some(c) if c.is_alphanumeric() => {
Some(c) if c.is_alphanumeric() || c == '_' => {
input.next();
buf.push(c);
},
@@ -225,12 +231,12 @@ fn handle_alphabetic(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>
}
match KEYWORDS.get(buf.as_str()) {
Some(kw) => TokenType::Keyword(*kw),
None => TokenType::Identifier(Rc::new(buf)),
Some(kw) => TokenKind::Keyword(*kw),
None => TokenKind::Identifier(Rc::new(buf)),
}
}
fn handle_operator(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenType {
fn handle_operator(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenKind {
match c {
'<' | '>' | '|' | '.' => {
let ref next = input.peek().map(|&(_, _, c)| { c });
@@ -275,7 +281,7 @@ fn handle_operator(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>)
}
}
}
TokenType::Operator(Rc::new(buf))
TokenKind::Operator(Rc::new(buf))
}
#[cfg(test)]
@@ -290,26 +296,29 @@ mod schala_tokenizer_tests {
#[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"),
let token_kinds: Vec<TokenKind> = a.into_iter().map(move |t| t.kind).collect();
assert_eq!(token_kinds, 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")]);
let token_kinds: Vec<TokenKind> = tokenize("4_8").into_iter().map(move |t| t.kind).collect();
assert_eq!(token_kinds, vec![digit!("4"), Underscore, digit!("8")]);
let token_kinds2: Vec<TokenKind> = tokenize("aba_yo").into_iter().map(move |t| t.kind).collect();
assert_eq!(token_kinds2, vec![ident!("aba_yo")]);
}
#[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")]);
let token_kinds: Vec<TokenKind> = tokenize("1 + /* hella /* bro */ */ 2").into_iter().map(move |t| t.kind).collect();
assert_eq!(token_kinds, 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")]);
let token_kinds: Vec<TokenKind> = tokenize("1 `plus` 2").into_iter().map(move |t| t.kind).collect();
assert_eq!(token_kinds, vec![digit!("1"), op!("plus"), digit!("2")]);
}
}

View File

@@ -1,493 +1,254 @@
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};
use crate::ast::*;
use crate::util::ScopeStack;
pub type TypeName = Rc<String>;
type TypeResult<T> = Result<T, String>;
#[derive(Debug, PartialEq, Clone)]
enum Type {
pub struct TypeContext<'a> {
variable_map: ScopeStack<'a, Rc<String>, Type<TVar>>,
evar_count: u32
}
/// `InferResult` is the monad in which type inference takes place.
type InferResult<T> = Result<T, TypeError>;
#[derive(Debug, Clone)]
struct TypeError { msg: String }
impl TypeError {
fn new<A>(msg: &str) -> InferResult<A> {
Err(TypeError { msg: msg.to_string() })
}
}
/// `Type` is parameterized by whether the type variables can be just universal, or universal or
/// existential.
#[derive(Debug, Clone)]
enum Type<A> {
Var(A),
Const(TConst),
Var(TypeName),
Func(Vec<Type>),
Arrow(Box<Type<A>>, Box<Type<A>>),
}
#[derive(Debug, PartialEq, Clone)]
enum TConst {
Unit,
Nat,
StringT,
//Custom(String)
#[derive(Debug, Clone)]
enum TVar {
Univ(UVar),
Exist(ExistentialVar)
}
#[derive(Debug, PartialEq, Clone)]
struct Scheme {
names: Vec<TypeName>,
ty: Type,
}
#[derive(Debug, Clone)]
struct UVar(Rc<String>);
impl fmt::Display for Scheme {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "∀{:?} . {:?}", self.names, self.ty)
#[derive(Debug, Clone)]
struct ExistentialVar(u32);
impl Type<UVar> {
fn to_tvar(&self) -> Type<TVar> {
match self {
Type::Var(UVar(name)) => Type::Var(TVar::Univ(UVar(name.clone()))),
Type::Const(ref c) => Type::Const(c.clone()),
Type::Arrow(a, b) => Type::Arrow(
Box::new(a.to_tvar()),
Box::new(b.to_tvar())
)
}
}
}
#[derive(Debug, PartialEq, Clone)]
struct Substitution(HashMap<TypeName, Type>);
impl Substitution {
fn empty() -> Substitution {
Substitution(HashMap::new())
impl Type<TVar> {
fn skolemize(&self) -> Type<UVar> {
match self {
Type::Var(TVar::Univ(uvar)) => Type::Var(uvar.clone()),
Type::Var(TVar::Exist(_)) => Type::Var(UVar(Rc::new(format!("sk")))),
Type::Const(ref c) => Type::Const(c.clone()),
Type::Arrow(a, b) => Type::Arrow(
Box::new(a.skolemize()),
Box::new(b.skolemize())
)
}
}
}
#[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;
impl TypeIdentifier {
fn to_monotype(&self) -> Type<UVar> {
match self {
TypeIdentifier::Tuple(_) => Type::Const(TConst::Nat),
TypeIdentifier::Singleton(TypeSingletonName { name, .. }) => {
match &name[..] {
"Nat" => Type::Const(TConst::Nat),
"Int" => Type::Const(TConst::Int),
"Float" => Type::Const(TConst::Float),
"Bool" => Type::Const(TConst::Bool),
"String" => Type::Const(TConst::StringT),
_ => Type::Const(TConst::Nat),
}
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
#[derive(Debug, Clone)]
enum TConst {
User(Rc<String>),
Unit,
Nat,
Int,
Float,
StringT,
Bool,
}
impl TConst {
fn user(name: &str) -> TConst {
TConst::User(Rc::new(name.to_string()))
}
}
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();
pub fn new() -> TypeContext<'a> {
TypeContext {
variable_map: ScopeStack::new(None),
evar_count: 0
}
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))
pub fn typecheck(&mut self, ast: &AST) -> Result<String, String> {
match self.infer_ast(ast) {
Ok(t) => Ok(format!("{:?}", t)),
Err(err) => Err(format!("Type error: {:?}", err))
}
}
}
impl TypeEnv {
fn instantiate(&mut self, sigma: Scheme) -> Type {
match sigma {
Scheme { ty, .. } => ty,
impl<'a> TypeContext<'a> {
fn infer_ast(&mut self, ast: &AST) -> InferResult<Type<UVar>> {
self.infer_block(&ast.0)
}
fn infer_statement(&mut self, stmt: &Statement) -> InferResult<Type<UVar>> {
match stmt {
Statement::ExpressionStatement(ref expr) => self.infer_expr(expr.node()),
Statement::Declaration(ref decl) => self.infer_decl(decl),
}
}
fn generate(&mut self, ty: Type) -> Scheme {
Scheme {
names: vec![], //TODO incomplete
ty
fn infer_expr(&mut self, expr: &Expression) -> InferResult<Type<UVar>> {
match expr {
Expression(expr_type, Some(type_anno)) => {
let tx = self.infer_expr_type(expr_type)?;
let ty = type_anno.to_monotype();
self.unify(&ty.to_tvar(), &tx.to_tvar()).map(|x| x.skolemize())
},
Expression(expr_type, None) => self.infer_expr_type(expr_type)
}
}
fn infer_block(&mut self, block: &Vec<ast::Statement>) -> TypeResult<Type> {
fn infer_decl(&mut self, _decl: &Declaration) -> InferResult<Type<UVar>> {
Ok(Type::Const(TConst::user("unimplemented")))
}
fn infer_expr_type(&mut self, expr_type: &ExpressionType) -> InferResult<Type<UVar>> {
use self::ExpressionType::*;
Ok(match expr_type {
NatLiteral(_) => Type::Const(TConst::Nat),
FloatLiteral(_) => Type::Const(TConst::Float),
StringLiteral(_) => Type::Const(TConst::StringT),
BoolLiteral(_) => Type::Const(TConst::Bool),
Value(name) => {
//TODO handle the distinction between 0-arg constructors and variables at some point
// need symbol table for that
match self.variable_map.lookup(name) {
Some(ty) => ty.clone().skolemize(),
None => return TypeError::new(&format!("Variable {} not found", name))
}
},
IfExpression { discriminator, body } => self.infer_if_expr(discriminator, body)?,
Call { f, arguments } => {
let tf = self.infer_expr(f)?; //has to be an Arrow Type
let targ = self.infer_expr(&arguments[0].node())?; // TODO make this work with functions with more than one arg
match tf {
Type::Arrow(t1, t2) => {
self.unify(&t1.to_tvar(), &targ.to_tvar())?;
*t2.clone()
},
_ => return TypeError::new("not a function")
}
},
Lambda { params, .. } => {
let _arg_type = match &params[0] {
(_, Some(type_anno)) => type_anno.to_monotype().to_tvar(),
(_, None) => self.allocate_existential(),
};
//let _result_type = unimplemented!();
return TypeError::new("Unimplemented");
//Type::Arrow(Box::new(arg_type), Box::new(result_type))
}
_ => Type::Const(TConst::user("unimplemented"))
})
}
fn infer_if_expr(&mut self, discriminator: &Discriminator, body: &IfExpressionBody) -> InferResult<Type<UVar>> {
let _test = match discriminator {
Discriminator::Simple(expr) => expr,
_ => return TypeError::new("Dame desu")
};
let (_then_clause, _maybe_else_clause) = match body {
IfExpressionBody::SimpleConditional(a, b) => (a, b),
_ => return TypeError::new("Dont work")
};
TypeError::new("Not implemented")
}
fn infer_block(&mut self, block: &Block) -> InferResult<Type<UVar>> {
let mut output = Type::Const(TConst::Unit);
for statement in block {
output = self.infer_statement(statement)?;
for statement in block.iter() {
output = self.infer_statement(statement.node())?;
}
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 unify(&mut self, _t1: &Type<TVar>, _t2: &Type<TVar>) -> InferResult<Type<TVar>> {
TypeError::new("not implemented")
}
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)
})
fn allocate_existential(&mut self) -> Type<TVar> {
let n = self.evar_count;
self.evar_count += 1;
Type::Var(TVar::Exist(ExistentialVar(n)))
}
}
/* 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;
use super::*;
fn parse(input: &str) -> AST {
let tokens: Vec<crate::tokenizing::Token> = crate::tokenizing::tokenize(input);
let mut parser = crate::parsing::Parser::new(tokens);
parser.parse().unwrap()
}
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())
let mut tc = TypeContext::new();
let ast = parse($input);
tc.add_symbols(&ast);
assert_eq!($correct, tc.type_check(&ast).unwrap())
}
}
}
#[test]
fn basic_inference() {
type_test!("30", Const(Nat));
//type_test!("fn x(a: Int): Bool {}; x(1)", TConst(Boolean));
}
*/
}

View File

@@ -35,6 +35,7 @@ impl<'a, T, V> ScopeStack<'a, T, V> where T: Hash + Eq {
scope_name: name,
}
}
#[allow(dead_code)]
pub fn get_name(&self) -> Option<&String> {
self.scope_name.as_ref()
}

View File

@@ -1,4 +1,5 @@
#![feature(trace_macros)]
#![recursion_limit="128"]
extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
@@ -66,6 +67,65 @@ fn get_attribute_identifier(attr_name: &str, attrs: &Vec<Attribute>) -> Option<p
})
}
/* a pass_chain function signature with input A and output B looks like:
* fn(A, &mut ProgrammingLanguageInterface, Option<&mut DebugHandler>) -> Result<B, String>
*
* TODO use some kind of failure-handling library to make this better
*/
fn generate_pass_chain(idents: Vec<Ident>) -> proc_macro2::TokenStream {
let final_return = quote! {
{
let final_output: FinishedComputation = unfinished_computation.finish(Ok(input_to_next_stage));
final_output
}
};
let nested_passes = idents.iter()
.rev()
.fold(final_return, |later_fragment, pass_name| {
quote! {
{
let pass_name = stringify!(#pass_name);
let (output, duration) = {
let ref debug_map = eval_options.debug_passes;
let debug_handle = match debug_map.get(pass_name) {
Some(PassDebugOptionsDescriptor { opts }) => {
let ptr = &mut unfinished_computation;
ptr.cur_debug_options = opts.clone();
Some(ptr)
}
_ => None
};
let start = time::Instant::now();
let pass_output = #pass_name(input_to_next_stage, self, debug_handle);
let elapsed = start.elapsed();
(pass_output, elapsed)
};
if eval_options.debug_timing {
unfinished_computation.durations.push(duration);
}
match output {
Ok(input_to_next_stage) => #later_fragment,
//TODO this error type needs to be guaranteed to provide a useable string
Err(err) => return unfinished_computation.output(Err(format!("Pass {} failed:\n{}", pass_name, err))),
}
}
}
});
quote! {
{
use std::time;
use schala_repl::PassDebugOptionsDescriptor;
let eval_options = options;
let input_to_next_stage = input;
let mut unfinished_computation = UnfinishedComputation::default();
#nested_passes
}
}
}
#[proc_macro_derive(ProgrammingLanguageInterface,
attributes(LanguageName, SourceFileExtension, PipelineSteps, DocMethod, HandleCustomInterpreterDirectives))]
pub fn derive_programming_language_interface(input: TokenStream) -> TokenStream {
@@ -112,6 +172,8 @@ pub fn derive_programming_language_interface(input: TokenStream) -> TokenStream
}
});
let pass_chain = generate_pass_chain(pass_idents.collect());
let tokens = quote! {
use schala_repl::PassDescriptor;
impl ProgrammingLanguageInterface for #name {
@@ -122,8 +184,7 @@ pub fn derive_programming_language_interface(input: TokenStream) -> TokenStream
#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)
#pass_chain
}
fn get_passes(&self) -> Vec<PassDescriptor> {
vec![ #(#pass_descriptors),* ]

View File

@@ -4,8 +4,8 @@ version = "0.1.0"
authors = ["greg <greg.shuflin@protonmail.com>"]
[dependencies]
llvm-sys = "*"
take_mut = "0.1.3"
llvm-sys = "70.0.2"
take_mut = "0.2.2"
itertools = "0.5.8"
getopts = "*"
lazy_static = "0.2.8"
@@ -14,13 +14,14 @@ 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"
rocket = "0.4.0"
rocket_contrib = "0.4.0"
phf = "0.7.12"
includedir = "0.2.0"
linefeed = "0.5.0"
regex = "0.2"
cursive = "0.10"
ncurses = "5.98.0"
[build-dependencies]
includedir_codegen = "0.2.0"

View File

@@ -3,8 +3,6 @@ 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,
@@ -173,61 +171,3 @@ pub trait ProgrammingLanguageInterface {
None
}
}
/* 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, $eval_options:expr; $($pass:path), *) => {
|text_input| {
let mut comp = UnfinishedComputation::default();
pass_chain_helper! { ($state, comp, $eval_options); text_input $(, $pass)* }
}
};
}
#[macro_export]
macro_rules! pass_chain_helper {
(($state:expr, $comp:expr, $eval_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 = $eval_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)
};
if $eval_options.debug_timing {
$comp.durations.push(duration);
}
match output {
Ok(result) => pass_chain_helper! { ($state, $comp, $eval_options); result $(, $rest)* },
Err(err) => {
$comp.output(Err(format!("Pass {} failed with {:?}", pass_name, err)))
}
}
}
};
// Done
(($state:expr, $comp:expr, $eval_options:expr); $final_output:expr) => {
{
let final_output: FinishedComputation = $comp.finish(Ok($final_output));
final_output
}
};
}

View File

@@ -1,15 +1,17 @@
#![feature(link_args)]
#![feature(slice_patterns, box_patterns, box_syntax)]
#![feature(slice_patterns, box_patterns, box_syntax, proc_macro_hygiene, decl_macro)]
#![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate getopts;
extern crate linefeed;
extern crate itertools;
extern crate colored;
extern crate ncurses;
extern crate cursive;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate rocket;
extern crate rocket_contrib;
extern crate includedir;
@@ -21,16 +23,16 @@ use std::io::Read;
use std::process::exit;
use std::default::Default;
mod new_repl;
mod repl;
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,
pub use language::{ProgrammingLanguageInterface, EvalOptions,
ExecutionMethod, TraceArtifact, FinishedComputation, UnfinishedComputation, PassDebugOptionsDescriptor, PassDescriptor};
pub type PLIGenerator = Box<Fn() -> Box<ProgrammingLanguageInterface> + Send + Sync>;
@@ -81,8 +83,11 @@ pub fn repl_main(generators: Vec<PLIGenerator>) {
match option_matches.free[..] {
[] | [_] => {
/*
let mut repl = repl::Repl::new(languages, initial_index);
repl.run();
*/
new_repl::run();
}
[_, ref filename, _..] => {
@@ -129,54 +134,6 @@ fn run_noninteractive(filename: &str, languages: Vec<Box<ProgrammingLanguageInte
}
}
/*
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",

View File

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

View File

@@ -0,0 +1,11 @@
use cursive::Cursive;
use cursive::views::{Dialog, TextView};
pub fn run() {
println!("YOLO");
let mut siv = Cursive::default();
siv.add_layer(Dialog::around(TextView::new("FUCKO"))
.title("YOLO SWAGGGGG")
.button("exit", |s| { s.quit() }));
siv.run();
}

View File

@@ -24,14 +24,14 @@ pub struct Repl {
impl Repl {
pub 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 current_language_index = 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,
languages,
current_language_index,
interpreter_directive_sigil: ':',
line_reader
}

View File

@@ -2,7 +2,7 @@ use rocket;
use rocket::State;
use rocket::response::Content;
use rocket::http::ContentType;
use rocket_contrib::Json;
use rocket_contrib::json::Json;
use language::{ProgrammingLanguageInterface, EvalOptions};
use WEBFILES;
use ::PLIGenerator;