Compare commits
56 Commits
pattern
...
failure_st
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7baf065fb | ||
|
|
6b42f8b8de | ||
|
|
d9e67a6341 | ||
|
|
7de536ade0 | ||
|
|
f62b4c6906 | ||
|
|
4679a9fc7f | ||
|
|
c25354b2c7 | ||
|
|
5f8b842bf2 | ||
|
|
fef66e345b | ||
|
|
e57d33eae7 | ||
|
|
dca9ad06c3 | ||
|
|
354148c5ba | ||
|
|
6219a06d6f | ||
|
|
3b20b40eb7 | ||
|
|
4ecf63c54d | ||
|
|
3d00667caf | ||
|
|
4b9c7e38dd | ||
|
|
03317233c6 | ||
|
|
dff204069f | ||
|
|
f2282f0101 | ||
|
|
40ccea8c05 | ||
|
|
cae6f2f768 | ||
|
|
1be6991f55 | ||
|
|
1b60bd38ff | ||
|
|
3b20b9e209 | ||
|
|
de0e150536 | ||
|
|
baf51fb147 | ||
|
|
dc9e493fa1 | ||
|
|
d57a8045a9 | ||
|
|
50d5176b45 | ||
|
|
501eaeee87 | ||
|
|
8619c94217 | ||
|
|
fc7c86be1a | ||
|
|
77e0d639c2 | ||
|
|
9927a6b1fd | ||
|
|
e8dfc2be34 | ||
|
|
abe2db25b2 | ||
|
|
a99a5f10a4 | ||
|
|
c52bc65bb9 | ||
|
|
819c422cee | ||
|
|
1c11fec803 | ||
|
|
a5c3c383dc | ||
|
|
76046b134a | ||
|
|
c24223f28e | ||
|
|
79e02b0999 | ||
|
|
80eb703f5e | ||
|
|
4fccff5e27 | ||
|
|
e934d7bdc5 | ||
|
|
de199e785a | ||
|
|
81ac918a59 | ||
|
|
5d4505241a | ||
|
|
f67793308e | ||
|
|
693766fa59 | ||
|
|
f9f29dd0dd | ||
|
|
3c1823510f | ||
|
|
92078ef7d8 |
@@ -6,11 +6,12 @@ authors = ["greg <greg.shuflin@protonmail.com>"]
|
||||
[dependencies]
|
||||
|
||||
schala-repl = { path = "schala-repl" }
|
||||
schala-codegen = { path = "schala-codegen" }
|
||||
schala-repl-codegen = { path = "schala-repl-codegen" }
|
||||
maaru-lang = { path = "maaru" }
|
||||
rukka-lang = { path = "rukka" }
|
||||
robo-lang = { path = "robo" }
|
||||
schala-lang = { path = "schala-lang" }
|
||||
schala-lang = { path = "schala-lang/language" }
|
||||
schala-lang-codegen = { path = "schala-lang/codegen" }
|
||||
|
||||
[build-dependencies]
|
||||
includedir_codegen = "0.2.0"
|
||||
|
||||
31
Grammar
31
Grammar
@@ -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>
|
||||
|
||||
|
||||
12
schala-lang/codegen/Cargo.toml
Normal file
12
schala-lang/codegen/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "schala-lang-codegen"
|
||||
version = "0.1.0"
|
||||
authors = ["greg <greg.shuflin@protonmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "0.15.12", features = ["full", "extra-traits", "fold"] }
|
||||
quote = "0.6.8"
|
||||
54
schala-lang/codegen/src/lib.rs
Normal file
54
schala-lang/codegen/src/lib.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
#![feature(box_patterns)]
|
||||
#![recursion_limit="128"]
|
||||
extern crate proc_macro;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
|
||||
use self::proc_macro::TokenStream;
|
||||
use self::syn::fold::Fold;
|
||||
|
||||
struct RecursiveDescentFn {
|
||||
}
|
||||
|
||||
impl Fold for RecursiveDescentFn {
|
||||
fn fold_item_fn(&mut self, mut i: syn::ItemFn) -> syn::ItemFn {
|
||||
let box block = i.block;
|
||||
let ref ident = i.ident;
|
||||
|
||||
let new_block: syn::Block = parse_quote! {
|
||||
{
|
||||
let next_token = self.peek_with_token_offset();
|
||||
let record = ParseRecord {
|
||||
production_name: stringify!(#ident).to_string(),
|
||||
next_token: format!("{}", next_token.to_string_with_metadata()),
|
||||
level: self.parse_level,
|
||||
};
|
||||
self.parse_level += 1;
|
||||
self.parse_record.push(record);
|
||||
let result = { #block };
|
||||
|
||||
if self.parse_level != 0 {
|
||||
self.parse_level -= 1;
|
||||
}
|
||||
match result {
|
||||
Err(ParseError { token: None, msg }) =>
|
||||
Err(ParseError { token: Some(next_token), msg }),
|
||||
_ => result
|
||||
}
|
||||
}
|
||||
};
|
||||
i.block = Box::new(new_block);
|
||||
i
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
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 {};
|
||||
let output = folder.fold_item_fn(input);
|
||||
TokenStream::from(quote!(#output))
|
||||
}
|
||||
@@ -8,6 +8,9 @@ itertools = "0.5.8"
|
||||
take_mut = "0.1.3"
|
||||
maplit = "*"
|
||||
lazy_static = "0.2.8"
|
||||
failure = "0.1.2"
|
||||
|
||||
schala-repl = { path = "../schala-repl" }
|
||||
schala-codegen = { path = "../schala-codegen" }
|
||||
|
||||
schala-lang-codegen = { path = "../codegen" }
|
||||
schala-repl = { path = "../../schala-repl" }
|
||||
schala-repl-codegen = { path = "../../schala-repl-codegen" }
|
||||
@@ -12,10 +12,9 @@ pub enum Statement {
|
||||
}
|
||||
|
||||
pub type Block = Vec<Statement>;
|
||||
|
||||
pub type ParamName = Rc<String>;
|
||||
pub type InterfaceName = Rc<String>; //should be a singleton I think??
|
||||
pub type FormalParam = (ParamName, Option<TypeName>);
|
||||
pub type FormalParam = (ParamName, Option<TypeIdentifier>);
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Declaration {
|
||||
@@ -33,7 +32,7 @@ pub enum Declaration {
|
||||
expr: Expression,
|
||||
},
|
||||
Impl {
|
||||
type_name: TypeName,
|
||||
type_name: TypeIdentifier,
|
||||
interface_name: Option<InterfaceName>,
|
||||
block: Vec<Declaration>,
|
||||
},
|
||||
@@ -48,7 +47,7 @@ pub struct Signature {
|
||||
pub name: Rc<String>,
|
||||
pub operator: bool,
|
||||
pub params: Vec<FormalParam>,
|
||||
pub type_anno: Option<TypeName>,
|
||||
pub type_anno: Option<TypeIdentifier>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
@@ -57,23 +56,23 @@ pub struct TypeBody(pub Vec<Variant>);
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Variant {
|
||||
UnitStruct(Rc<String>),
|
||||
TupleStruct(Rc<String>, Vec<TypeName>),
|
||||
Record(Rc<String>, Vec<(Rc<String>, TypeName)>),
|
||||
TupleStruct(Rc<String>, Vec<TypeIdentifier>),
|
||||
Record(Rc<String>, Vec<(Rc<String>, TypeIdentifier)>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Expression(pub ExpressionType, pub Option<TypeName>);
|
||||
pub struct Expression(pub ExpressionType, pub Option<TypeIdentifier>);
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum TypeName {
|
||||
Tuple(Vec<TypeName>),
|
||||
pub enum TypeIdentifier {
|
||||
Tuple(Vec<TypeIdentifier>),
|
||||
Singleton(TypeSingletonName)
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct TypeSingletonName {
|
||||
pub name: Rc<String>,
|
||||
pub params: Vec<TypeName>,
|
||||
pub params: Vec<TypeIdentifier>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
@@ -79,6 +79,15 @@ impl BinOp {
|
||||
default
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn get_precedence(&self) -> i32 {
|
||||
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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
@@ -7,7 +7,7 @@ use std::io;
|
||||
use itertools::Itertools;
|
||||
|
||||
use util::ScopeStack;
|
||||
use reduced_ast::{ReducedAST, Stmt, Expr, Lit, Func};
|
||||
use reduced_ast::{ReducedAST, Stmt, Expr, Lit, Func, Alternative};
|
||||
use symbol_table::{SymbolSpec, Symbol, SymbolTable};
|
||||
|
||||
pub struct State<'a> {
|
||||
@@ -106,7 +106,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 {
|
||||
@@ -181,11 +181,7 @@ impl<'a> State<'a> {
|
||||
obj @ Node::PrimObject { .. } => Ok(obj),
|
||||
Node::Expr(expr) => match expr {
|
||||
literal @ Lit(_) => Ok(Node::Expr(literal)),
|
||||
Call { box f, args } => match self.expression(Node::Expr(f))? {
|
||||
Node::Expr(Constructor { type_name, name, tag, arity }) => self.apply_data_constructor(type_name, name, tag, arity, args),
|
||||
Node::Expr(Func(f)) => self.apply_function(f, args),
|
||||
other => return Err(format!("Tried to call {:?} which is not a function or data constructor", other)),
|
||||
},
|
||||
Call { box f, args } => self.call_expression(f, args),
|
||||
Val(v) => self.value(v),
|
||||
Constructor { arity, ref name, tag, .. } if arity == 0 => Ok(Node::PrimObject { name: name.clone(), tag, items: vec![] }),
|
||||
constructor @ Constructor { .. } => Ok(Node::Expr(constructor)),
|
||||
@@ -195,51 +191,24 @@ impl<'a> State<'a> {
|
||||
Ok(Node::PrimTuple { items: nodes })
|
||||
},
|
||||
Conditional { box cond, then_clause, else_clause } => self.conditional(cond, then_clause, else_clause),
|
||||
Assign { box val, box expr } => {
|
||||
let name = match val {
|
||||
Expr::Val(name) => name,
|
||||
_ => return Err(format!("Trying to assign to a non-value")),
|
||||
};
|
||||
|
||||
let constant = match self.values.lookup(&name) {
|
||||
None => return Err(format!("{} is undefined", name)),
|
||||
Some(ValueEntry::Binding { constant, .. }) => constant.clone(),
|
||||
};
|
||||
if constant {
|
||||
return Err(format!("trying to update {}, a non-mutable binding", name));
|
||||
}
|
||||
let val = self.expression(Node::Expr(expr))?;
|
||||
self.values.insert(name.clone(), ValueEntry::Binding { constant: false, val });
|
||||
Ok(Node::Expr(Expr::Unit))
|
||||
},
|
||||
Assign { box val, box expr } => self.assign_expression(val, expr),
|
||||
Unit => Ok(Node::Expr(Unit)),
|
||||
CaseMatch { box cond, alternatives } => match self.expression(Node::Expr(cond))? {
|
||||
Node::PrimObject { name, tag, items } => {
|
||||
for alt in alternatives {
|
||||
if alt.tag.map(|t| t == tag).unwrap_or(true) {
|
||||
let mut inner_state = State {
|
||||
values: self.values.new_scope(None),
|
||||
symbol_table_handle: self.symbol_table_handle.clone(),
|
||||
};
|
||||
for (bound_var, val) in alt.bound_vars.iter().zip(items.iter()) {
|
||||
if let Some(bv) = bound_var.as_ref() {
|
||||
inner_state.values.insert(bv.clone(), ValueEntry::Binding { constant: true, val: val.clone() });
|
||||
}
|
||||
}
|
||||
return inner_state.block(alt.item)
|
||||
}
|
||||
}
|
||||
return Err(format!("No matches found"));
|
||||
},
|
||||
Node::PrimTuple { .. } => Err(format!("Tuples don't work")),
|
||||
Node::Expr(e) => Err(format!("Exprs don't work {:?}", e))
|
||||
},
|
||||
CaseMatch { box cond, alternatives } => self.case_match_expression(cond, alternatives),
|
||||
UnimplementedSigilValue => Err(format!("Sigil value eval not implemented"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_data_constructor(&mut self, type_name: Rc<String>, name: Rc<String>, tag: usize, arity: usize, args: Vec<Expr>) -> EvalResult<Node> {
|
||||
fn call_expression(&mut self, f: Expr, args: Vec<Expr>) -> EvalResult<Node> {
|
||||
use self::Expr::*;
|
||||
match self.expression(Node::Expr(f))? {
|
||||
Node::Expr(Constructor { type_name, name, tag, arity }) => self.apply_data_constructor(type_name, name, tag, arity, args),
|
||||
Node::Expr(Func(f)) => self.apply_function(f, args),
|
||||
other => return Err(format!("Tried to call {:?} which is not a function or data constructor", other)),
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
@@ -364,6 +333,71 @@ impl<'a> State<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
fn assign_expression(&mut self, val: Expr, expr: Expr) -> EvalResult<Node> {
|
||||
let name = match val {
|
||||
Expr::Val(name) => name,
|
||||
_ => return Err(format!("Trying to assign to a non-value")),
|
||||
};
|
||||
|
||||
let constant = match self.values.lookup(&name) {
|
||||
None => return Err(format!("Constant {} is undefined", name)),
|
||||
Some(ValueEntry::Binding { constant, .. }) => constant.clone(),
|
||||
};
|
||||
if constant {
|
||||
return Err(format!("trying to update {}, a non-mutable binding", name));
|
||||
}
|
||||
let val = self.expression(Node::Expr(expr))?;
|
||||
self.values.insert(name.clone(), ValueEntry::Binding { constant: false, val });
|
||||
Ok(Node::Expr(Expr::Unit))
|
||||
}
|
||||
|
||||
fn case_match_expression(&mut self, cond: Expr, alternatives: Vec<Alternative>) -> EvalResult<Node> {
|
||||
match self.expression(Node::Expr(cond))? {
|
||||
Node::PrimObject { 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 value(&mut self, name: Rc<String>) -> EvalResult<Node> {
|
||||
use self::ValueEntry::*;
|
||||
use self::Func::*;
|
||||
@@ -402,17 +436,24 @@ impl<'a> State<'a> {
|
||||
mod eval_tests {
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use tokenizing::{Token, tokenize};
|
||||
use ::parsing::ParseResult;
|
||||
use ::ast::AST;
|
||||
use symbol_table::SymbolTable;
|
||||
use tokenizing::tokenize;
|
||||
use parsing::parse;
|
||||
use eval::State;
|
||||
|
||||
fn parse(tokens: Vec<Token>) -> ParseResult<AST> {
|
||||
let mut parser = ::parsing::Parser::new(tokens);
|
||||
parser.parse()
|
||||
}
|
||||
|
||||
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();
|
||||
let ast = parse(tokenize($string)).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);
|
||||
@@ -421,7 +462,7 @@ mod eval_tests {
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! fresh_env {
|
||||
macro_rules! test_in_fresh_env {
|
||||
($string:expr, $correct:expr) => {
|
||||
{
|
||||
let all_output = all_output!($string);
|
||||
@@ -433,16 +474,16 @@ mod eval_tests {
|
||||
|
||||
#[test]
|
||||
fn test_basic_eval() {
|
||||
fresh_env!("1 + 2", "3");
|
||||
fresh_env!("let mut a = 1; a = 2", "Unit");
|
||||
fresh_env!("let mut a = 1; a = 2; a", "2");
|
||||
fresh_env!(r#"("a", 1 + 2)"#, r#"("a", 3)"#);
|
||||
test_in_fresh_env!("1 + 2", "3");
|
||||
test_in_fresh_env!("let mut a = 1; a = 2", "Unit");
|
||||
test_in_fresh_env!("let mut a = 1; a = 2; a", "2");
|
||||
test_in_fresh_env!(r#"("a", 1 + 2)"#, r#"("a", 3)"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_eval() {
|
||||
fresh_env!("fn oi(x) { x + 1 }; oi(4)", "5");
|
||||
fresh_env!("fn oi(x) { x + 1 }; oi(1+2)", "4");
|
||||
test_in_fresh_env!("fn oi(x) { x + 1 }; oi(4)", "5");
|
||||
test_in_fresh_env!("fn oi(x) { x + 1 }; oi(1+2)", "4");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -455,7 +496,7 @@ mod eval_tests {
|
||||
}
|
||||
haha()
|
||||
"#;
|
||||
fresh_env!(scope_ok, "10");
|
||||
test_in_fresh_env!(scope_ok, "10");
|
||||
let scope_ok = r#"
|
||||
let a = 20
|
||||
fn haha() {
|
||||
@@ -464,19 +505,80 @@ mod eval_tests {
|
||||
}
|
||||
a
|
||||
"#;
|
||||
fresh_env!(scope_ok, "20");
|
||||
test_in_fresh_env!(scope_ok, "20");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_patterns() {
|
||||
fn if_is_patterns() {
|
||||
let source = r#"
|
||||
type Option<T> = Some(T) | None
|
||||
let x = Some(9); if x is Some(q) then { q } else { 0 }"#;
|
||||
fresh_env!(source, "9");
|
||||
test_in_fresh_env!(source, "9");
|
||||
|
||||
let source = r#"
|
||||
type Option<T> = Some(T) | None
|
||||
let x = None; if x is Some(q) then { q } else { 0 }"#;
|
||||
fresh_env!(source, "0");
|
||||
test_in_fresh_env!(source, "0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_if_matching() {
|
||||
let source = r#"
|
||||
type Option<T> = Some(T) | None
|
||||
let a = None
|
||||
if a { is None -> 4, is Some(x) -> x }
|
||||
"#;
|
||||
test_in_fresh_env!(source, "4");
|
||||
|
||||
let source = r#"
|
||||
type Option<T> = Some(T) | None
|
||||
let a = Some(99)
|
||||
if a { is None -> 4, is Some(x) -> x }
|
||||
"#;
|
||||
test_in_fresh_env!(source, "99");
|
||||
|
||||
let source = r#"
|
||||
let a = 10
|
||||
if a { is 10 -> "x", is 4 -> "y" }
|
||||
"#;
|
||||
test_in_fresh_env!(source, "\"x\"");
|
||||
|
||||
let source = r#"
|
||||
let a = 10
|
||||
if a { is 15 -> "x", is 10 -> "y" }
|
||||
"#;
|
||||
test_in_fresh_env!(source, "\"y\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boolean_pattern() {
|
||||
let source = r#"
|
||||
let a = true
|
||||
if a {
|
||||
is true -> "x",
|
||||
is false -> "y"
|
||||
}
|
||||
"#;
|
||||
test_in_fresh_env!(source, "\"x\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boolean_pattern_2() {
|
||||
let source = r#"
|
||||
let a = false
|
||||
if a { is true -> "x", is false -> "y" }
|
||||
"#;
|
||||
test_in_fresh_env!(source, "\"y\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignore_pattern() {
|
||||
let source = r#"
|
||||
type Option<T> = Some(T) | None
|
||||
if Some(10) {
|
||||
is _ -> "hella"
|
||||
}
|
||||
"#;
|
||||
test_in_fresh_env!(source, "\"hella\"");
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#![feature(trace_macros)]
|
||||
#![feature(custom_attribute)]
|
||||
#![feature(unrestricted_attribute_tokens)]
|
||||
#![feature(slice_patterns, box_patterns, box_syntax)]
|
||||
#![feature(proc_macro)]
|
||||
extern crate itertools;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
@@ -9,7 +10,9 @@ extern crate maplit;
|
||||
#[macro_use]
|
||||
extern crate schala_repl;
|
||||
#[macro_use]
|
||||
extern crate schala_codegen;
|
||||
extern crate schala_repl_codegen;
|
||||
#[macro_use]
|
||||
extern crate schala_lang_codegen;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
@@ -36,20 +39,31 @@ mod eval;
|
||||
#[LanguageName = "Schala"]
|
||||
#[SourceFileExtension = "schala"]
|
||||
#[PipelineSteps(tokenizing, parsing(compact,expanded,trace), symbol_table, typechecking, ast_reducing, eval)]
|
||||
#[DocMethod = get_doc]
|
||||
#[HandleCustomInterpreterDirectives = handle_custom_interpreter_directives]
|
||||
pub struct Schala {
|
||||
state: eval::State<'static>,
|
||||
symbol_table: Rc<RefCell<symbol_table::SymbolTable>>,
|
||||
type_context: typechecking::TypeContext<'static>,
|
||||
active_parser: Option<parsing::Parser>,
|
||||
}
|
||||
|
||||
impl Schala {
|
||||
fn get_doc(&self, commands: &Vec<&str>) -> Option<String> {
|
||||
Some(format!("Documentation on commands: {:?}", commands))
|
||||
}
|
||||
|
||||
fn handle_custom_interpreter_directives(&mut self, commands: &Vec<&str>) -> Option<String> {
|
||||
Some(format!("Schala-lang command: {:?} not supported", commands.get(0)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Schala {
|
||||
fn new_blank_env() -> Schala {
|
||||
let symbols = Rc::new(RefCell::new(symbol_table::SymbolTable::new()));
|
||||
Schala {
|
||||
symbol_table: symbols.clone(),
|
||||
type_context: typechecking::TypeContext::new(symbols.clone()),
|
||||
state: eval::State::new(symbols),
|
||||
active_parser: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +94,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(handle: &mut Schala, input: Vec<tokenizing::Token>, comp: Option<&mut UnfinishedComputation>) -> Result<ast::AST, String> {
|
||||
use 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());
|
||||
@@ -109,23 +131,8 @@ 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(_handle: &mut Schala, input: ast::AST, _comp: Option<&mut UnfinishedComputation>) -> Result<ast::AST, String> {
|
||||
Ok(input)
|
||||
}
|
||||
|
||||
fn ast_reducing(handle: &mut Schala, input: ast::AST, comp: Option<&mut UnfinishedComputation>) -> Result<reduced_ast::ReducedAST, String> {
|
||||
@@ -34,7 +34,7 @@ pub struct ParseRecord {
|
||||
level: u32,
|
||||
}
|
||||
|
||||
struct Parser {
|
||||
pub struct Parser {
|
||||
tokens: Peekable<IntoIter<Token>>,
|
||||
parse_record: Vec<ParseRecord>,
|
||||
parse_level: u32,
|
||||
@@ -46,9 +46,9 @@ struct ParserRestrictions {
|
||||
}
|
||||
|
||||
impl Parser {
|
||||
fn new(input: Vec<Token>) -> Parser {
|
||||
pub fn new(initial_input: Vec<Token>) -> Parser {
|
||||
Parser {
|
||||
tokens: input.into_iter().peekable(),
|
||||
tokens: initial_input.into_iter().peekable(),
|
||||
parse_record: vec![],
|
||||
parse_level: 0,
|
||||
restrictions: ParserRestrictions { no_struct_literal: false }
|
||||
@@ -64,6 +64,26 @@ impl Parser {
|
||||
fn next(&mut self) -> TokenType {
|
||||
self.tokens.next().map(|ref t| { t.token_type.clone() }).unwrap_or(TokenType::EOF)
|
||||
}
|
||||
|
||||
pub fn parse(&mut self) -> ParseResult<AST> {
|
||||
self.program()
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn parse_with_new_tokens(&mut self, new_tokens: Vec<Token>) -> ParseResult<AST> {
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn format_parse_trace(self) -> Vec<String> {
|
||||
self.parse_record.into_iter().map(|r| {
|
||||
let mut indent = String::new();
|
||||
for _ in 0..r.level {
|
||||
indent.push(' ');
|
||||
}
|
||||
format!("{}Production `{}`, token: {}", indent, r.production_name, r.next_token)
|
||||
}).collect()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! print_token_pattern {
|
||||
@@ -83,32 +103,6 @@ macro_rules! expect {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
macro_rules! parse_method {
|
||||
($name:ident(&mut $self:ident) -> $type:ty $body:block) => {
|
||||
fn $name(&mut $self) -> $type {
|
||||
let next_token = $self.peek_with_token_offset();
|
||||
let record = ParseRecord {
|
||||
production_name: stringify!($name).to_string(),
|
||||
next_token: format!("{}", next_token.to_string_with_metadata()),
|
||||
level: $self.parse_level,
|
||||
};
|
||||
$self.parse_level += 1;
|
||||
$self.parse_record.push(record);
|
||||
let result = { $body };
|
||||
|
||||
if $self.parse_level != 0 {
|
||||
$self.parse_level -= 1;
|
||||
}
|
||||
match result {
|
||||
Err(ParseError { token: None, msg }) =>
|
||||
Err(ParseError { token: Some(next_token), msg }),
|
||||
_ => result
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! delimited {
|
||||
($self:expr, $start:pat, $parse_fn:ident, $( $delim:pat )|+, $end:pat, nonstrict) => {
|
||||
delimited!($self, $start, $parse_fn, $( $delim )|*, $end, false)
|
||||
@@ -266,7 +260,9 @@ enumerator := identifier '<-' expression | identifier '=' expression //TODO add
|
||||
*/
|
||||
|
||||
impl Parser {
|
||||
parse_method!(program(&mut self) -> ParseResult<AST> {
|
||||
//TODO make this a proper public interface
|
||||
#[recursive_descent_method]
|
||||
fn program(&mut self) -> ParseResult<AST> {
|
||||
let mut statements = Vec::new();
|
||||
loop {
|
||||
match self.peek() {
|
||||
@@ -279,9 +275,10 @@ impl Parser {
|
||||
}
|
||||
}
|
||||
Ok(AST(statements))
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(statement(&mut self) -> ParseResult<Statement> {
|
||||
#[recursive_descent_method]
|
||||
fn statement(&mut self) -> ParseResult<Statement> {
|
||||
//TODO handle error recovery here
|
||||
match self.peek() {
|
||||
Keyword(Type) => self.type_declaration().map(|decl| { Statement::Declaration(decl) }),
|
||||
@@ -291,14 +288,16 @@ impl Parser {
|
||||
Keyword(Impl) => self.impl_declaration().map(|decl| Statement::Declaration(decl)),
|
||||
_ => self.expression().map(|expr| { Statement::ExpressionStatement(expr) } ),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(type_declaration(&mut self) -> ParseResult<Declaration> {
|
||||
#[recursive_descent_method]
|
||||
fn type_declaration(&mut self) -> ParseResult<Declaration> {
|
||||
expect!(self, Keyword(Type));
|
||||
self.type_declaration_body()
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(type_declaration_body(&mut self) -> ParseResult<Declaration> {
|
||||
#[recursive_descent_method]
|
||||
fn type_declaration_body(&mut self) -> ParseResult<Declaration> {
|
||||
if let Keyword(Alias) = self.peek() {
|
||||
self.type_alias()
|
||||
} else {
|
||||
@@ -313,17 +312,19 @@ impl Parser {
|
||||
let body = self.type_body()?;
|
||||
Ok(Declaration::TypeDecl { name, body, mutable})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(type_alias(&mut self) -> ParseResult<Declaration> {
|
||||
#[recursive_descent_method]
|
||||
fn type_alias(&mut self) -> ParseResult<Declaration> {
|
||||
expect!(self, Keyword(Alias));
|
||||
let alias = self.identifier()?;
|
||||
expect!(self, Operator(ref c) if **c == "=");
|
||||
let original = self.identifier()?;
|
||||
Ok(Declaration::TypeAlias(alias, original))
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(type_body(&mut self) -> ParseResult<TypeBody> {
|
||||
#[recursive_descent_method]
|
||||
fn type_body(&mut self) -> ParseResult<TypeBody> {
|
||||
let mut variants = Vec::new();
|
||||
variants.push(self.variant_specifier()?);
|
||||
loop {
|
||||
@@ -335,9 +336,10 @@ impl Parser {
|
||||
}
|
||||
}
|
||||
Ok(TypeBody(variants))
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(variant_specifier(&mut self) -> ParseResult<Variant> {
|
||||
#[recursive_descent_method]
|
||||
fn variant_specifier(&mut self) -> ParseResult<Variant> {
|
||||
use self::Variant::*;
|
||||
|
||||
let name = self.identifier()?;
|
||||
@@ -352,16 +354,18 @@ impl Parser {
|
||||
},
|
||||
_ => Ok(UnitStruct(name))
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(typed_identifier(&mut self) -> ParseResult<(Rc<String>, TypeName)> {
|
||||
#[recursive_descent_method]
|
||||
fn typed_identifier(&mut self) -> ParseResult<(Rc<String>, TypeIdentifier)> {
|
||||
let identifier = self.identifier()?;
|
||||
expect!(self, Colon);
|
||||
let type_name = self.type_name()?;
|
||||
Ok((identifier, type_name))
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(func_declaration(&mut self) -> ParseResult<Declaration> {
|
||||
#[recursive_descent_method]
|
||||
fn func_declaration(&mut self) -> ParseResult<Declaration> {
|
||||
let signature = self.func_signature()?;
|
||||
if let LCurlyBrace = self.peek() {
|
||||
let statements = delimited!(self, LCurlyBrace, statement, Newline | Semicolon, RCurlyBrace, nonstrict);
|
||||
@@ -369,9 +373,10 @@ impl Parser {
|
||||
} else {
|
||||
Ok(Declaration::FuncSig(signature))
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(func_signature(&mut self) -> ParseResult<Signature> {
|
||||
#[recursive_descent_method]
|
||||
fn func_signature(&mut self) -> ParseResult<Signature> {
|
||||
expect!(self, Keyword(Func));
|
||||
let (name, operator) = match self.peek() {
|
||||
Operator(s) => {
|
||||
@@ -387,19 +392,20 @@ impl Parser {
|
||||
_ => None,
|
||||
};
|
||||
Ok(Signature { name, operator, params, type_anno })
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(formal_param(&mut self) -> ParseResult<FormalParam> {
|
||||
#[recursive_descent_method]
|
||||
fn formal_param(&mut self) -> ParseResult<FormalParam> {
|
||||
let name = self.identifier()?;
|
||||
let ty = match self.peek() {
|
||||
Colon => Some(self.type_anno()?),
|
||||
_ => None
|
||||
};
|
||||
Ok((name, ty))
|
||||
});
|
||||
|
||||
parse_method!(binding_declaration(&mut self) -> ParseResult<Declaration> {
|
||||
}
|
||||
|
||||
#[recursive_descent_method]
|
||||
fn binding_declaration(&mut self) -> ParseResult<Declaration> {
|
||||
expect!(self, Keyword(Kw::Let));
|
||||
let constant = match self.peek() {
|
||||
Keyword(Kw::Mut) => {
|
||||
@@ -413,20 +419,23 @@ impl Parser {
|
||||
let expr = self.expression()?;
|
||||
|
||||
Ok(Declaration::Binding { name, constant, expr })
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(interface_declaration(&mut self) -> ParseResult<Declaration> {
|
||||
#[recursive_descent_method]
|
||||
fn interface_declaration(&mut self) -> ParseResult<Declaration> {
|
||||
expect!(self, Keyword(Interface));
|
||||
let name = self.identifier()?;
|
||||
let signatures = self.signature_block()?;
|
||||
Ok(Declaration::Interface { name, signatures })
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(signature_block(&mut self) -> ParseResult<Vec<Signature>> {
|
||||
#[recursive_descent_method]
|
||||
fn signature_block(&mut self) -> ParseResult<Vec<Signature>> {
|
||||
Ok(delimited!(self, LCurlyBrace, func_signature, Newline | Semicolon, RCurlyBrace, nonstrict))
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(impl_declaration(&mut self) -> ParseResult<Declaration> {
|
||||
#[recursive_descent_method]
|
||||
fn impl_declaration(&mut self) -> ParseResult<Declaration> {
|
||||
expect!(self, Keyword(Impl));
|
||||
let first = self.type_name()?;
|
||||
let second = if let Keyword(For) = self.peek() {
|
||||
@@ -441,7 +450,7 @@ impl Parser {
|
||||
let result = match (first, second) {
|
||||
(first, Some(second)) => {
|
||||
match first {
|
||||
TypeName::Singleton(TypeSingletonName { ref name, ref params }) if params.len() == 0 =>
|
||||
TypeIdentifier::Singleton(TypeSingletonName { ref name, ref params }) if params.len() == 0 =>
|
||||
Declaration::Impl { type_name: second, interface_name: Some(name.clone()), block },
|
||||
_ => return ParseError::new(&format!("Invalid name for an interface")),
|
||||
}
|
||||
@@ -449,13 +458,15 @@ impl Parser {
|
||||
(first, None) => Declaration::Impl { type_name: first, interface_name: None, block }
|
||||
};
|
||||
Ok(result)
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(decl_block(&mut self) -> ParseResult<Vec<Declaration>> {
|
||||
#[recursive_descent_method]
|
||||
fn decl_block(&mut self) -> ParseResult<Vec<Declaration>> {
|
||||
Ok(delimited!(self, LCurlyBrace, func_declaration, Newline | Semicolon, RCurlyBrace, nonstrict))
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(expression(&mut self) -> ParseResult<Expression> {
|
||||
#[recursive_descent_method]
|
||||
fn expression(&mut self) -> ParseResult<Expression> {
|
||||
let mut expr_body = self.precedence_expr(BinOp::min_precedence())?;
|
||||
let type_anno = match self.peek() {
|
||||
Colon => Some(self.type_anno()?),
|
||||
@@ -466,22 +477,25 @@ impl Parser {
|
||||
}
|
||||
expr_body.1 = type_anno;
|
||||
Ok(expr_body)
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(type_anno(&mut self) -> ParseResult<TypeName> {
|
||||
#[recursive_descent_method]
|
||||
fn type_anno(&mut self) -> ParseResult<TypeIdentifier> {
|
||||
expect!(self, Colon);
|
||||
self.type_name()
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(type_name(&mut self) -> ParseResult<TypeName> {
|
||||
use self::TypeName::*;
|
||||
#[recursive_descent_method]
|
||||
fn type_name(&mut self) -> ParseResult<TypeIdentifier> {
|
||||
use self::TypeIdentifier::*;
|
||||
Ok(match self.peek() {
|
||||
LParen => Tuple(delimited!(self, LParen, type_name, Comma, RParen)),
|
||||
_ => Singleton(self.type_singleton_name()?),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(type_singleton_name(&mut self) -> ParseResult<TypeSingletonName> {
|
||||
#[recursive_descent_method]
|
||||
fn type_singleton_name(&mut self) -> ParseResult<TypeSingletonName> {
|
||||
Ok(TypeSingletonName {
|
||||
name: self.identifier()?,
|
||||
params: match self.peek() {
|
||||
@@ -489,7 +503,7 @@ impl Parser {
|
||||
_ => vec![],
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// this implements Pratt parsing, see http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
|
||||
fn precedence_expr(&mut self, precedence: i32) -> ParseResult<Expression> {
|
||||
@@ -522,7 +536,8 @@ impl Parser {
|
||||
Ok(lhs)
|
||||
}
|
||||
|
||||
parse_method!(prefix_expr(&mut self) -> ParseResult<Expression> {
|
||||
#[recursive_descent_method]
|
||||
fn prefix_expr(&mut self) -> ParseResult<Expression> {
|
||||
match self.peek() {
|
||||
Operator(ref op) if PrefixOp::is_prefix(&*op) => {
|
||||
let sigil = match self.next() {
|
||||
@@ -536,9 +551,10 @@ impl Parser {
|
||||
},
|
||||
_ => self.call_expr()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(call_expr(&mut self) -> ParseResult<Expression> {
|
||||
#[recursive_descent_method]
|
||||
fn call_expr(&mut self) -> ParseResult<Expression> {
|
||||
let index = self.index_expr()?;
|
||||
Ok(if let LParen = self.peek() {
|
||||
let arguments = delimited!(self, LParen, expression, Comma, RParen);
|
||||
@@ -546,9 +562,10 @@ impl Parser {
|
||||
} else {
|
||||
index
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(index_expr(&mut self) -> ParseResult<Expression> {
|
||||
#[recursive_descent_method]
|
||||
fn index_expr(&mut self) -> ParseResult<Expression> {
|
||||
let primary = self.primary()?;
|
||||
Ok(if let LSquareBracket = self.peek() {
|
||||
let indexers = delimited!(self, LSquareBracket, expression, Comma, RSquareBracket);
|
||||
@@ -559,9 +576,10 @@ impl Parser {
|
||||
} else {
|
||||
primary
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(primary(&mut self) -> ParseResult<Expression> {
|
||||
#[recursive_descent_method]
|
||||
fn primary(&mut self) -> ParseResult<Expression> {
|
||||
match self.peek() {
|
||||
LCurlyBrace => self.curly_brace_expr(),
|
||||
LParen => self.paren_expr(),
|
||||
@@ -572,18 +590,21 @@ impl Parser {
|
||||
Identifier(_) => self.identifier_expr(),
|
||||
_ => self.literal(),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(list_expr(&mut self) -> ParseResult<Expression> {
|
||||
#[recursive_descent_method]
|
||||
fn list_expr(&mut self) -> ParseResult<Expression> {
|
||||
let exprs = delimited!(self, LSquareBracket, expression, Comma, RSquareBracket);
|
||||
Ok(Expression(ExpressionType::ListLiteral(exprs), None))
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(curly_brace_expr(&mut self) -> ParseResult<Expression> {
|
||||
#[recursive_descent_method]
|
||||
fn curly_brace_expr(&mut self) -> ParseResult<Expression> {
|
||||
self.lambda_expr()
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(lambda_expr(&mut self) -> ParseResult<Expression> {
|
||||
#[recursive_descent_method]
|
||||
fn lambda_expr(&mut self) -> ParseResult<Expression> {
|
||||
expect!(self, LCurlyBrace);
|
||||
let params = delimited!(self, Pipe, formal_param, Comma, Pipe);
|
||||
let mut body = Vec::new();
|
||||
@@ -599,9 +620,10 @@ impl Parser {
|
||||
}
|
||||
expect!(self, RCurlyBrace);
|
||||
Ok(Expression(ExpressionType::Lambda { params, body }, None)) //TODO need to handle types somehow
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(paren_expr(&mut self) -> ParseResult<Expression> {
|
||||
#[recursive_descent_method]
|
||||
fn paren_expr(&mut self) -> ParseResult<Expression> {
|
||||
use self::ExpressionType::*;
|
||||
let old_struct_value = self.restrictions.no_struct_literal;
|
||||
self.restrictions.no_struct_literal = false;
|
||||
@@ -615,9 +637,10 @@ impl Parser {
|
||||
};
|
||||
self.restrictions.no_struct_literal = old_struct_value;
|
||||
output
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(identifier_expr(&mut self) -> ParseResult<Expression> {
|
||||
#[recursive_descent_method]
|
||||
fn identifier_expr(&mut self) -> ParseResult<Expression> {
|
||||
use self::ExpressionType::*;
|
||||
let identifier = self.identifier()?;
|
||||
Ok(match self.peek() {
|
||||
@@ -627,20 +650,23 @@ impl Parser {
|
||||
},
|
||||
_ => Expression(Value(identifier), None)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(record_block(&mut self) -> ParseResult<Vec<(Rc<String>, Expression)>> {
|
||||
#[recursive_descent_method]
|
||||
fn record_block(&mut self) -> ParseResult<Vec<(Rc<String>, Expression)>> {
|
||||
Ok(delimited!(self, LCurlyBrace, record_entry, Comma, RCurlyBrace))
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(record_entry(&mut self) -> ParseResult<(Rc<String>, Expression)> {
|
||||
#[recursive_descent_method]
|
||||
fn record_entry(&mut self) -> ParseResult<(Rc<String>, Expression)> {
|
||||
let field_name = self.identifier()?;
|
||||
expect!(self, Colon);
|
||||
let value = self.expression()?;
|
||||
Ok((field_name, value))
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(if_expr(&mut self) -> ParseResult<Expression> {
|
||||
#[recursive_descent_method]
|
||||
fn if_expr(&mut self) -> ParseResult<Expression> {
|
||||
expect!(self, Keyword(Kw::If));
|
||||
let discriminator = Box::new({
|
||||
self.restrictions.no_struct_literal = true;
|
||||
@@ -656,9 +682,10 @@ impl Parser {
|
||||
});
|
||||
|
||||
Ok(Expression(ExpressionType::IfExpression { discriminator, body }, None))
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(discriminator(&mut self) -> ParseResult<Discriminator> {
|
||||
#[recursive_descent_method]
|
||||
fn discriminator(&mut self) -> ParseResult<Discriminator> {
|
||||
let lhs = self.prefix_expr()?;
|
||||
let ref next = self.peek();
|
||||
Ok(if let Some(op) = BinOp::from_sigil_token(next) {
|
||||
@@ -666,46 +693,70 @@ impl Parser {
|
||||
} else {
|
||||
Discriminator::Simple(lhs)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(conditional(&mut self) -> ParseResult<IfExpressionBody> {
|
||||
#[recursive_descent_method]
|
||||
fn conditional(&mut self) -> ParseResult<IfExpressionBody> {
|
||||
expect!(self, Keyword(Kw::Then));
|
||||
let then_clause = self.expr_or_block()?;
|
||||
let else_clause = self.else_clause()?;
|
||||
Ok(IfExpressionBody::SimpleConditional(then_clause, else_clause))
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(simple_pattern_match(&mut self) -> ParseResult<IfExpressionBody> {
|
||||
#[recursive_descent_method]
|
||||
fn simple_pattern_match(&mut self) -> ParseResult<IfExpressionBody> {
|
||||
expect!(self, Keyword(Kw::Is));
|
||||
let pat = self.pattern()?;
|
||||
expect!(self, Keyword(Kw::Then));
|
||||
let then_clause = self.expr_or_block()?;
|
||||
let else_clause = self.else_clause()?;
|
||||
Ok(IfExpressionBody::SimplePatternMatch(pat, then_clause, else_clause))
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(else_clause(&mut self) -> ParseResult<Option<Block>> {
|
||||
#[recursive_descent_method]
|
||||
fn else_clause(&mut self) -> ParseResult<Option<Block>> {
|
||||
Ok(if let Keyword(Kw::Else) = self.peek() {
|
||||
self.next();
|
||||
Some(self.expr_or_block()?)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(guard_block(&mut self) -> ParseResult<IfExpressionBody> {
|
||||
let guards = delimited!(self, LCurlyBrace, guard_arm, Comma, RCurlyBrace);
|
||||
#[recursive_descent_method]
|
||||
fn guard_block(&mut self) -> ParseResult<IfExpressionBody> {
|
||||
//TODO - delimited! isn't sophisticated enough to do thisa
|
||||
//let guards = delimited!(self, LCurlyBrace, guard_arm, Comma, RCurlyBrace);
|
||||
expect!(self, LCurlyBrace);
|
||||
let mut guards = vec![];
|
||||
loop {
|
||||
while let Newline = self.peek() {
|
||||
self.next();
|
||||
}
|
||||
let guard_arm = self.guard_arm()?;
|
||||
guards.push(guard_arm);
|
||||
while let Newline = self.peek() {
|
||||
self.next();
|
||||
}
|
||||
match self.peek() {
|
||||
Comma => {self.next(); continue },
|
||||
_ => break
|
||||
}
|
||||
}
|
||||
expect!(self, RCurlyBrace);
|
||||
Ok(IfExpressionBody::GuardList(guards))
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(guard_arm(&mut self) -> ParseResult<GuardArm> {
|
||||
#[recursive_descent_method]
|
||||
fn guard_arm(&mut self) -> ParseResult<GuardArm> {
|
||||
let guard = self.guard()?;
|
||||
expect!(self, Operator(ref c) if **c == "->");
|
||||
let body = self.expr_or_block()?;
|
||||
Ok(GuardArm { guard, body })
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(guard(&mut self) -> ParseResult<Guard> {
|
||||
#[recursive_descent_method]
|
||||
fn guard(&mut self) -> ParseResult<Guard> {
|
||||
Ok(match self.peek() {
|
||||
Keyword(Kw::Is) => {
|
||||
self.next();
|
||||
@@ -714,26 +765,30 @@ impl Parser {
|
||||
},
|
||||
ref tok if BinOp::from_sigil_token(tok).is_some() => {
|
||||
let op = BinOp::from_sigil_token(&self.next()).unwrap();
|
||||
let Expression(expr, _) = self.expression()?;
|
||||
let precedence = op.get_precedence();
|
||||
let Expression(expr, _) = self.precedence_expr(precedence)?;
|
||||
Guard::HalfExpr(HalfExpr { op: Some(op), expr })
|
||||
},
|
||||
_ => {
|
||||
let Expression(expr, _) = self.expression()?;
|
||||
//TODO - I think there's a better way to do this involving the precedence of ->
|
||||
let Expression(expr, _) = self.prefix_expr()?;
|
||||
Guard::HalfExpr(HalfExpr { op: None, expr })
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(pattern(&mut self) -> ParseResult<Pattern> {
|
||||
#[recursive_descent_method]
|
||||
fn pattern(&mut self) -> ParseResult<Pattern> {
|
||||
if let LParen = self.peek() {
|
||||
let tuple_pattern_variants = delimited!(self, LParen, pattern, Comma, RParen);
|
||||
Ok(Pattern::TuplePattern(tuple_pattern_variants))
|
||||
} else {
|
||||
self.simple_pattern()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(simple_pattern(&mut self) -> ParseResult<Pattern> {
|
||||
#[recursive_descent_method]
|
||||
fn simple_pattern(&mut self) -> ParseResult<Pattern> {
|
||||
Ok(match self.peek() {
|
||||
Identifier(_) => {
|
||||
let id = self.identifier()?;
|
||||
@@ -769,9 +824,10 @@ impl Parser {
|
||||
},
|
||||
other => return ParseError::new(&format!("{:?} is not a valid Pattern", other))
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(signed_number_literal(&mut self) -> ParseResult<Pattern> {
|
||||
#[recursive_descent_method]
|
||||
fn signed_number_literal(&mut self) -> ParseResult<Pattern> {
|
||||
let neg = match self.peek() {
|
||||
Operator(ref op) if **op == "-" => {
|
||||
self.next();
|
||||
@@ -781,9 +837,10 @@ impl Parser {
|
||||
};
|
||||
let Expression(expr_type, _) = self.number_literal()?;
|
||||
Ok(Pattern::Literal(PatternLiteral::NumPattern { neg, num: expr_type }))
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(record_pattern_entry(&mut self) -> ParseResult<(Rc<String>, Pattern)> {
|
||||
#[recursive_descent_method]
|
||||
fn record_pattern_entry(&mut self) -> ParseResult<(Rc<String>, Pattern)> {
|
||||
let name = self.identifier()?;
|
||||
Ok(match self.peek() {
|
||||
Colon => {
|
||||
@@ -793,13 +850,15 @@ impl Parser {
|
||||
},
|
||||
_ => (name.clone(), Pattern::Literal(PatternLiteral::StringPattern(name.clone())))
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(block(&mut self) -> ParseResult<Block> {
|
||||
#[recursive_descent_method]
|
||||
fn block(&mut self) -> ParseResult<Block> {
|
||||
Ok(delimited!(self, LCurlyBrace, statement, Newline | Semicolon, RCurlyBrace, nonstrict))
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(expr_or_block(&mut self) -> ParseResult<Block> {
|
||||
#[recursive_descent_method]
|
||||
fn expr_or_block(&mut self) -> ParseResult<Block> {
|
||||
match self.peek() {
|
||||
LCurlyBrace => self.block(),
|
||||
_ => {
|
||||
@@ -807,9 +866,10 @@ impl Parser {
|
||||
Ok(vec![Statement::ExpressionStatement(expr)])
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(while_expr(&mut self) -> ParseResult<Expression> {
|
||||
#[recursive_descent_method]
|
||||
fn while_expr(&mut self) -> ParseResult<Expression> {
|
||||
use self::ExpressionType::*;
|
||||
expect!(self, Keyword(Kw::While));
|
||||
let condition = {
|
||||
@@ -820,16 +880,18 @@ impl Parser {
|
||||
};
|
||||
let body = self.block()?;
|
||||
Ok(Expression(WhileExpression {condition, body}, None))
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(while_cond(&mut self) -> ParseResult<Option<Expression>> {
|
||||
#[recursive_descent_method]
|
||||
fn while_cond(&mut self) -> ParseResult<Option<Expression>> {
|
||||
Ok(match self.peek() {
|
||||
LCurlyBrace => None,
|
||||
_ => Some(self.expression()?),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(for_expr(&mut self) -> ParseResult<Expression> {
|
||||
#[recursive_descent_method]
|
||||
fn for_expr(&mut self) -> ParseResult<Expression> {
|
||||
expect!(self, Keyword(Kw::For));
|
||||
let enumerators = if let LCurlyBrace = self.peek() {
|
||||
delimited!(self, LCurlyBrace, enumerator, Comma | Newline, RCurlyBrace)
|
||||
@@ -844,16 +906,18 @@ impl Parser {
|
||||
};
|
||||
let body = Box::new(self.for_expr_body()?);
|
||||
Ok(Expression(ExpressionType::ForExpression { enumerators, body }, None))
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(enumerator(&mut self) -> ParseResult<Enumerator> {
|
||||
#[recursive_descent_method]
|
||||
fn enumerator(&mut self) -> ParseResult<Enumerator> {
|
||||
let id = self.identifier()?;
|
||||
expect!(self, Operator(ref c) if **c == "<-");
|
||||
let generator = self.expression()?;
|
||||
Ok(Enumerator { id, generator })
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(for_expr_body(&mut self) -> ParseResult<ForBody> {
|
||||
#[recursive_descent_method]
|
||||
fn for_expr_body(&mut self) -> ParseResult<ForBody> {
|
||||
use self::ForBody::*;
|
||||
Ok(match self.peek() {
|
||||
LCurlyBrace => {
|
||||
@@ -866,16 +930,18 @@ impl Parser {
|
||||
},
|
||||
_ => return ParseError::new("for expressions must end in a block or 'return'"),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(identifier(&mut self) -> ParseResult<Rc<String>> {
|
||||
#[recursive_descent_method]
|
||||
fn identifier(&mut self) -> ParseResult<Rc<String>> {
|
||||
match self.next() {
|
||||
Identifier(s) => Ok(s),
|
||||
p => ParseError::new(&format!("Expected an identifier, got {:?}", p)),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(literal(&mut self) -> ParseResult<Expression> {
|
||||
#[recursive_descent_method]
|
||||
fn literal(&mut self) -> ParseResult<Expression> {
|
||||
use self::ExpressionType::*;
|
||||
match self.peek() {
|
||||
DigitGroup(_) | HexLiteral(_) | BinNumberSigil | Period => self.number_literal(),
|
||||
@@ -893,16 +959,18 @@ impl Parser {
|
||||
}
|
||||
e => ParseError::new(&format!("Expected a literal expression, got {:?}", e)),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(number_literal(&mut self) -> ParseResult<Expression> {
|
||||
#[recursive_descent_method]
|
||||
fn number_literal(&mut self) -> ParseResult<Expression> {
|
||||
match self.peek() {
|
||||
HexLiteral(_) | BinNumberSigil => self.int_literal(),
|
||||
_ => self.float_literal(),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(int_literal(&mut self) -> ParseResult<Expression> {
|
||||
#[recursive_descent_method]
|
||||
fn int_literal(&mut self) -> ParseResult<Expression> {
|
||||
use self::ExpressionType::*;
|
||||
match self.next() {
|
||||
BinNumberSigil => {
|
||||
@@ -917,9 +985,10 @@ impl Parser {
|
||||
},
|
||||
_ => return ParseError::new("Expected '0x' or '0b'"),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(float_literal(&mut self) -> ParseResult<Expression> {
|
||||
#[recursive_descent_method]
|
||||
fn float_literal(&mut self) -> ParseResult<Expression> {
|
||||
use self::ExpressionType::*;
|
||||
let mut digits = self.digits()?;
|
||||
if let TokenType::Period = self.peek() {
|
||||
@@ -937,9 +1006,10 @@ impl Parser {
|
||||
Err(e) => ParseError::new(&format!("Integer failed to parse with error: {}", e)),
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
parse_method!(digits(&mut self) -> ParseResult<String> {
|
||||
#[recursive_descent_method]
|
||||
fn digits(&mut self) -> ParseResult<String> {
|
||||
let mut ds = String::new();
|
||||
loop {
|
||||
match self.peek() {
|
||||
@@ -949,7 +1019,7 @@ impl Parser {
|
||||
}
|
||||
}
|
||||
Ok(ds)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_binary(digits: String) -> ParseResult<u64> {
|
||||
@@ -985,43 +1055,35 @@ fn parse_hex(digits: String) -> ParseResult<u64> {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn parse(input: Vec<Token>) -> (Result<AST, ParseError>, Vec<String>) {
|
||||
let mut parser = Parser::new(input);
|
||||
let ast = parser.program();
|
||||
|
||||
let trace = parser.parse_record.into_iter().map(|r| {
|
||||
let mut indent = String::new();
|
||||
for _ in 0..r.level {
|
||||
indent.push(' ');
|
||||
}
|
||||
format!("{}Production `{}`, token: {}", indent, r.production_name, r.next_token)
|
||||
}).collect();
|
||||
(ast, trace)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod parse_tests {
|
||||
use ::std::rc::Rc;
|
||||
use super::{parse, tokenize};
|
||||
use super::tokenize;
|
||||
use super::ParseResult;
|
||||
use builtin::{PrefixOp, BinOp};
|
||||
use ast::{AST, Expression, Statement, IfExpressionBody, Discriminator, Pattern, PatternLiteral, TypeBody, Enumerator, ForBody};
|
||||
use super::Statement::*;
|
||||
use super::Declaration::*;
|
||||
use super::Signature;
|
||||
use super::TypeName::*;
|
||||
use super::TypeIdentifier::*;
|
||||
use super::TypeSingletonName;
|
||||
use super::ExpressionType::*;
|
||||
use super::Variant::*;
|
||||
use super::ForBody::*;
|
||||
|
||||
fn parse(tokens: Vec<::tokenizing::Token>) -> ParseResult<AST> {
|
||||
let mut parser = super::Parser::new(tokens);
|
||||
parser.parse()
|
||||
}
|
||||
|
||||
macro_rules! rc {
|
||||
($string:tt) => { Rc::new(stringify!($string).to_string()) }
|
||||
}
|
||||
macro_rules! parse_test {
|
||||
($string:expr, $correct:expr) => { assert_eq!(parse(tokenize($string)).0.unwrap(), $correct) }
|
||||
($string:expr, $correct:expr) => { assert_eq!(parse(tokenize($string)).unwrap(), $correct) }
|
||||
}
|
||||
macro_rules! parse_error {
|
||||
($string:expr) => { assert!(parse(tokenize($string)).0.is_err()) }
|
||||
($string:expr) => { assert!(parse(tokenize($string)).is_err()) }
|
||||
}
|
||||
macro_rules! val {
|
||||
($var:expr) => { Value(Rc::new($var.to_string())) }
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use ast::{AST, Statement, Expression, Declaration, Discriminator, IfExpressionBody, Pattern, PatternLiteral, Guard, HalfExpr};
|
||||
use ast::{AST, Statement, Expression, ExpressionType, Declaration, Discriminator, IfExpressionBody, Pattern, PatternLiteral, Guard, HalfExpr};
|
||||
use symbol_table::{Symbol, SymbolSpec, SymbolTable};
|
||||
use builtin::{BinOp, PrefixOp};
|
||||
|
||||
@@ -58,11 +58,18 @@ pub enum Expr {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Alternative {
|
||||
pub tag: Option<usize>,
|
||||
pub subpatterns: Vec<Alternative>,
|
||||
pub guard: Option<Expr>,
|
||||
pub bound_vars: Vec<Option<Rc<String>>>, //remember that order matters here
|
||||
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 enum Lit {
|
||||
Nat(u64),
|
||||
@@ -136,9 +143,7 @@ impl Expression {
|
||||
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!()
|
||||
}
|
||||
Discriminator::BinOp(ref _expr, ref _binop) => panic!("Can't yet handle binop discriminators")
|
||||
});
|
||||
match *body {
|
||||
IfExpressionBody::SimpleConditional(ref then_clause, ref else_clause) => {
|
||||
@@ -157,13 +162,8 @@ fn reduce_if_expression(discriminator: &Discriminator, body: &IfExpressionBody,
|
||||
};
|
||||
|
||||
let alternatives = vec![
|
||||
pat.to_alternative(then_clause, symbol_table),
|
||||
Alternative {
|
||||
tag: None,
|
||||
guard: None,
|
||||
bound_vars: vec![],
|
||||
item: else_clause,
|
||||
},
|
||||
pat.to_alternative(&cond, then_clause, symbol_table),
|
||||
Alternative::default(else_clause),
|
||||
];
|
||||
|
||||
Expr::CaseMatch {
|
||||
@@ -172,41 +172,67 @@ fn reduce_if_expression(discriminator: &Discriminator, body: &IfExpressionBody,
|
||||
}
|
||||
},
|
||||
IfExpressionBody::GuardList(ref guard_arms) => {
|
||||
let alternatives = guard_arms.iter().map(|arm| match arm.guard {
|
||||
Guard::Pat(ref p) => {
|
||||
let item = arm.body.iter().map(|expr| expr.reduce(symbol_table)).collect();
|
||||
p.to_alternative(item, symbol_table)
|
||||
},
|
||||
Guard::HalfExpr(HalfExpr { op: _, expr: _ }) => {
|
||||
unimplemented!()
|
||||
let mut alternatives = vec![];
|
||||
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);
|
||||
alternatives.push(alt);
|
||||
},
|
||||
Guard::HalfExpr(HalfExpr { op: _, expr: _ }) => {
|
||||
return Expr::UnimplementedSigilValue
|
||||
}
|
||||
}
|
||||
}).collect();
|
||||
}
|
||||
Expr::CaseMatch { cond, alternatives }
|
||||
}
|
||||
}
|
||||
}
|
||||
/* ig var pat
|
||||
* x is SomeBigOldEnum(_, x, Some(t))
|
||||
*/
|
||||
|
||||
|
||||
impl Pattern {
|
||||
fn to_alternative(&self, item: Vec<Stmt>, symbol_table: &SymbolTable) -> Alternative {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
match self {
|
||||
TupleStruct(name, subpatterns) => {
|
||||
let symbol = symbol_table.values.get(name).expect(&format!("Symbol {} not found", name));
|
||||
let tag = match symbol.spec {
|
||||
SymbolSpec::DataConstructor { index, .. } => index.clone(),
|
||||
_ => panic!("Bad symbol"),
|
||||
};
|
||||
let bound_vars = subpatterns.iter().map(|p| match p {
|
||||
Literal(PatternLiteral::VarPattern(var)) => Some(var.clone()),
|
||||
Ignored => None,
|
||||
_ => None,
|
||||
}).collect();
|
||||
Alternative {
|
||||
tag: Some(tag),
|
||||
guard: None,
|
||||
bound_vars,
|
||||
item,
|
||||
}
|
||||
let symbol = symbol_table.lookup_by_name(name).expect(&format!("Symbol {} not found", name));
|
||||
handle_symbol(symbol, subpatterns, item)
|
||||
},
|
||||
TuplePattern(_items) => {
|
||||
unimplemented!()
|
||||
@@ -214,12 +240,56 @@ impl Pattern {
|
||||
Record(_name, _pairs) => {
|
||||
unimplemented!()
|
||||
},
|
||||
Ignored => unimplemented!(),
|
||||
Ignored => Alternative::default(item),
|
||||
Literal(lit) => match lit {
|
||||
PatternLiteral::NumPattern { neg, num } => unimplemented!(),
|
||||
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) => unimplemented!(),
|
||||
PatternLiteral::VarPattern(_var) => 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
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ impl fmt::Display for SymbolSpec {
|
||||
use self::SymbolSpec::*;
|
||||
match self {
|
||||
Func(type_names) => write!(f, "Func({:?})", type_names),
|
||||
DataConstructor { index, type_name, type_args } => write!(f, "DataConstructor({})({:?} -> {})", index, type_args, type_name),
|
||||
DataConstructor { index, type_name, type_args } => write!(f, "DataConstructor(idx: {})({:?} -> {})", index, type_args, type_name),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ impl SymbolTable {
|
||||
/* note: this adds names for *forward reference* but doesn't actually create any types. solve that problem
|
||||
* later */
|
||||
pub fn add_top_level_symbols(&mut self, ast: &ast::AST) -> Result<(), String> {
|
||||
use self::ast::{Statement, TypeName, Variant, TypeSingletonName, TypeBody};
|
||||
use self::ast::{Statement, TypeIdentifier, Variant, TypeSingletonName, TypeBody};
|
||||
use self::ast::Declaration::*;
|
||||
for statement in ast.0.iter() {
|
||||
if let Statement::Declaration(decl) = statement {
|
||||
@@ -100,8 +100,8 @@ impl SymbolTable {
|
||||
},
|
||||
Variant::TupleStruct(variant_name, tuple_members) => {
|
||||
let type_args = tuple_members.iter().map(|type_name| match type_name {
|
||||
TypeName::Singleton(TypeSingletonName { name, ..}) => name.clone(),
|
||||
TypeName::Tuple(_) => unimplemented!(),
|
||||
TypeIdentifier::Singleton(TypeSingletonName { name, ..}) => name.clone(),
|
||||
TypeIdentifier::Tuple(_) => unimplemented!(),
|
||||
}).collect();
|
||||
let spec = SymbolSpec::DataConstructor {
|
||||
index,
|
||||
3
schala-lang/language/src/typechecking.rs
Normal file
3
schala-lang/language/src/typechecking.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
pub type TypeName = Rc<String>;
|
||||
@@ -27,7 +27,7 @@ impl<'a, T, V> ScopeStack<'a, T, V> where T: Hash + Eq {
|
||||
(Some(value), _) => Some(value),
|
||||
}
|
||||
}
|
||||
//TODO rename new_scope
|
||||
|
||||
pub fn new_scope(&'a self, name: Option<String>) -> ScopeStack<'a, T, V> where T: Hash + Eq {
|
||||
ScopeStack {
|
||||
parent: Some(self),
|
||||
@@ -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()
|
||||
}
|
||||
@@ -1,493 +0,0 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::fmt::Write;
|
||||
/*
|
||||
use std::collections::hash_set::Union;
|
||||
use std::iter::Iterator;
|
||||
use itertools::Itertools;
|
||||
*/
|
||||
|
||||
use ast;
|
||||
use util::ScopeStack;
|
||||
use symbol_table::{SymbolSpec, SymbolTable};
|
||||
|
||||
pub type TypeName = Rc<String>;
|
||||
type TypeResult<T> = Result<T, String>;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
enum Type {
|
||||
Const(TConst),
|
||||
Var(TypeName),
|
||||
Func(Vec<Type>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
enum TConst {
|
||||
Unit,
|
||||
Nat,
|
||||
StringT,
|
||||
//Custom(String)
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
struct Scheme {
|
||||
names: Vec<TypeName>,
|
||||
ty: Type,
|
||||
}
|
||||
|
||||
impl fmt::Display for Scheme {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "∀{:?} . {:?}", self.names, self.ty)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
struct Substitution(HashMap<TypeName, Type>);
|
||||
impl Substitution {
|
||||
fn empty() -> Substitution {
|
||||
Substitution(HashMap::new())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
struct TypeEnv(HashMap<TypeName, Scheme>);
|
||||
|
||||
impl TypeEnv {
|
||||
fn default() -> TypeEnv {
|
||||
TypeEnv(HashMap::new())
|
||||
}
|
||||
|
||||
fn populate_from_symbols(&mut self, symbol_table: &SymbolTable) {
|
||||
for (name, symbol) in symbol_table.values.iter() {
|
||||
if let SymbolSpec::Func(ref type_names) = symbol.spec {
|
||||
let mut ch: char = 'a';
|
||||
let mut names = vec![];
|
||||
for _ in type_names.iter() {
|
||||
names.push(Rc::new(format!("{}", ch)));
|
||||
ch = ((ch as u8) + 1) as char;
|
||||
}
|
||||
|
||||
let sigma = Scheme {
|
||||
names: names.clone(),
|
||||
ty: Type::Func(names.into_iter().map(|n| Type::Var(n)).collect())
|
||||
};
|
||||
self.0.insert(name.clone(), sigma);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TypeContext<'a> {
|
||||
values: ScopeStack<'a, TypeName, Type>,
|
||||
symbol_table_handle: Rc<RefCell<SymbolTable>>,
|
||||
global_env: TypeEnv
|
||||
}
|
||||
|
||||
impl<'a> TypeContext<'a> {
|
||||
pub fn new(symbol_table_handle: Rc<RefCell<SymbolTable>>) -> TypeContext<'static> {
|
||||
TypeContext { values: ScopeStack::new(None), global_env: TypeEnv::default(), symbol_table_handle }
|
||||
}
|
||||
|
||||
pub fn debug_types(&self) -> String {
|
||||
let mut output = format!("Type environment\n");
|
||||
for (name, scheme) in &self.global_env.0 {
|
||||
write!(output, "{} -> {}\n", name, scheme).unwrap();
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
pub fn type_check_ast(&mut self, input: &ast::AST) -> Result<String, String> {
|
||||
let ref symbol_table = self.symbol_table_handle.borrow();
|
||||
self.global_env.populate_from_symbols(symbol_table);
|
||||
let output = self.global_env.infer_block(&input.0)?;
|
||||
Ok(format!("{:?}", output))
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeEnv {
|
||||
fn instantiate(&mut self, sigma: Scheme) -> Type {
|
||||
match sigma {
|
||||
Scheme { ty, .. } => ty,
|
||||
}
|
||||
}
|
||||
fn generate(&mut self, ty: Type) -> Scheme {
|
||||
Scheme {
|
||||
names: vec![], //TODO incomplete
|
||||
ty
|
||||
}
|
||||
}
|
||||
fn infer_block(&mut self, block: &Vec<ast::Statement>) -> TypeResult<Type> {
|
||||
let mut output = Type::Const(TConst::Unit);
|
||||
for statement in block {
|
||||
output = self.infer_statement(statement)?;
|
||||
}
|
||||
Ok(output)
|
||||
}
|
||||
fn infer_statement(&mut self, statement: &ast::Statement) -> TypeResult<Type> {
|
||||
match statement {
|
||||
ast::Statement::ExpressionStatement(expr) => self.infer_expr(expr),
|
||||
ast::Statement::Declaration(decl) => self.infer_decl(decl)
|
||||
}
|
||||
}
|
||||
fn infer_decl(&mut self, decl: &ast::Declaration) -> TypeResult<Type> {
|
||||
use ast::Declaration::*;
|
||||
match decl {
|
||||
Binding { name, expr, .. } => {
|
||||
let ty = self.infer_expr(expr)?;
|
||||
let sigma = self.generate(ty);
|
||||
self.0.insert(name.clone(), sigma);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
Ok(Type::Const(TConst::Unit))
|
||||
}
|
||||
fn infer_expr(&mut self, expr: &ast::Expression) -> TypeResult<Type> {
|
||||
match expr {
|
||||
ast::Expression(expr, Some(anno)) => {
|
||||
self.infer_exprtype(expr)
|
||||
},
|
||||
ast::Expression(expr, None) => {
|
||||
self.infer_exprtype(expr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_exprtype(&mut self, expr: &ast::ExpressionType) -> TypeResult<Type> {
|
||||
use self::TConst::*;
|
||||
use ast::ExpressionType::*;
|
||||
Ok(match expr {
|
||||
NatLiteral(_) => Type::Const(Nat),
|
||||
StringLiteral(_) => Type::Const(StringT),
|
||||
BinExp(op, lhs, rhs) => {
|
||||
|
||||
return Err(format!("NOTDONE"))
|
||||
},
|
||||
Call { f, arguments } => {
|
||||
|
||||
return Err(format!("NOTDONE"))
|
||||
},
|
||||
Value(name) => {
|
||||
let s = match self.0.get(name) {
|
||||
Some(sigma) => sigma.clone(),
|
||||
None => return Err(format!("Unknown variable: {}", name))
|
||||
};
|
||||
self.instantiate(s)
|
||||
},
|
||||
_ => Type::Const(Unit)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* GIANT TODO - use the rust im crate, unless I make this code way less haskell-ish after it's done
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
|
||||
pub type TypeResult<T> = Result<T, String>;
|
||||
*/
|
||||
|
||||
/* TODO this should just check the name against a map, and that map should be pre-populated with
|
||||
* types */
|
||||
/*
|
||||
impl parsing::TypeName {
|
||||
fn to_type(&self) -> TypeResult<Type> {
|
||||
use self::parsing::TypeSingletonName;
|
||||
use self::parsing::TypeName::*;
|
||||
use self::Type::*; use self::TConstOld::*;
|
||||
Ok(match self {
|
||||
Tuple(_) => return Err(format!("Tuples not yet implemented")),
|
||||
Singleton(name) => match name {
|
||||
TypeSingletonName { name, .. } => match &name[..] {
|
||||
/*
|
||||
"Nat" => Const(Nat),
|
||||
"Int" => Const(Int),
|
||||
"Float" => Const(Float),
|
||||
"Bool" => Const(Bool),
|
||||
"String" => Const(StringT),
|
||||
*/
|
||||
n => Const(Custom(n.to_string()))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
impl TypeContext {
|
||||
pub fn type_check_ast(&mut self, ast: &parsing::AST) -> TypeResult<String> {
|
||||
let ref block = ast.0;
|
||||
let mut infer = Infer::default();
|
||||
let env = TypeEnvironment::default();
|
||||
let output = infer.infer_block(block, &env);
|
||||
match output {
|
||||
Ok(s) => Ok(format!("{:?}", s)),
|
||||
Err(s) => Err(format!("Error: {:?}", s))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this is the equivalent of the Haskell Infer monad
|
||||
#[derive(Debug, Default)]
|
||||
struct Infer {
|
||||
_idents: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum InferError {
|
||||
CannotUnify(MonoType, MonoType),
|
||||
OccursCheckFailed(Rc<String>, MonoType),
|
||||
UnknownIdentifier(Rc<String>),
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
type InferResult<T> = Result<T, InferError>;
|
||||
|
||||
|
||||
impl Infer {
|
||||
fn fresh(&mut self) -> MonoType {
|
||||
let i = self._idents;
|
||||
self._idents += 1;
|
||||
let name = Rc::new(format!("{}", ('a' as u8 + 1) as char));
|
||||
MonoType::Var(name)
|
||||
}
|
||||
|
||||
fn unify(&mut self, a: MonoType, b: MonoType) -> InferResult<Substitution> {
|
||||
use self::InferError::*; use self::MonoType::*;
|
||||
Ok(match (a, b) {
|
||||
(Const(ref a), Const(ref b)) if a == b => Substitution::new(),
|
||||
(Var(ref name), ref var) => Substitution::bind_variable(name, var),
|
||||
(ref var, Var(ref name)) => Substitution::bind_variable(name, var),
|
||||
(Function(box a1, box b1), Function(box a2, box b2)) => {
|
||||
let s1 = self.unify(a1, a2)?;
|
||||
let s2 = self.unify(b1.apply_substitution(&s1), b2.apply_substitution(&s1))?;
|
||||
s1.merge(s2)
|
||||
},
|
||||
(a, b) => return Err(CannotUnify(a, b))
|
||||
})
|
||||
}
|
||||
|
||||
fn infer_block(&mut self, block: &Vec<parsing::Statement>, env: &TypeEnvironment) -> InferResult<MonoType> {
|
||||
use self::parsing::Statement;
|
||||
let mut ret = MonoType::Const(TypeConst::Unit);
|
||||
for statement in block.iter() {
|
||||
ret = match statement {
|
||||
Statement::ExpressionStatement(expr) => {
|
||||
let (sub, ty) = self.infer_expr(expr, env)?;
|
||||
//TODO handle substitution monadically
|
||||
|
||||
ty
|
||||
}
|
||||
Statement::Declaration(decl) => MonoType::Const(TypeConst::Unit),
|
||||
}
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn infer_expr(&mut self, expr: &parsing::Expression, env: &TypeEnvironment) -> InferResult<(Substitution, MonoType)> {
|
||||
use self::parsing::Expression;
|
||||
match expr {
|
||||
Expression(e, Some(anno)) => self.infer_annotated_expr(e, anno, env),
|
||||
/*
|
||||
let anno_ty = anno.to_type()?;
|
||||
let ty = self.infer_exprtype(&e)?;
|
||||
self.unify(ty, anno_ty)
|
||||
},
|
||||
*/
|
||||
Expression(e, None) => self.infer_exprtype(e, env)
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_annotated_expr(&mut self, expr: &parsing::ExpressionType, anno: &parsing::TypeName, env: &TypeEnvironment) -> InferResult<(Substitution, MonoType)> {
|
||||
Err(InferError::Custom(format!("exprtype not done: {:?}", expr)))
|
||||
}
|
||||
fn infer_exprtype(&mut self, expr: &parsing::ExpressionType, env: &TypeEnvironment) -> InferResult<(Substitution, MonoType)> {
|
||||
use self::parsing::ExpressionType::*;
|
||||
use self::TypeConst::*;
|
||||
Ok(match expr {
|
||||
NatLiteral(_) => (Substitution::new(), MonoType::Const(Nat)),
|
||||
FloatLiteral(_) => (Substitution::new(), MonoType::Const(Float)),
|
||||
StringLiteral(_) => (Substitution::new(), MonoType::Const(StringT)),
|
||||
BoolLiteral(_) => (Substitution::new(), MonoType::Const(Bool)),
|
||||
Value(name) => match env.lookup(name) {
|
||||
Some(sigma) => {
|
||||
let tau = self.instantiate(&sigma);
|
||||
(Substitution::new(), tau)
|
||||
},
|
||||
None => return Err(InferError::UnknownIdentifier(name.clone())),
|
||||
},
|
||||
e => return Err(InferError::Custom(format!("Type inference for {:?} not done", e)))
|
||||
})
|
||||
}
|
||||
fn instantiate(&mut self, sigma: &PolyType) -> MonoType {
|
||||
let ref ty: MonoType = sigma.1;
|
||||
let mut subst = Substitution::new();
|
||||
|
||||
for name in sigma.0.iter() {
|
||||
let fresh_mvar = self.fresh();
|
||||
let new = Substitution::bind_variable(name, &fresh_mvar);
|
||||
subst = subst.merge(new);
|
||||
}
|
||||
ty.apply_substitution(&subst)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* OLD STUFF DOWN HERE */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
impl TypeContext {
|
||||
fn infer_block(&mut self, statements: &Vec<parsing::Statement>) -> TypeResult<Type> {
|
||||
let mut ret_type = Type::Const(TConst::Unit);
|
||||
for statement in statements {
|
||||
ret_type = self.infer_statement(statement)?;
|
||||
}
|
||||
Ok(ret_type)
|
||||
}
|
||||
|
||||
fn infer_statement(&mut self, statement: &parsing::Statement) -> TypeResult<Type> {
|
||||
use self::parsing::Statement::*;
|
||||
match statement {
|
||||
ExpressionStatement(expr) => self.infer(expr),
|
||||
Declaration(decl) => self.add_declaration(decl),
|
||||
}
|
||||
}
|
||||
fn add_declaration(&mut self, decl: &parsing::Declaration) -> TypeResult<Type> {
|
||||
use self::parsing::Declaration::*;
|
||||
use self::Type::*;
|
||||
match decl {
|
||||
Binding { name, expr, .. } => {
|
||||
let ty = self.infer(expr)?;
|
||||
self.bindings.insert(name.clone(), ty);
|
||||
},
|
||||
_ => return Err(format!("other formats not done"))
|
||||
}
|
||||
Ok(Void)
|
||||
}
|
||||
fn infer(&mut self, expr: &parsing::Expression) -> TypeResult<Type> {
|
||||
use self::parsing::Expression;
|
||||
match expr {
|
||||
Expression(e, Some(anno)) => {
|
||||
let anno_ty = anno.to_type()?;
|
||||
let ty = self.infer_exprtype(&e)?;
|
||||
self.unify(ty, anno_ty)
|
||||
},
|
||||
Expression(e, None) => self.infer_exprtype(e)
|
||||
}
|
||||
}
|
||||
fn infer_exprtype(&mut self, expr: &parsing::ExpressionType) -> TypeResult<Type> {
|
||||
use self::parsing::ExpressionType::*;
|
||||
use self::Type::*; use self::TConst::*;
|
||||
match expr {
|
||||
NatLiteral(_) => Ok(Const(Nat)),
|
||||
FloatLiteral(_) => Ok(Const(Float)),
|
||||
StringLiteral(_) => Ok(Const(StringT)),
|
||||
BoolLiteral(_) => Ok(Const(Bool)),
|
||||
BinExp(op, lhs, rhs) => { /* remember there are both the haskell convention talk and the write you a haskell ways to do this! */
|
||||
match op.get_type()? {
|
||||
Func(box t1, box Func(box t2, box t3)) => {
|
||||
let lhs_ty = self.infer(lhs)?;
|
||||
let rhs_ty = self.infer(rhs)?;
|
||||
self.unify(t1, lhs_ty)?;
|
||||
self.unify(t2, rhs_ty)?;
|
||||
Ok(t3)
|
||||
},
|
||||
other => Err(format!("{:?} is not a binary function type", other))
|
||||
}
|
||||
},
|
||||
PrefixExp(op, expr) => match op.get_type()? {
|
||||
Func(box t1, box t2) => {
|
||||
let expr_ty = self.infer(expr)?;
|
||||
self.unify(t1, expr_ty)?;
|
||||
Ok(t2)
|
||||
},
|
||||
other => Err(format!("{:?} is not a prefix op function type", other))
|
||||
},
|
||||
Value(name) => {
|
||||
match self.bindings.get(name) {
|
||||
Some(ty) => Ok(ty.clone()),
|
||||
None => Err(format!("No binding found for variable: {}", name)),
|
||||
}
|
||||
},
|
||||
Call { f, arguments } => {
|
||||
let mut tf = self.infer(f)?;
|
||||
for arg in arguments.iter() {
|
||||
match tf {
|
||||
Func(box t, box rest) => {
|
||||
let t_arg = self.infer(arg)?;
|
||||
self.unify(t, t_arg)?;
|
||||
tf = rest;
|
||||
},
|
||||
other => return Err(format!("Function call failed to unify; last type: {:?}", other)),
|
||||
}
|
||||
}
|
||||
Ok(tf)
|
||||
},
|
||||
TupleLiteral(expressions) => {
|
||||
let mut types = vec![];
|
||||
for expr in expressions {
|
||||
types.push(self.infer(expr)?);
|
||||
}
|
||||
Ok(Sum(types))
|
||||
},
|
||||
_ => Err(format!("Type not yet implemented"))
|
||||
}
|
||||
}
|
||||
fn unify(&mut self, t1: Type, t2: Type) -> TypeResult<Type> {
|
||||
use self::Type::*;// use self::TConst::*;
|
||||
match (t1, t2) {
|
||||
(Const(ref a), Const(ref b)) if a == b => Ok(Const(a.clone())),
|
||||
(a, b) => Err(format!("Types {:?} and {:?} don't unify", a, b))
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
/*
|
||||
use super::{Type, TConst, TypeContext};
|
||||
use super::Type::*;
|
||||
use super::TConst::*;
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
|
||||
macro_rules! type_test {
|
||||
($input:expr, $correct:expr) => {
|
||||
{
|
||||
let symbol_table = Rc::new(RefCell::new(SymbolTable::new()));
|
||||
let mut tc = TypeContext::new(symbol_table);
|
||||
let ast = ::ast::parse(::tokenizing::tokenize($input)).0.unwrap() ;
|
||||
//tc.add_symbols(&ast);
|
||||
assert_eq!($correct, tc.infer_block(&ast.0).unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_inference() {
|
||||
type_test!("30", Const(Nat));
|
||||
//type_test!("fn x(a: Int): Bool {}; x(1)", TConst(Boolean));
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
[package]
|
||||
name = "schala-codegen"
|
||||
name = "schala-repl-codegen"
|
||||
version = "0.1.0"
|
||||
authors = ["greg <greg.shuflin@protonmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "0.13.1", features = ["full", "extra-traits"] }
|
||||
quote = "0.5"
|
||||
syn = { version = "0.15.6", features = ["full", "extra-traits"] }
|
||||
quote = "0.6.8"
|
||||
proc-macro2 = "0.4.19"
|
||||
schala-repl = { path = "../schala-repl" }
|
||||
|
||||
[lib]
|
||||
@@ -1,24 +1,26 @@
|
||||
#![feature(trace_macros)]
|
||||
#![feature(proc_macro)]
|
||||
extern crate proc_macro;
|
||||
extern crate proc_macro2;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
extern crate syn;
|
||||
|
||||
extern crate schala_repl;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use syn::{Ident, Attribute, DeriveInput};
|
||||
|
||||
fn find_attr_by_name<'a>(name: &str, attrs: &'a Vec<Attribute>) -> Option<&'a Attribute> {
|
||||
attrs.iter().find(|attr| {
|
||||
let first = attr.path.segments.first();
|
||||
let seg: Option<&&syn::PathSegment> = first.as_ref().map(|x| x.value());
|
||||
seg.map(|seg| seg.ident.to_string() == name).unwrap_or(false)
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_attribute_arg_by_name(name: &str, attrs: &Vec<Attribute>) -> Option<String> {
|
||||
use syn::{Meta, Lit, MetaNameValue};
|
||||
attrs.iter().map(|attr| attr.interpret_meta()).find(|meta| {
|
||||
match meta {
|
||||
&Some(Meta::NameValue(MetaNameValue { ident, .. })) if ident.as_ref() == name => true,
|
||||
_ => false
|
||||
}
|
||||
}).and_then(|meta| {
|
||||
match meta {
|
||||
find_attr_by_name(name, attrs)
|
||||
.and_then(|attr| {
|
||||
match attr.interpret_meta() {
|
||||
Some(Meta::NameValue(MetaNameValue { lit: Lit::Str(litstr), .. })) => Some(litstr.value()),
|
||||
_ => None,
|
||||
}
|
||||
@@ -27,22 +29,18 @@ fn extract_attribute_arg_by_name(name: &str, attrs: &Vec<Attribute>) -> Option<S
|
||||
|
||||
fn extract_attribute_list(name: &str, attrs: &Vec<Attribute>) -> Option<Vec<(Ident, Option<Vec<Ident>>)>> {
|
||||
use syn::{Meta, MetaList, NestedMeta};
|
||||
attrs.iter().find(|attr| {
|
||||
match attr.path.segments.iter().nth(0) {
|
||||
Some(segment) if segment.ident.as_ref() == name => true,
|
||||
_ => false
|
||||
}
|
||||
}).and_then(|attr| {
|
||||
find_attr_by_name(name, attrs)
|
||||
.and_then(|attr| {
|
||||
match attr.interpret_meta() {
|
||||
Some(Meta::List(MetaList { nested, .. })) => {
|
||||
Some(nested.iter().map(|nested_meta| match nested_meta {
|
||||
&NestedMeta::Meta(Meta::Word(ident)) => (ident, None),
|
||||
&NestedMeta::Meta(Meta::List(MetaList { ident, nested: ref nested2, .. })) => {
|
||||
&NestedMeta::Meta(Meta::Word(ref ident)) => (ident.clone(), None),
|
||||
&NestedMeta::Meta(Meta::List(MetaList { ref ident, nested: ref nested2, .. })) => {
|
||||
let own_args = nested2.iter().map(|nested_meta2| match nested_meta2 {
|
||||
&NestedMeta::Meta(Meta::Word(ident)) => ident,
|
||||
&NestedMeta::Meta(Meta::Word(ref ident)) => ident.clone(),
|
||||
_ => panic!("Bad format for doubly-nested attribute list")
|
||||
}).collect();
|
||||
(ident, Some(own_args))
|
||||
(ident.clone(), Some(own_args))
|
||||
},
|
||||
_ => panic!("Bad format for nested list")
|
||||
}).collect())
|
||||
@@ -52,7 +50,24 @@ fn extract_attribute_list(name: &str, attrs: &Vec<Attribute>) -> Option<Vec<(Ide
|
||||
})
|
||||
}
|
||||
|
||||
#[proc_macro_derive(ProgrammingLanguageInterface, attributes(LanguageName, SourceFileExtension, PipelineSteps))]
|
||||
fn get_attribute_identifier(attr_name: &str, attrs: &Vec<Attribute>) -> Option<proc_macro2::Ident> {
|
||||
find_attr_by_name(attr_name, attrs).and_then(|attr| {
|
||||
let tts = attr.tts.clone().into_iter().collect::<Vec<_>>();
|
||||
|
||||
if tts.len() == 2 {
|
||||
let ref after_equals: proc_macro2::TokenTree = tts[1];
|
||||
match after_equals {
|
||||
proc_macro2::TokenTree::Ident(ident) => Some(ident.clone()),
|
||||
_ => None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[proc_macro_derive(ProgrammingLanguageInterface,
|
||||
attributes(LanguageName, SourceFileExtension, PipelineSteps, DocMethod, HandleCustomInterpreterDirectives))]
|
||||
pub fn derive_programming_language_interface(input: TokenStream) -> TokenStream {
|
||||
let ast: DeriveInput = syn::parse(input).unwrap();
|
||||
let name = &ast.ident;
|
||||
@@ -61,9 +76,27 @@ pub fn derive_programming_language_interface(input: TokenStream) -> TokenStream
|
||||
let language_name: String = extract_attribute_arg_by_name("LanguageName", attrs).expect("LanguageName is required");
|
||||
let file_ext = extract_attribute_arg_by_name("SourceFileExtension", attrs).expect("SourceFileExtension is required");
|
||||
let passes = extract_attribute_list("PipelineSteps", attrs).expect("PipelineSteps are required");
|
||||
let pass_idents = passes.iter().map(|x| x.0);
|
||||
let pass_idents = passes.iter().map(|x| x.0.clone());
|
||||
|
||||
let get_doc_impl = match get_attribute_identifier("DocMethod", attrs) {
|
||||
None => quote! { },
|
||||
Some(method_name) => quote! {
|
||||
fn get_doc(&self, commands: &Vec<&str>) -> Option<String> {
|
||||
self.#method_name(commands)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let handle_custom_interpreter_directives_impl = match get_attribute_identifier("HandleCustomInterpreterDirectives", attrs) {
|
||||
None => quote! { },
|
||||
Some(method_name) => quote! {
|
||||
fn handle_custom_interpreter_directives(&mut self, commands: &Vec<&str>) -> Option<String> {
|
||||
//println!("If #method_name is &self not &mut self, this runs forever");
|
||||
self.#method_name(commands)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//let pass_names: Vec<String> = passes.iter().map(|pass| pass.0.to_string()).collect();
|
||||
let pass_descriptors = passes.iter().map(|pass| {
|
||||
let name = pass.0.to_string();
|
||||
let opts: Vec<String> = match &pass.1 {
|
||||
@@ -94,9 +127,12 @@ pub fn derive_programming_language_interface(input: TokenStream) -> TokenStream
|
||||
}
|
||||
fn get_passes(&self) -> Vec<PassDescriptor> {
|
||||
vec![ #(#pass_descriptors),* ]
|
||||
//vec![ #(PassDescriptor { name: #pass_names.to_string(), debug_options: vec![] }),* ]
|
||||
}
|
||||
#get_doc_impl
|
||||
#handle_custom_interpreter_directives_impl
|
||||
}
|
||||
};
|
||||
tokens.into()
|
||||
|
||||
let output: TokenStream = tokens.into();
|
||||
output
|
||||
}
|
||||
@@ -9,6 +9,7 @@ pub struct LLVMCodeString(pub String);
|
||||
pub struct EvalOptions {
|
||||
pub execution_method: ExecutionMethod,
|
||||
pub debug_passes: HashMap<String, PassDebugOptionsDescriptor>,
|
||||
pub debug_timing: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, PartialEq)]
|
||||
@@ -55,7 +56,7 @@ impl UnfinishedComputation {
|
||||
FinishedComputation {
|
||||
artifacts: self.artifacts,
|
||||
text_output,
|
||||
durations: self.durations
|
||||
durations: self.durations,
|
||||
}
|
||||
}
|
||||
pub fn output(self, output: Result<String, String>) -> FinishedComputation {
|
||||
@@ -68,6 +69,22 @@ impl UnfinishedComputation {
|
||||
}
|
||||
|
||||
impl FinishedComputation {
|
||||
|
||||
fn get_timing(&self) -> Option<String> {
|
||||
if self.durations.len() != 0 {
|
||||
let mut buf = String::new();
|
||||
write!(&mut buf, "Timing: ").unwrap();
|
||||
for duration in self.durations.iter() {
|
||||
let timing = (duration.as_secs() as f64) + (duration.subsec_nanos() as f64 * 1e-9);
|
||||
write!(&mut buf, "{}s, ", timing).unwrap()
|
||||
}
|
||||
write!(&mut buf, "\n").unwrap();
|
||||
Some(buf)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_repl(&self) -> String {
|
||||
let mut buf = String::new();
|
||||
for (stage, artifact) in self.artifacts.iter() {
|
||||
@@ -77,15 +94,11 @@ impl FinishedComputation {
|
||||
write!(&mut buf, "{}: {}\n", stage, output).unwrap();
|
||||
}
|
||||
|
||||
let debug_timing = true;
|
||||
if debug_timing {
|
||||
write!(&mut buf, "Timing: ").unwrap();
|
||||
for duration in self.durations.iter() {
|
||||
let timing = (duration.as_secs() as f64) + (duration.subsec_nanos() as f64 * 1e-9);
|
||||
write!(&mut buf, "{}s, ", timing).unwrap()
|
||||
}
|
||||
write!(&mut buf, "\n").unwrap();
|
||||
match self.get_timing() {
|
||||
Some(timing) => write!(&mut buf, "{}", timing).unwrap(),
|
||||
None => ()
|
||||
}
|
||||
|
||||
match self.text_output {
|
||||
Ok(ref output) => write!(&mut buf, "{}", output).unwrap(),
|
||||
Err(ref err) => write!(&mut buf, "{} {}", "Error: ".red().bold(), err).unwrap(),
|
||||
@@ -156,6 +169,9 @@ pub trait ProgrammingLanguageInterface {
|
||||
fn custom_interpreter_directives_help(&self) -> String {
|
||||
format!(">> No custom interpreter directives specified <<")
|
||||
}
|
||||
fn get_doc(&self, _commands: &Vec<&str>) -> Option<String> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/* a pass_chain function signature looks like:
|
||||
@@ -166,23 +182,23 @@ pub trait ProgrammingLanguageInterface {
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pass_chain {
|
||||
($state:expr, $options:expr; $($pass:path), *) => {
|
||||
($state:expr, $eval_options:expr; $($pass:path), *) => {
|
||||
|text_input| {
|
||||
let mut comp = UnfinishedComputation::default();
|
||||
pass_chain_helper! { ($state, comp, $options); text_input $(, $pass)* }
|
||||
pass_chain_helper! { ($state, comp, $eval_options); text_input $(, $pass)* }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pass_chain_helper {
|
||||
(($state:expr, $comp:expr, $options:expr); $input:expr, $pass:path $(, $rest:path)*) => {
|
||||
(($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 = $options.debug_passes;
|
||||
let ref debug_map = $eval_options.debug_passes;
|
||||
let debug_handle = match debug_map.get(pass_name) {
|
||||
Some(PassDebugOptionsDescriptor { opts }) => {
|
||||
let ptr = &mut $comp;
|
||||
@@ -196,17 +212,19 @@ macro_rules! pass_chain_helper {
|
||||
let elapsed = start.elapsed();
|
||||
(pass_output, elapsed)
|
||||
};
|
||||
$comp.durations.push(duration);
|
||||
if $eval_options.debug_timing {
|
||||
$comp.durations.push(duration);
|
||||
}
|
||||
match output {
|
||||
Ok(result) => pass_chain_helper! { ($state, $comp, $options); result $(, $rest)* },
|
||||
Err(err) => {
|
||||
$comp.output(Err(format!("Pass {} failed with {:?}", pass_name, err)))
|
||||
Ok(result) => pass_chain_helper! { ($state, $comp, $eval_options); result $(, $rest)* },
|
||||
Err(err) => { //TODO this error type needs to be guaranteed to provide a useable string
|
||||
$comp.output(Err(format!("Pass {} failed:\n{}", pass_name, err)))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
// Done
|
||||
(($state:expr, $comp:expr, $options:expr); $final_output:expr) => {
|
||||
(($state:expr, $comp:expr, $eval_options:expr); $final_output:expr) => {
|
||||
{
|
||||
let final_output: FinishedComputation = $comp.finish(Ok($final_output));
|
||||
final_output
|
||||
|
||||
@@ -17,14 +17,11 @@ extern crate phf;
|
||||
|
||||
use std::path::Path;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::io::Read;
|
||||
use std::process::exit;
|
||||
use std::default::Default;
|
||||
use std::fmt::Write as FmtWrite;
|
||||
|
||||
use colored::*;
|
||||
use itertools::Itertools;
|
||||
|
||||
mod repl;
|
||||
mod language;
|
||||
mod webapp;
|
||||
pub mod llvm_wrap;
|
||||
@@ -84,7 +81,7 @@ pub fn repl_main(generators: Vec<PLIGenerator>) {
|
||||
|
||||
match option_matches.free[..] {
|
||||
[] | [_] => {
|
||||
let mut repl = Repl::new(languages, initial_index);
|
||||
let mut repl = repl::Repl::new(languages, initial_index);
|
||||
repl.run();
|
||||
}
|
||||
[_, ref filename, _..] => {
|
||||
@@ -132,359 +129,6 @@ fn run_noninteractive(filename: &str, languages: Vec<Box<ProgrammingLanguageInte
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum CommandTree {
|
||||
Terminal(String, Option<String>),
|
||||
NonTerminal(String, Vec<CommandTree>, Option<String>),
|
||||
Top(Vec<CommandTree>),
|
||||
}
|
||||
|
||||
impl CommandTree {
|
||||
fn term(s: &str, help: Option<&str>) -> CommandTree {
|
||||
CommandTree::Terminal(s.to_string(), help.map(|x| x.to_string()))
|
||||
}
|
||||
fn get_cmd(&self) -> String {
|
||||
match self {
|
||||
CommandTree::Terminal(s, _) => s.to_string(),
|
||||
CommandTree::NonTerminal(s, _, _) => s.to_string(),
|
||||
CommandTree::Top(_) => "".to_string(),
|
||||
}
|
||||
}
|
||||
fn get_help(&self) -> String {
|
||||
match self {
|
||||
CommandTree::Terminal(_, h) => h.as_ref().map(|h| h.clone()).unwrap_or(format!("")),
|
||||
CommandTree::NonTerminal(_, _, h) => h.as_ref().map(|h| h.clone()).unwrap_or(format!("")),
|
||||
CommandTree::Top(_) => "".to_string(),
|
||||
}
|
||||
}
|
||||
fn get_children(&self) -> Vec<String> {
|
||||
match self {
|
||||
CommandTree::Terminal(_, _) => vec![],
|
||||
CommandTree::NonTerminal(_, children, _) => children.iter().map(|x| x.get_cmd()).collect(),
|
||||
CommandTree::Top(children) => children.iter().map(|x| x.get_cmd()).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TabCompleteHandler {
|
||||
sigil: char,
|
||||
top_level_commands: CommandTree,
|
||||
}
|
||||
|
||||
use linefeed::complete::{Completion, Completer};
|
||||
use linefeed::terminal::Terminal;
|
||||
|
||||
impl TabCompleteHandler {
|
||||
fn new(sigil: char, top_level_commands: CommandTree) -> TabCompleteHandler {
|
||||
TabCompleteHandler {
|
||||
top_level_commands,
|
||||
sigil,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Terminal> Completer<T> for TabCompleteHandler {
|
||||
fn complete(&self, word: &str, prompter: &linefeed::prompter::Prompter<T>, start: usize, _end: usize) -> Option<Vec<Completion>> {
|
||||
let line = prompter.buffer();
|
||||
|
||||
if line.starts_with(&format!("{}", self.sigil)) {
|
||||
let mut words = line[1..(if start == 0 { 1 } else { start })].split_whitespace();
|
||||
let mut completions = Vec::new();
|
||||
let mut command_tree: Option<&CommandTree> = Some(&self.top_level_commands);
|
||||
|
||||
loop {
|
||||
match words.next() {
|
||||
None => {
|
||||
let top = match command_tree {
|
||||
Some(CommandTree::Top(_)) => true,
|
||||
_ => false
|
||||
};
|
||||
let word = if top { word.get(1..).unwrap() } else { word };
|
||||
for cmd in command_tree.map(|x| x.get_children()).unwrap_or(vec![]).into_iter() {
|
||||
if cmd.starts_with(word) {
|
||||
completions.push(Completion {
|
||||
completion: format!("{}{}", if top { ":" } else { "" }, cmd),
|
||||
display: Some(cmd.clone()),
|
||||
suffix: linefeed::complete::Suffix::Some(' ')
|
||||
})
|
||||
}
|
||||
}
|
||||
break;
|
||||
},
|
||||
Some(s) => {
|
||||
let new_ptr: Option<&CommandTree> = command_tree.and_then(|cm| match cm {
|
||||
CommandTree::Top(children) => children.iter().find(|c| c.get_cmd() == s),
|
||||
CommandTree::NonTerminal(_, children, _) => children.iter().find(|c| c.get_cmd() == s),
|
||||
CommandTree::Terminal(_, _) => None,
|
||||
});
|
||||
command_tree = new_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(completions)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Repl {
|
||||
options: EvalOptions,
|
||||
languages: Vec<Box<ProgrammingLanguageInterface>>,
|
||||
current_language_index: usize,
|
||||
interpreter_directive_sigil: char,
|
||||
line_reader: linefeed::interface::Interface<linefeed::terminal::DefaultTerminal>,
|
||||
}
|
||||
|
||||
impl Repl {
|
||||
fn new(languages: Vec<Box<ProgrammingLanguageInterface>>, initial_index: usize) -> Repl {
|
||||
use linefeed::Interface;
|
||||
let i = if initial_index < languages.len() { initial_index } else { 0 };
|
||||
|
||||
let line_reader = Interface::new("schala-repl").unwrap();
|
||||
|
||||
Repl {
|
||||
options: Repl::get_options(),
|
||||
languages: languages,
|
||||
current_language_index: i,
|
||||
interpreter_directive_sigil: ':',
|
||||
line_reader
|
||||
}
|
||||
}
|
||||
|
||||
fn get_cur_language(&self) -> &ProgrammingLanguageInterface {
|
||||
self.languages[self.current_language_index].as_ref()
|
||||
}
|
||||
|
||||
fn get_options() -> EvalOptions {
|
||||
File::open(".schala_repl")
|
||||
.and_then(|mut file| {
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)?;
|
||||
Ok(contents)
|
||||
})
|
||||
.and_then(|contents| {
|
||||
let options: EvalOptions = serde_json::from_str(&contents)?;
|
||||
Ok(options)
|
||||
}).unwrap_or(EvalOptions::default())
|
||||
}
|
||||
|
||||
fn save_options(&self) {
|
||||
let ref options = self.options;
|
||||
let read = File::create(".schala_repl")
|
||||
.and_then(|mut file| {
|
||||
let buf = serde_json::to_string(options).unwrap();
|
||||
file.write_all(buf.as_bytes())
|
||||
});
|
||||
|
||||
if let Err(err) = read {
|
||||
println!("Error saving .schala_repl file {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
fn run(&mut self) {
|
||||
use linefeed::ReadResult;
|
||||
|
||||
println!("Schala MetaInterpreter version {}", VERSION_STRING);
|
||||
println!("Type {}help for help with the REPL", self.interpreter_directive_sigil);
|
||||
|
||||
self.line_reader.load_history(".schala_history").unwrap_or(());
|
||||
|
||||
loop {
|
||||
let language_name = self.languages[self.current_language_index].get_language_name();
|
||||
let directives = self.get_directives();
|
||||
let tab_complete_handler = TabCompleteHandler::new(self.interpreter_directive_sigil, directives);
|
||||
self.line_reader.set_completer(std::sync::Arc::new(tab_complete_handler));
|
||||
|
||||
let prompt_str = format!("{} >> ", language_name);
|
||||
self.line_reader.set_prompt(&prompt_str);
|
||||
|
||||
match self.line_reader.read_line() {
|
||||
Err(e) => {
|
||||
println!("Terminal read error: {}", e);
|
||||
},
|
||||
Ok(ReadResult::Eof) => break,
|
||||
Ok(ReadResult::Signal(_)) => break,
|
||||
Ok(ReadResult::Input(ref input)) => {
|
||||
self.line_reader.add_history_unique(input.to_string());
|
||||
let output = match input.chars().nth(0) {
|
||||
Some(ch) if ch == self.interpreter_directive_sigil => self.handle_interpreter_directive(input),
|
||||
_ => Some(self.input_handler(input)),
|
||||
};
|
||||
if let Some(o) = output {
|
||||
println!("=> {}", o);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.line_reader.save_history(".schala_history").unwrap_or(());
|
||||
self.save_options();
|
||||
println!("Exiting...");
|
||||
}
|
||||
|
||||
fn input_handler(&mut self, input: &str) -> String {
|
||||
let ref mut language = self.languages[self.current_language_index];
|
||||
let interpreter_output = language.execute_pipeline(input, &self.options);
|
||||
interpreter_output.to_repl()
|
||||
}
|
||||
|
||||
fn get_directives(&self) -> CommandTree {
|
||||
let ref passes = self.get_cur_language().get_passes();
|
||||
|
||||
let passes_directives: Vec<CommandTree> = passes.iter()
|
||||
.map(|pass_descriptor| {
|
||||
let name = &pass_descriptor.name;
|
||||
if pass_descriptor.debug_options.len() == 0 {
|
||||
CommandTree::term(name, None)
|
||||
} else {
|
||||
let sub_opts: Vec<CommandTree> = pass_descriptor.debug_options.iter()
|
||||
.map(|o| CommandTree::term(o, None)).collect();
|
||||
CommandTree::NonTerminal(
|
||||
name.clone(),
|
||||
sub_opts,
|
||||
None
|
||||
)
|
||||
}
|
||||
}).collect();
|
||||
|
||||
CommandTree::Top(vec![
|
||||
CommandTree::term("exit", Some("exit the REPL")),
|
||||
CommandTree::term("quit", Some("exit the REPL")),
|
||||
CommandTree::term("help", Some("Print this help message")),
|
||||
CommandTree::NonTerminal(format!("debug"), vec![
|
||||
CommandTree::term("passes", None),
|
||||
CommandTree::NonTerminal(format!("show"), passes_directives.clone(), None),
|
||||
CommandTree::NonTerminal(format!("hide"), passes_directives.clone(), None),
|
||||
], Some(format!("show or hide pass info for a given pass, or display the names of all passes"))),
|
||||
CommandTree::NonTerminal(format!("lang"), vec![
|
||||
CommandTree::term("next", None),
|
||||
CommandTree::term("prev", None),
|
||||
CommandTree::NonTerminal(format!("go"), vec![], None)//TODO
|
||||
], Some(format!("switch between languages, or go directly to a langauge by name"))),
|
||||
])
|
||||
}
|
||||
|
||||
fn handle_interpreter_directive(&mut self, input: &str) -> Option<String> {
|
||||
let mut iter = input.chars();
|
||||
iter.next();
|
||||
let commands: Vec<&str> = iter
|
||||
.as_str()
|
||||
.split_whitespace()
|
||||
.collect();
|
||||
|
||||
let cmd: &str = match commands.get(0).clone() {
|
||||
None => return None,
|
||||
Some(s) => s
|
||||
};
|
||||
|
||||
match cmd {
|
||||
"exit" | "quit" => {
|
||||
self.save_options();
|
||||
exit(0)
|
||||
},
|
||||
"lang" | "language" => match commands.get(1) {
|
||||
Some(&"show") => {
|
||||
let mut buf = String::new();
|
||||
for (i, lang) in self.languages.iter().enumerate() {
|
||||
write!(buf, "{}{}\n", if i == self.current_language_index { "* "} else { "" }, lang.get_language_name()).unwrap();
|
||||
}
|
||||
Some(buf)
|
||||
},
|
||||
Some(&"go") => match commands.get(2) {
|
||||
None => Some(format!("Must specify a language name")),
|
||||
Some(&desired_name) => {
|
||||
for (i, _) in self.languages.iter().enumerate() {
|
||||
let lang_name = self.languages[i].get_language_name();
|
||||
if lang_name.to_lowercase() == desired_name.to_lowercase() {
|
||||
self.current_language_index = i;
|
||||
return Some(format!("Switching to {}", self.languages[self.current_language_index].get_language_name()));
|
||||
}
|
||||
}
|
||||
Some(format!("Language {} not found", desired_name))
|
||||
}
|
||||
},
|
||||
Some(&"next") | Some(&"n") => {
|
||||
self.current_language_index = (self.current_language_index + 1) % self.languages.len();
|
||||
Some(format!("Switching to {}", self.languages[self.current_language_index].get_language_name()))
|
||||
},
|
||||
Some(&"previous") | Some(&"p") | Some(&"prev") => {
|
||||
self.current_language_index = if self.current_language_index == 0 { self.languages.len() - 1 } else { self.current_language_index - 1 };
|
||||
Some(format!("Switching to {}", self.languages[self.current_language_index].get_language_name()))
|
||||
},
|
||||
Some(e) => Some(format!("Bad `lang(uage)` argument: {}", e)),
|
||||
None => Some(format!("Valid arguments for `lang(uage)` are `show`, `next`|`n`, `previous`|`prev`|`n`"))
|
||||
},
|
||||
"help" => {
|
||||
let mut buf = String::new();
|
||||
let ref lang = self.languages[self.current_language_index];
|
||||
let directives = match self.get_directives() {
|
||||
CommandTree::Top(children) => children,
|
||||
_ => panic!("Top-level CommandTree not Top")
|
||||
};
|
||||
|
||||
writeln!(buf, "MetaInterpreter options").unwrap();
|
||||
writeln!(buf, "-----------------------").unwrap();
|
||||
|
||||
for directive in directives {
|
||||
let trailer = " ";
|
||||
writeln!(buf, "{}{}- {}", directive.get_cmd(), trailer, directive.get_help()).unwrap();
|
||||
}
|
||||
|
||||
writeln!(buf, "").unwrap();
|
||||
writeln!(buf, "Language-specific help for {}", lang.get_language_name()).unwrap();
|
||||
writeln!(buf, "-----------------------").unwrap();
|
||||
writeln!(buf, "{}", lang.custom_interpreter_directives_help()).unwrap();
|
||||
Some(buf)
|
||||
},
|
||||
"debug" => self.handle_debug(commands),
|
||||
e => self.languages[self.current_language_index]
|
||||
.handle_custom_interpreter_directives(&commands)
|
||||
.or(Some(format!("Unknown command: {}", e)))
|
||||
}
|
||||
}
|
||||
fn handle_debug(&mut self, commands: Vec<&str>) -> Option<String> {
|
||||
let passes = self.get_cur_language().get_passes();
|
||||
match commands.get(1) {
|
||||
Some(&"passes") => Some(
|
||||
passes.into_iter()
|
||||
.map(|desc| {
|
||||
if self.options.debug_passes.contains_key(&desc.name) {
|
||||
let color = "green";
|
||||
format!("*{}", desc.name.color(color))
|
||||
} else {
|
||||
desc.name
|
||||
}
|
||||
})
|
||||
.intersperse(format!(" -> "))
|
||||
.collect()),
|
||||
b @ Some(&"show") | b @ Some(&"hide") => {
|
||||
let show = b == Some(&"show");
|
||||
let debug_pass: String = match commands.get(2) {
|
||||
Some(s) => s.to_string(),
|
||||
None => return Some(format!("Must specify a stage to debug")),
|
||||
};
|
||||
let pass_opt = commands.get(3);
|
||||
if let Some(desc) = passes.iter().find(|desc| desc.name == debug_pass) {
|
||||
let mut opts = vec![];
|
||||
if let Some(opt) = pass_opt {
|
||||
opts.push(opt.to_string());
|
||||
}
|
||||
let msg = format!("{} debug for pass {}", if show { "Enabling" } else { "Disabling" }, debug_pass);
|
||||
if show {
|
||||
self.options.debug_passes.insert(desc.name.clone(), PassDebugOptionsDescriptor { opts });
|
||||
} else {
|
||||
self.options.debug_passes.remove(&desc.name);
|
||||
}
|
||||
Some(msg)
|
||||
} else {
|
||||
Some(format!("Couldn't find stage: {}", debug_pass))
|
||||
}
|
||||
},
|
||||
_ => Some(format!("Unknown debug command"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn compilation_sequence(llvm_code: LLVMCodeString, sourcefile: &str) {
|
||||
use std::process::Command;
|
||||
|
||||
53
schala-repl/src/repl/command_tree.rs
Normal file
53
schala-repl/src/repl/command_tree.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum CommandTree {
|
||||
Terminal {
|
||||
name: String,
|
||||
help_msg: Option<String>,
|
||||
function: Option<Box<(fn() -> Option<String>)>>,
|
||||
},
|
||||
NonTerminal {
|
||||
name: String,
|
||||
children: Vec<CommandTree>,
|
||||
help_msg: Option<String>,
|
||||
function: Option<Box<(fn() -> Option<String>)>>,
|
||||
},
|
||||
Top(Vec<CommandTree>),
|
||||
}
|
||||
|
||||
impl CommandTree {
|
||||
pub fn term(s: &str, help: Option<&str>) -> CommandTree {
|
||||
CommandTree::Terminal {name: s.to_string(), help_msg: help.map(|x| x.to_string()), function: None }
|
||||
}
|
||||
|
||||
pub fn nonterm(s: &str, help: Option<&str>, children: Vec<CommandTree>) -> CommandTree {
|
||||
CommandTree::NonTerminal {
|
||||
name: s.to_string(),
|
||||
help_msg: help.map(|x| x.to_string()),
|
||||
children,
|
||||
function: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cmd(&self) -> &str {
|
||||
match self {
|
||||
CommandTree::Terminal { name, .. } => name.as_str(),
|
||||
CommandTree::NonTerminal {name, ..} => name.as_str(),
|
||||
CommandTree::Top(_) => "",
|
||||
}
|
||||
}
|
||||
pub fn get_help(&self) -> &str {
|
||||
match self {
|
||||
CommandTree::Terminal { help_msg, ..} => help_msg.as_ref().map(|s| s.as_str()).unwrap_or(""),
|
||||
CommandTree::NonTerminal { help_msg, .. } => help_msg.as_ref().map(|s| s.as_str()).unwrap_or(""),
|
||||
CommandTree::Top(_) => ""
|
||||
}
|
||||
}
|
||||
pub fn get_children(&self) -> Vec<&str> {
|
||||
match self {
|
||||
CommandTree::Terminal { .. } => vec![],
|
||||
CommandTree::NonTerminal { children, .. } => children.iter().map(|x| x.get_cmd()).collect(),
|
||||
CommandTree::Top(children) => children.iter().map(|x| x.get_cmd()).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
355
schala-repl/src/repl/mod.rs
Normal file
355
schala-repl/src/repl/mod.rs
Normal file
@@ -0,0 +1,355 @@
|
||||
use std::fmt::Write as FmtWrite;
|
||||
use std::io::{Read, Write};
|
||||
use std::fs::File;
|
||||
use std::sync::Arc;
|
||||
|
||||
use colored::*;
|
||||
use itertools::Itertools;
|
||||
use language::{ProgrammingLanguageInterface, EvalOptions,
|
||||
PassDebugOptionsDescriptor};
|
||||
mod command_tree;
|
||||
use self::command_tree::CommandTree;
|
||||
|
||||
const HISTORY_SAVE_FILE: &'static str = ".schala_history";
|
||||
const OPTIONS_SAVE_FILE: &'static str = ".schala_repl";
|
||||
|
||||
pub struct Repl {
|
||||
options: EvalOptions,
|
||||
languages: Vec<Box<ProgrammingLanguageInterface>>,
|
||||
current_language_index: usize,
|
||||
interpreter_directive_sigil: char,
|
||||
line_reader: ::linefeed::interface::Interface<::linefeed::terminal::DefaultTerminal>,
|
||||
}
|
||||
|
||||
impl Repl {
|
||||
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 line_reader = Interface::new("schala-repl").unwrap();
|
||||
|
||||
Repl {
|
||||
options: Repl::get_options(),
|
||||
languages: languages,
|
||||
current_language_index: i,
|
||||
interpreter_directive_sigil: ':',
|
||||
line_reader
|
||||
}
|
||||
}
|
||||
|
||||
fn get_cur_language(&self) -> &ProgrammingLanguageInterface {
|
||||
self.languages[self.current_language_index].as_ref()
|
||||
}
|
||||
|
||||
fn get_options() -> EvalOptions {
|
||||
File::open(OPTIONS_SAVE_FILE)
|
||||
.and_then(|mut file| {
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)?;
|
||||
Ok(contents)
|
||||
})
|
||||
.and_then(|contents| {
|
||||
let options: EvalOptions = ::serde_json::from_str(&contents)?;
|
||||
Ok(options)
|
||||
}).unwrap_or(EvalOptions::default())
|
||||
}
|
||||
|
||||
fn save_options(&self) {
|
||||
let ref options = self.options;
|
||||
let read = File::create(OPTIONS_SAVE_FILE)
|
||||
.and_then(|mut file| {
|
||||
let buf = ::serde_json::to_string(options).unwrap();
|
||||
file.write_all(buf.as_bytes())
|
||||
});
|
||||
|
||||
if let Err(err) = read {
|
||||
println!("Error saving {} file {}", OPTIONS_SAVE_FILE, err);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
use linefeed::ReadResult;
|
||||
|
||||
println!("Schala MetaInterpreter version {}", ::VERSION_STRING);
|
||||
println!("Type {}help for help with the REPL", self.interpreter_directive_sigil);
|
||||
|
||||
self.line_reader.load_history(HISTORY_SAVE_FILE).unwrap_or(());
|
||||
|
||||
loop {
|
||||
let language_name = self.get_cur_language().get_language_name();
|
||||
let directives = self.get_directives();
|
||||
let tab_complete_handler = TabCompleteHandler::new(self.interpreter_directive_sigil, directives);
|
||||
self.line_reader.set_completer(Arc::new(tab_complete_handler));
|
||||
|
||||
let prompt_str = format!("{} >> ", language_name);
|
||||
self.line_reader.set_prompt(&prompt_str).unwrap();
|
||||
|
||||
match self.line_reader.read_line() {
|
||||
Err(e) => {
|
||||
println!("Terminal read error: {}", e);
|
||||
},
|
||||
Ok(ReadResult::Eof) => break,
|
||||
Ok(ReadResult::Signal(_)) => break,
|
||||
Ok(ReadResult::Input(ref input)) => {
|
||||
self.line_reader.add_history_unique(input.to_string());
|
||||
let output = match input.chars().nth(0) {
|
||||
Some(ch) if ch == self.interpreter_directive_sigil => self.handle_interpreter_directive(input),
|
||||
_ => Some(self.input_handler(input)),
|
||||
};
|
||||
if let Some(o) = output {
|
||||
println!("=> {}", o);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.line_reader.save_history(HISTORY_SAVE_FILE).unwrap_or(());
|
||||
self.save_options();
|
||||
println!("Exiting...");
|
||||
}
|
||||
|
||||
fn input_handler(&mut self, input: &str) -> String {
|
||||
let ref mut language = self.languages[self.current_language_index];
|
||||
let interpreter_output = language.execute_pipeline(input, &self.options);
|
||||
interpreter_output.to_repl()
|
||||
}
|
||||
|
||||
fn get_directives(&self) -> CommandTree {
|
||||
let ref passes = self.get_cur_language().get_passes();
|
||||
|
||||
let passes_directives: Vec<CommandTree> = passes.iter()
|
||||
.map(|pass_descriptor| {
|
||||
let name = &pass_descriptor.name;
|
||||
if pass_descriptor.debug_options.len() == 0 {
|
||||
CommandTree::term(name, None)
|
||||
} else {
|
||||
let children: Vec<CommandTree> = pass_descriptor.debug_options.iter()
|
||||
.map(|o| CommandTree::term(o, None)).collect();
|
||||
CommandTree::NonTerminal {
|
||||
name: name.clone(),
|
||||
children,
|
||||
help_msg: None,
|
||||
function: None,
|
||||
}
|
||||
}
|
||||
}).collect();
|
||||
|
||||
CommandTree::Top(vec![
|
||||
CommandTree::term("exit", Some("exit the REPL")),
|
||||
CommandTree::term("quit", Some("exit the REPL")),
|
||||
CommandTree::term("help", Some("Print this help message")),
|
||||
CommandTree::nonterm("debug",
|
||||
Some("show or hide pass debug info for a given pass, or display the names of all passes, or turn timing on/off"),
|
||||
vec![
|
||||
CommandTree::term("passes", None),
|
||||
CommandTree::nonterm("show", None, passes_directives.clone()),
|
||||
CommandTree::nonterm("hide", None, passes_directives.clone()),
|
||||
CommandTree::nonterm("timing", None, vec![
|
||||
CommandTree::term("on", None),
|
||||
CommandTree::term("off", None),
|
||||
])
|
||||
]
|
||||
),
|
||||
CommandTree::nonterm("lang",
|
||||
Some("switch between languages, or go directly to a langauge by name"),
|
||||
vec![
|
||||
CommandTree::term("next", None),
|
||||
CommandTree::term("prev", None),
|
||||
CommandTree::nonterm("go", None, vec![]),
|
||||
]
|
||||
),
|
||||
CommandTree::term("doc", Some("Get language-specific help for an item")),
|
||||
])
|
||||
}
|
||||
|
||||
fn handle_interpreter_directive(&mut self, input: &str) -> Option<String> {
|
||||
let mut iter = input.chars();
|
||||
iter.next();
|
||||
let commands: Vec<&str> = iter
|
||||
.as_str()
|
||||
.split_whitespace()
|
||||
.collect();
|
||||
|
||||
let initial_cmd: &str = match commands.get(0).clone() {
|
||||
None => return None,
|
||||
Some(s) => s
|
||||
};
|
||||
|
||||
match initial_cmd {
|
||||
"exit" | "quit" => {
|
||||
self.save_options();
|
||||
::std::process::exit(0)
|
||||
},
|
||||
"lang" | "language" => match commands.get(1) {
|
||||
Some(&"show") => {
|
||||
let mut buf = String::new();
|
||||
for (i, lang) in self.languages.iter().enumerate() {
|
||||
write!(buf, "{}{}\n", if i == self.current_language_index { "* "} else { "" }, lang.get_language_name()).unwrap();
|
||||
}
|
||||
Some(buf)
|
||||
},
|
||||
Some(&"go") => match commands.get(2) {
|
||||
None => Some(format!("Must specify a language name")),
|
||||
Some(&desired_name) => {
|
||||
for (i, _) in self.languages.iter().enumerate() {
|
||||
let lang_name = self.languages[i].get_language_name();
|
||||
if lang_name.to_lowercase() == desired_name.to_lowercase() {
|
||||
self.current_language_index = i;
|
||||
return Some(format!("Switching to {}", self.languages[self.current_language_index].get_language_name()));
|
||||
}
|
||||
}
|
||||
Some(format!("Language {} not found", desired_name))
|
||||
}
|
||||
},
|
||||
Some(&"next") | Some(&"n") => {
|
||||
self.current_language_index = (self.current_language_index + 1) % self.languages.len();
|
||||
Some(format!("Switching to {}", self.languages[self.current_language_index].get_language_name()))
|
||||
},
|
||||
Some(&"previous") | Some(&"p") | Some(&"prev") => {
|
||||
self.current_language_index = if self.current_language_index == 0 { self.languages.len() - 1 } else { self.current_language_index - 1 };
|
||||
Some(format!("Switching to {}", self.languages[self.current_language_index].get_language_name()))
|
||||
},
|
||||
Some(e) => Some(format!("Bad `lang(uage)` argument: {}", e)),
|
||||
None => Some(format!("Valid arguments for `lang(uage)` are `show`, `next`|`n`, `previous`|`prev`|`n`"))
|
||||
},
|
||||
"help" => {
|
||||
let mut buf = String::new();
|
||||
let ref lang = self.languages[self.current_language_index];
|
||||
let directives = match self.get_directives() {
|
||||
CommandTree::Top(children) => children,
|
||||
_ => panic!("Top-level CommandTree not Top")
|
||||
};
|
||||
|
||||
writeln!(buf, "MetaInterpreter options").unwrap();
|
||||
writeln!(buf, "-----------------------").unwrap();
|
||||
|
||||
for directive in directives {
|
||||
let trailer = " ";
|
||||
writeln!(buf, "{}{}- {}", directive.get_cmd(), trailer, directive.get_help()).unwrap();
|
||||
}
|
||||
|
||||
writeln!(buf, "").unwrap();
|
||||
writeln!(buf, "Language-specific help for {}", lang.get_language_name()).unwrap();
|
||||
writeln!(buf, "-----------------------").unwrap();
|
||||
writeln!(buf, "{}", lang.custom_interpreter_directives_help()).unwrap();
|
||||
Some(buf)
|
||||
},
|
||||
"debug" => self.handle_debug(commands),
|
||||
"doc" => self.languages[self.current_language_index]
|
||||
.get_doc(&commands)
|
||||
.or(Some(format!("No docs implemented"))),
|
||||
e => {
|
||||
self.languages[self.current_language_index]
|
||||
.handle_custom_interpreter_directives(&commands)
|
||||
.or(Some(format!("Unknown command: {}", e)))
|
||||
}
|
||||
}
|
||||
}
|
||||
fn handle_debug(&mut self, commands: Vec<&str>) -> Option<String> {
|
||||
let passes = self.get_cur_language().get_passes();
|
||||
match commands.get(1) {
|
||||
Some(&"timing") => match commands.get(2) {
|
||||
Some(&"on") => { self.options.debug_timing = true; None }
|
||||
Some(&"off") => { self.options.debug_timing = false; None }
|
||||
_ => return Some(format!(r#"Argument to "timing" must be "on" or "off""#)),
|
||||
},
|
||||
Some(&"passes") => Some(
|
||||
passes.into_iter()
|
||||
.map(|desc| {
|
||||
if self.options.debug_passes.contains_key(&desc.name) {
|
||||
let color = "green";
|
||||
format!("*{}", desc.name.color(color))
|
||||
} else {
|
||||
desc.name
|
||||
}
|
||||
})
|
||||
.intersperse(format!(" -> "))
|
||||
.collect()),
|
||||
b @ Some(&"show") | b @ Some(&"hide") => {
|
||||
let show = b == Some(&"show");
|
||||
let debug_pass: String = match commands.get(2) {
|
||||
Some(s) => s.to_string(),
|
||||
None => return Some(format!("Must specify a stage to debug")),
|
||||
};
|
||||
let pass_opt = commands.get(3);
|
||||
if let Some(desc) = passes.iter().find(|desc| desc.name == debug_pass) {
|
||||
let mut opts = vec![];
|
||||
if let Some(opt) = pass_opt {
|
||||
opts.push(opt.to_string());
|
||||
}
|
||||
let msg = format!("{} debug for pass {}", if show { "Enabling" } else { "Disabling" }, debug_pass);
|
||||
if show {
|
||||
self.options.debug_passes.insert(desc.name.clone(), PassDebugOptionsDescriptor { opts });
|
||||
} else {
|
||||
self.options.debug_passes.remove(&desc.name);
|
||||
}
|
||||
Some(msg)
|
||||
} else {
|
||||
Some(format!("Couldn't find stage: {}", debug_pass))
|
||||
}
|
||||
},
|
||||
_ => Some(format!("Unknown debug command"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TabCompleteHandler {
|
||||
sigil: char,
|
||||
top_level_commands: CommandTree,
|
||||
}
|
||||
|
||||
use linefeed::complete::{Completion, Completer};
|
||||
use linefeed::terminal::Terminal;
|
||||
|
||||
impl TabCompleteHandler {
|
||||
fn new(sigil: char, top_level_commands: CommandTree) -> TabCompleteHandler {
|
||||
TabCompleteHandler {
|
||||
top_level_commands,
|
||||
sigil,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Terminal> Completer<T> for TabCompleteHandler {
|
||||
fn complete(&self, word: &str, prompter: &::linefeed::prompter::Prompter<T>, start: usize, _end: usize) -> Option<Vec<Completion>> {
|
||||
let line = prompter.buffer();
|
||||
|
||||
if line.starts_with(&format!("{}", self.sigil)) {
|
||||
let mut words = line[1..(if start == 0 { 1 } else { start })].split_whitespace();
|
||||
let mut completions = Vec::new();
|
||||
let mut command_tree: Option<&CommandTree> = Some(&self.top_level_commands);
|
||||
|
||||
loop {
|
||||
match words.next() {
|
||||
None => {
|
||||
let top = match command_tree {
|
||||
Some(CommandTree::Top(_)) => true,
|
||||
_ => false
|
||||
};
|
||||
let word = if top { word.get(1..).unwrap() } else { word };
|
||||
for cmd in command_tree.map(|x| x.get_children()).unwrap_or(vec![]).into_iter() {
|
||||
if cmd.starts_with(word) {
|
||||
completions.push(Completion {
|
||||
completion: format!("{}{}", if top { ":" } else { "" }, cmd),
|
||||
display: Some(cmd.to_string()),
|
||||
suffix: ::linefeed::complete::Suffix::Some(' ')
|
||||
})
|
||||
}
|
||||
}
|
||||
break;
|
||||
},
|
||||
Some(s) => {
|
||||
let new_ptr: Option<&CommandTree> = command_tree.and_then(|cm| match cm {
|
||||
CommandTree::Top(children) => children.iter().find(|c| c.get_cmd() == s),
|
||||
CommandTree::NonTerminal { children, .. } => children.iter().find(|c| c.get_cmd() == s),
|
||||
CommandTree::Terminal { .. } => None,
|
||||
});
|
||||
command_tree = new_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(completions)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user