1 Commits

Author SHA1 Message Date
greg
b0ca955de6 Rename CommandTree to OldCommandTree 2019-06-01 18:34:13 -07:00
25 changed files with 1455 additions and 1983 deletions

View File

@@ -59,10 +59,6 @@ much like Haskell
Here's a partial list of resources I've made use of in the process
of learning how to write a programming language.
### General
http://thume.ca/2019/04/18/writing-a-compiler-in-rust/
### Type-checking
https://skillsmatter.com/skillscasts/10868-inside-the-rust-compiler
https://www.youtube.com/watch?v=il3gD7XMdmA

20
TODO.md
View File

@@ -7,6 +7,8 @@
- cf. the notation mentioned in the cardelli paper, the debug information for the `typechecking` pass should
print the generated type variable for every subexpression in an expression
- change 'trait' to 'interface'
- think about idris-related ideas of multiple implementations of a type for an interface (+ vs * impl for monoids, for preorder/inorder/postorder for Foldable)
-should have an Idris-like `cast To From` function
@@ -15,6 +17,8 @@ print the generated type variable for every subexpression in an expression
-idea: the `type` declaration should have some kind of GADT-like syntax
-idea: I should make the BNF grammar part of the documentation...
- Idea: if you have a pattern-match where one variant has a variable and the other lacks it
instead of treating this as a type error, promote the bound variable to an option type
@@ -65,8 +69,6 @@ ex.
## Playing around with conditional syntax ideas
-
- if/match playground
simple if
@@ -105,3 +107,17 @@ if the only two guard patterns are true and false, then the abbreviated syntax:
`'if' discriminator 'then' block_or_expr 'else' block_or_expr`
can replace `'if' discriminator '{' 'true' 'then' block_or_expr; 'false' 'then' block_or_expr '}'`

View File

@@ -19,10 +19,6 @@ impl<T> Meta<T> {
pub fn node(&self) -> &T {
&self.n
}
pub fn node_mut(&mut self) -> &mut T {
&mut self.n
}
}
//TODO this PartialEq is here to make tests work - find a way to make it not necessary
@@ -47,13 +43,7 @@ pub enum Statement {
pub type Block = Vec<Meta<Statement>>;
pub type ParamName = Rc<String>;
#[derive(Debug, PartialEq, Clone)]
pub struct FormalParam {
pub name: ParamName,
pub default: Option<Expression>,
pub anno: Option<TypeIdentifier>
}
pub type FormalParam = (ParamName, Option<TypeIdentifier>);
#[derive(Debug, PartialEq, Clone)]
pub enum Declaration {
@@ -104,20 +94,8 @@ pub enum Variant {
}
#[derive(Debug, PartialEq, Clone)]
pub struct Expression {
pub kind: ExpressionKind,
pub type_anno: Option<TypeIdentifier>
}
pub struct Expression(pub ExpressionKind, pub Option<TypeIdentifier>);
impl Expression {
pub fn new(kind: ExpressionKind) -> Expression {
Expression { kind, type_anno: None }
}
pub fn with_anno(kind: ExpressionKind, type_anno: TypeIdentifier) -> Expression {
Expression { kind, type_anno: Some(type_anno) }
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum TypeIdentifier {
@@ -147,7 +125,7 @@ pub enum ExpressionKind {
},
Call {
f: Box<Meta<Expression>>,
arguments: Vec<Meta<InvocationArgument>>,
arguments: Vec<Meta<Expression>>,
},
Index {
indexee: Box<Meta<Expression>>,
@@ -173,16 +151,6 @@ pub enum ExpressionKind {
ListLiteral(Vec<Meta<Expression>>),
}
#[derive(Debug, PartialEq, Clone)]
pub enum InvocationArgument {
Positional(Expression),
Keyword {
name: Rc<String>,
expr: Expression,
},
Ignored
}
#[derive(Debug, PartialEq, Clone)]
pub enum Discriminator {
Simple(Expression),

View File

@@ -25,7 +25,6 @@ impl BinOp {
Slash => "/",
LAngleBracket => "<",
RAngleBracket => ">",
Equals => "=",
_ => return None
};
Some(BinOp::from_sigil(s))
@@ -48,7 +47,6 @@ impl BinOp {
Slash => "/",
LAngleBracket => "<",
RAngleBracket => ">",
Equals => "=",
_ => return None
};
let default = 10_000_000;

View File

@@ -532,10 +532,8 @@ mod eval_tests {
fn test_basic_eval() {
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]

View File

@@ -17,6 +17,18 @@ extern crate schala_repl;
extern crate schala_lang_codegen;
extern crate ena;
use stopwatch::Stopwatch;
use std::time::Duration;
use std::cell::RefCell;
use std::rc::Rc;
use std::collections::HashSet;
use itertools::Itertools;
use schala_repl::{ProgrammingLanguageInterface,
ComputationRequest, ComputationResponse,
LangMetaRequest, LangMetaResponse, GlobalOutputStats,
DebugResponse, DebugAsk};
macro_rules! bx {
($e:expr) => { Box::new($e) }
@@ -34,8 +46,287 @@ mod symbol_table;
mod builtin;
mod reduced_ast;
mod eval;
mod visitor;
mod schala;
/// All bits of state necessary to parse and execute a Schala program are stored in this struct.
/// `state` represents the execution state for the AST-walking interpreter, the other fields
/// should be self-explanatory.
pub struct Schala {
source_reference: SourceReference,
state: eval::State<'static>,
symbol_table: Rc<RefCell<symbol_table::SymbolTable>>,
type_context: typechecking::TypeContext<'static>,
active_parser: Option<parsing::Parser>,
}
pub use schala::Schala;
impl Schala {
fn handle_docs(&self, source: String) -> LangMetaResponse {
LangMetaResponse::Docs {
doc_string: format!("Schala item `{}` : <<Schala-lang documentation not yet implemented>>", source)
}
}
}
impl Schala {
/// Creates a new Schala environment *without* any prelude.
fn new_blank_env() -> Schala {
let symbols = Rc::new(RefCell::new(symbol_table::SymbolTable::new()));
Schala {
source_reference: SourceReference::new(),
symbol_table: symbols.clone(),
state: eval::State::new(symbols),
type_context: typechecking::TypeContext::new(),
active_parser: None,
}
}
/// Creates a new Schala environment with the standard prelude, which is defined as ordinary
/// Schala code in the file `prelude.schala`
pub fn new() -> Schala {
let prelude = include_str!("prelude.schala");
let mut s = Schala::new_blank_env();
let request = ComputationRequest { source: prelude, debug_requests: HashSet::default() };
s.run_computation(request);
s
}
fn handle_debug_immediate(&self, request: DebugAsk) -> DebugResponse {
use DebugAsk::*;
match request {
Timing => DebugResponse { ask: Timing, value: format!("Invalid") },
ByStage { stage_name } => match &stage_name[..] {
"symbol-table" => {
let value = self.symbol_table.borrow().debug_symbol_table();
DebugResponse {
ask: ByStage { stage_name: format!("symbol-table") },
value
}
},
s => {
DebugResponse {
ask: ByStage { stage_name: s.to_string() },
value: format!("Not-implemented")
}
}
}
}
}
}
fn tokenizing(input: &str, _handle: &mut Schala, comp: Option<&mut PassDebugArtifact>) -> Result<Vec<tokenizing::Token>, String> {
let tokens = tokenizing::tokenize(input);
comp.map(|comp| {
let token_string = tokens.iter().map(|t| t.to_string_with_metadata()).join(", ");
comp.add_artifact(token_string);
});
let errors: Vec<String> = tokens.iter().filter_map(|t| t.get_error()).collect();
if errors.len() == 0 {
Ok(tokens)
} else {
Err(format!("{:?}", errors))
}
}
fn parsing(input: Vec<tokenizing::Token>, handle: &mut Schala, comp: Option<&mut PassDebugArtifact>) -> Result<ast::AST, String> {
use crate::parsing::Parser;
let mut parser = match handle.active_parser.take() {
None => Parser::new(input),
Some(parser) => parser
};
let ast = parser.parse();
let _trace = parser.format_parse_trace();
comp.map(|_comp| {
/*
//TODO need to control which of these debug stages get added
let opt = comp.cur_debug_options.get(0).map(|s| s.clone());
match opt {
None => comp.add_artifact(TraceArtifact::new("ast", format!("{:?}", ast))),
Some(ref s) if s == "compact" => comp.add_artifact(TraceArtifact::new("ast", format!("{:?}", ast))),
Some(ref s) if s == "expanded" => comp.add_artifact(TraceArtifact::new("ast", format!("{:#?}", ast))),
Some(ref s) if s == "trace" => comp.add_artifact(TraceArtifact::new_parse_trace(trace)),
Some(ref x) => println!("Bad parsing debug option: {}", x),
};
*/
});
ast.map_err(|err| format_parse_error(err, handle))
}
fn format_parse_error(error: parsing::ParseError, handle: &mut Schala) -> String {
let line_num = error.token.line_num;
let ch = error.token.char_num;
let line_from_program = handle.source_reference.get_line(line_num);
let location_pointer = format!("{}^", " ".repeat(ch));
let line_num_digits = format!("{}", line_num).chars().count();
let space_padding = " ".repeat(line_num_digits);
format!(r#"
{error_msg}
{space_padding} |
{line_num} | {}
{space_padding} | {}
"#, line_from_program, location_pointer, error_msg=error.msg, space_padding=space_padding, line_num=line_num)
}
fn symbol_table(input: ast::AST, handle: &mut Schala, comp: Option<&mut PassDebugArtifact>) -> Result<ast::AST, String> {
let add = handle.symbol_table.borrow_mut().add_top_level_symbols(&input);
match add {
Ok(()) => {
let debug = handle.symbol_table.borrow().debug_symbol_table();
comp.map(|comp| comp.add_artifact(debug));
Ok(input)
},
Err(msg) => Err(msg)
}
}
fn typechecking(input: ast::AST, handle: &mut Schala, comp: Option<&mut PassDebugArtifact>) -> Result<ast::AST, String> {
let result = handle.type_context.typecheck(&input);
comp.map(|comp| {
comp.add_artifact(match result {
Ok(ty) => ty.to_string(),
Err(err) => format!("Type error: {}", err.msg)
});
});
Ok(input)
}
fn ast_reducing(input: ast::AST, handle: &mut Schala, comp: Option<&mut PassDebugArtifact>) -> Result<reduced_ast::ReducedAST, String> {
let ref symbol_table = handle.symbol_table.borrow();
let output = input.reduce(symbol_table);
comp.map(|comp| comp.add_artifact(format!("{:?}", output)));
Ok(output)
}
fn eval(input: reduced_ast::ReducedAST, handle: &mut Schala, comp: Option<&mut PassDebugArtifact>) -> Result<String, String> {
comp.map(|comp| comp.add_artifact(handle.state.debug_print()));
let evaluation_outputs = handle.state.evaluate(input, true);
let text_output: Result<Vec<String>, String> = evaluation_outputs
.into_iter()
.collect();
let eval_output: Result<String, String> = text_output
.map(|v| { v.into_iter().intersperse(format!("\n")).collect() });
eval_output
}
/// Represents lines of source code
struct SourceReference {
lines: Option<Vec<String>>
}
impl SourceReference {
fn new() -> SourceReference {
SourceReference { lines: None }
}
fn load_new_source(&mut self, source: &str) {
//TODO this is a lot of heap allocations - maybe there's a way to make it more efficient?
self.lines = Some(source.lines().map(|s| s.to_string()).collect()); }
fn get_line(&self, line: usize) -> String {
self.lines.as_ref().and_then(|x| x.get(line).map(|s| s.to_string())).unwrap_or(format!("NO LINE FOUND"))
}
}
#[derive(Default)]
struct PassDebugArtifact {
artifacts: Vec<String>
}
impl PassDebugArtifact {
fn add_artifact(&mut self, artifact: String) {
self.artifacts.push(artifact)
}
}
fn stage_names() -> Vec<&'static str> {
vec![
"tokenizing",
"parsing",
"symbol-table",
"typechecking",
"ast-reduction",
"ast-walking-evaluation"
]
}
impl ProgrammingLanguageInterface for Schala {
fn get_language_name(&self) -> String { format!("Schala") }
fn get_source_file_suffix(&self) -> String { format!("schala") }
fn run_computation(&mut self, request: ComputationRequest) -> ComputationResponse {
struct PassToken<'a> {
schala: &'a mut Schala,
stage_durations: &'a mut Vec<(String, Duration)>,
sw: &'a Stopwatch,
debug_requests: &'a HashSet<DebugAsk>,
debug_responses: &'a mut Vec<DebugResponse>,
}
fn output_wrapper<Input, Output, F>(n: usize, func: F, input: Input, tok: &mut PassToken) -> Result<Output, String>
where F: Fn(Input, &mut Schala, Option<&mut PassDebugArtifact>) -> Result<Output, String>
{
let stage_names = stage_names();
let mut debug_artifact = if tok.debug_requests.contains(&DebugAsk::ByStage { stage_name: stage_names[n].to_string() }) {
Some(PassDebugArtifact::default())
} else {
None
};
let output = func(input, tok.schala, debug_artifact.as_mut());
tok.stage_durations.push((stage_names[n].to_string(), tok.sw.elapsed()));
if let Some(artifact) = debug_artifact {
for value in artifact.artifacts.into_iter() {
let resp = DebugResponse {
ask: DebugAsk::ByStage { stage_name: stage_names[n].to_string() },
value,
};
tok.debug_responses.push(resp);
}
}
output
}
let ComputationRequest { source, debug_requests } = request;
self.source_reference.load_new_source(source);
let sw = Stopwatch::start_new();
let mut stage_durations = Vec::new();
let mut debug_responses = Vec::new();
let mut tok = PassToken { schala: self, stage_durations: &mut stage_durations, sw: &sw, debug_requests: &debug_requests, debug_responses: &mut debug_responses };
let main_output: Result<String, String> = Ok(source)
.and_then(|source| output_wrapper(0, tokenizing, source, &mut tok))
.and_then(|tokens| output_wrapper(1, parsing, tokens, &mut tok))
.and_then(|ast| output_wrapper(2, symbol_table, ast, &mut tok))
.and_then(|ast| output_wrapper(3, typechecking, ast, &mut tok))
.and_then(|ast| output_wrapper(4, ast_reducing, ast, &mut tok))
.and_then(|reduced_ast| output_wrapper(5, eval, reduced_ast, &mut tok));
let total_duration = sw.elapsed();
let global_output_stats = GlobalOutputStats {
total_duration, stage_durations
};
ComputationResponse {
main_output,
global_output_stats,
debug_responses,
}
}
fn request_meta(&mut self, request: LangMetaRequest) -> LangMetaResponse {
match request {
LangMetaRequest::StageNames => LangMetaResponse::StageNames(stage_names().iter().map(|s| s.to_string()).collect()),
LangMetaRequest::Docs { source } => self.handle_docs(source),
LangMetaRequest::ImmediateDebug(debug_request) =>
LangMetaResponse::ImmediateDebug(self.handle_debug_immediate(debug_request)),
LangMetaRequest::Custom { .. } => LangMetaResponse::Custom { kind: format!("not-implemented"), value: format!("") }
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,662 +0,0 @@
#![cfg(test)]
use ::std::rc::Rc;
use super::tokenize;
use super::ParseResult;
use crate::builtin::{PrefixOp, BinOp};
use crate::ast::{AST, Meta, Expression, Statement, IfExpressionBody, Discriminator, Pattern, PatternLiteral, TypeBody, Enumerator, ForBody, InvocationArgument, FormalParam};
use super::Statement::*;
use super::Declaration::*;
use super::Signature;
use super::TypeIdentifier::*;
use super::TypeSingletonName;
use super::ExpressionKind::*;
use super::Variant::*;
use super::ForBody::*;
fn parse(input: &str) -> ParseResult<AST> {
let tokens: Vec<crate::tokenizing::Token> = tokenize(input);
let mut parser = super::Parser::new(tokens);
parser.parse()
}
macro_rules! parse_test {
($string:expr, $correct:expr) => { assert_eq!(parse($string).unwrap(), $correct) };
}
macro_rules! parse_test_wrap_ast {
($string:expr, $correct:expr) => { parse_test!($string, AST(vec![$correct])) }
}
macro_rules! parse_error {
($string:expr) => { assert!(parse($string).is_err()) }
}
macro_rules! val {
($var:expr) => { Value(Rc::new($var.to_string())) }
}
macro_rules! ty {
($name:expr) => { Singleton(tys!($name)) }
}
macro_rules! tys {
($name:expr) => { TypeSingletonName { name: Rc::new($name.to_string()), params: vec![] } };
}
macro_rules! ex {
($expr_type:expr) => { Expression::new($expr_type) };
(m $expr_type:expr) => { Meta::new(Expression::new($expr_type)) };
(m $expr_type:expr, $type_anno:expr) => { Meta::new(Expression::with_anno($expr_type, $type_anno)) };
(s $expr_text:expr) => {
{
let tokens: Vec<crate::tokenizing::Token> = tokenize($expr_text);
let mut parser = super::Parser::new(tokens);
parser.expression().unwrap()
}
};
}
macro_rules! inv {
($expr_type:expr) => { Meta::new(InvocationArgument::Positional($expr_type)) }
}
macro_rules! binexp {
($op:expr, $lhs:expr, $rhs:expr) => { BinExp(BinOp::from_sigil($op), bx!(Expression::new($lhs).into()), bx!(Expression::new($rhs).into())) }
}
macro_rules! prefexp {
($op:expr, $lhs:expr) => { PrefixExp(PrefixOp::from_sigil($op), bx!(Expression::new($lhs).into())) }
}
macro_rules! exst {
($expr_type:expr) => { Meta::new(Statement::ExpressionStatement(Expression::new($expr_type).into())) };
($expr_type:expr, $type_anno:expr) => { Meta::new(Statement::ExpressionStatement(Expression::with_anno($expr_type, $type_anno).into())) };
($op:expr, $lhs:expr, $rhs:expr) => { Meta::new(Statement::ExpressionStatement(ex!(binexp!($op, $lhs, $rhs)))) };
(s $statement_text:expr) => {
{
let tokens: Vec<crate::tokenizing::Token> = tokenize($statement_text);
let mut parser = super::Parser::new(tokens);
Meta::new(parser.statement().unwrap())
}
}
}
#[test]
fn parsing_number_literals_and_binexps() {
parse_test_wrap_ast! { ".2", exst!(FloatLiteral(0.2)) };
parse_test_wrap_ast! { "8.1", exst!(FloatLiteral(8.1)) };
parse_test_wrap_ast! { "0b010", exst!(NatLiteral(2)) };
parse_test_wrap_ast! { "0b0_1_0_", exst!(NatLiteral(2)) }
parse_test_wrap_ast! {"0xff", exst!(NatLiteral(255)) };
parse_test_wrap_ast! {"0xf_f_", exst!(NatLiteral(255)) };
parse_test_wrap_ast! {"0xf_f_+1", exst!(binexp!("+", NatLiteral(255), NatLiteral(1))) };
parse_test! {"3; 4; 4.3", AST(
vec![exst!(NatLiteral(3)), exst!(NatLiteral(4)),
exst!(FloatLiteral(4.3))])
};
parse_test!("1 + 2 * 3", AST(vec!
[
exst!(binexp!("+", NatLiteral(1), binexp!("*", NatLiteral(2), NatLiteral(3))))
]));
parse_test!("1 * 2 + 3", AST(vec!
[
exst!(binexp!("+", binexp!("*", NatLiteral(1), NatLiteral(2)), NatLiteral(3)))
]));
parse_test!("1 && 2", AST(vec![exst!(binexp!("&&", NatLiteral(1), NatLiteral(2)))]));
parse_test!("1 + 2 * 3 + 4", AST(vec![exst!(
binexp!("+",
binexp!("+", NatLiteral(1), binexp!("*", NatLiteral(2), NatLiteral(3))),
NatLiteral(4)))]));
parse_test!("(1 + 2) * 3", AST(vec!
[exst!(binexp!("*", binexp!("+", NatLiteral(1), NatLiteral(2)), NatLiteral(3)))]));
parse_test!(".1 + .2", AST(vec![exst!(binexp!("+", FloatLiteral(0.1), FloatLiteral(0.2)))]));
parse_test!("1 / 2", AST(vec![exst!(binexp!("/", NatLiteral(1), NatLiteral(2)))]));
}
#[test]
fn parsing_tuples() {
parse_test!("()", AST(vec![exst!(TupleLiteral(vec![]))]));
parse_test!("(\"hella\", 34)", AST(vec![exst!(
TupleLiteral(
vec![ex!(s r#""hella""#).into(), ex!(s "34").into()]
)
)]));
parse_test!("((1+2), \"slough\")", AST(vec![exst!(TupleLiteral(vec![
ex!(binexp!("+", NatLiteral(1), NatLiteral(2))).into(),
ex!(StringLiteral(rc!(slough))).into(),
]))]))
}
#[test]
fn parsing_identifiers() {
parse_test!("a", AST(vec![exst!(val!("a"))]));
parse_test!("some_value", AST(vec![exst!(val!("some_value"))]));
parse_test!("a + b", AST(vec![exst!(binexp!("+", val!("a"), val!("b")))]));
//parse_test!("a[b]", AST(vec![Expression(
//parse_test!("a[]", <- TODO THIS NEEDS TO FAIL
//parse_test("a()[b]()[d]")
//TODO fix this parsing stuff
/*
parse_test! { "perspicacity()[a]", AST(vec![
exst!(Index {
indexee: bx!(ex!(Call { f: bx!(ex!(val!("perspicacity"))), arguments: vec![] })),
indexers: vec![ex!(val!("a"))]
})
])
}
*/
parse_test!("a[b,c]", AST(vec![exst!(Index { indexee: bx!(ex!(m val!("a"))), indexers: vec![ex!(m val!("b")), ex!(m val!("c"))]} )]));
parse_test!("None", AST(vec![exst!(val!("None"))]));
parse_test!("Pandas { a: x + y }", AST(vec![
exst!(NamedStruct { name: rc!(Pandas), fields: vec![(rc!(a), ex!(m binexp!("+", val!("x"), val!("y"))))]})
]));
parse_test! { "Pandas { a: n, b: q, }",
AST(vec![
exst!(NamedStruct { name: rc!(Pandas), fields:
vec![(rc!(a), ex!(m val!("n"))), (rc!(b), ex!(m val!("q")))]
}
)
])
};
}
#[test]
fn parsing_complicated_operators() {
parse_test!("a <- b", AST(vec![exst!(binexp!("<-", val!("a"), val!("b")))]));
parse_test!("a || b", AST(vec![exst!(binexp!("||", val!("a"), val!("b")))]));
parse_test!("a<>b", AST(vec![exst!(binexp!("<>", val!("a"), val!("b")))]));
parse_test!("a.b.c.d", AST(vec![exst!(binexp!(".",
binexp!(".",
binexp!(".", val!("a"), val!("b")),
val!("c")),
val!("d")))]));
parse_test!("-3", AST(vec![exst!(prefexp!("-", NatLiteral(3)))]));
parse_test!("-0.2", AST(vec![exst!(prefexp!("-", FloatLiteral(0.2)))]));
parse_test!("!3", AST(vec![exst!(prefexp!("!", NatLiteral(3)))]));
parse_test!("a <- -b", AST(vec![exst!(binexp!("<-", val!("a"), prefexp!("-", val!("b"))))]));
parse_test!("a <--b", AST(vec![exst!(binexp!("<--", val!("a"), val!("b")))]));
}
#[test]
fn parsing_functions() {
parse_test!("fn oi()", AST(vec![Meta::new(Declaration(FuncSig(Signature { name: rc!(oi), operator: false, params: vec![], type_anno: None })))]));
parse_test!("oi()", AST(vec![exst!(Call { f: bx!(ex!(m val!("oi"))), arguments: vec![] })]));
parse_test!("oi(a, 2 + 2)", AST(vec![exst!(Call
{ f: bx!(ex!(m val!("oi"))),
arguments: vec![inv!(ex!(val!("a"))).into(), inv!(ex!(binexp!("+", NatLiteral(2), NatLiteral(2)))).into()]
})]));
parse_error!("a(b,,c)");
parse_test!("fn a(b, c: Int): Int", AST(vec![Meta::new(Declaration(
FuncSig(Signature { name: rc!(a), operator: false, params: vec![
FormalParam { name: rc!(b), anno: None, default: None },
FormalParam { name: rc!(c), anno: Some(ty!("Int")), default: None }
], type_anno: Some(ty!("Int")) })))]));
parse_test!("fn a(x) { x() }", AST(vec![Meta::new(Declaration(
FuncDecl(Signature { name: rc!(a), operator: false, params: vec![FormalParam { name: rc!(x), anno: None, default: None }], type_anno: None },
vec![exst!(Call { f: bx!(ex!(m val!("x"))), arguments: vec![] })])))]));
parse_test!("fn a(x) {\n x() }", AST(vec![Meta::new(Declaration(
FuncDecl(Signature { name: rc!(a), operator: false, params: vec![FormalParam { name: rc!(x), anno: None, default: None }], type_anno: None },
vec![exst!(Call { f: bx!(ex!(m val!("x"))), arguments: vec![] })])))]));
let multiline = r#"
fn a(x) {
x()
}
"#;
parse_test!(multiline, AST(vec![Meta::new(Declaration(
FuncDecl(Signature { name: rc!(a), operator: false, params: vec![FormalParam { name: rc!(x), default: None, anno: None }], type_anno: None },
vec![exst!(Call { f: bx!(ex!(m val!("x"))), arguments: vec![] })])))]));
let multiline2 = r#"
fn a(x) {
x()
}
"#;
parse_test!(multiline2, AST(vec![Meta::new(Declaration(
FuncDecl(Signature { name: rc!(a), operator: false, params: vec![FormalParam { name: rc!(x), default: None, anno: None }], type_anno: None },
vec![exst!(s "x()")])))]));
}
#[test]
fn functions_with_default_args() {
parse_test! {
"fn func(x: Int, y: Int = 4) { }",
AST(vec![
Meta::new(Declaration(
FuncDecl(Signature { name: rc!(func), operator: false, type_anno: None, params: vec![
FormalParam { name: rc!(x), default: None, anno: Some(ty!("Int")) },
FormalParam { name: rc!(y), default: Some(ex!(s "4")), anno: Some(ty!("Int")) }
]}, vec![])
))
])
};
}
#[test]
fn parsing_bools() {
parse_test!("false", AST(vec![exst!(BoolLiteral(false))]));
parse_test!("true", AST(vec![exst!(BoolLiteral(true))]));
}
#[test]
fn parsing_strings() {
parse_test!(r#""hello""#, AST(vec![exst!(StringLiteral(rc!(hello)))]));
}
#[test]
fn parsing_types() {
parse_test!("type Yolo = Yolo", AST(vec![Meta::new(Declaration(TypeDecl { name: tys!("Yolo"), body: TypeBody(vec![UnitStruct(rc!(Yolo))]), mutable: false} ))]));
parse_test!("type mut Yolo = Yolo", AST(vec![Meta::new(Declaration(TypeDecl { name: tys!("Yolo"), body: TypeBody(vec![UnitStruct(rc!(Yolo))]), mutable: true} ))]));
parse_test!("type alias Sex = Drugs", AST(vec![Meta::new(Declaration(TypeAlias(rc!(Sex), rc!(Drugs))))]));
parse_test!("type Sanchez = Miguel | Alejandro(Int, Option<a>) | Esperanza { a: Int, b: String }",
AST(vec![Meta::new(Declaration(TypeDecl{
name: tys!("Sanchez"),
body: TypeBody(vec![
UnitStruct(rc!(Miguel)),
TupleStruct(rc!(Alejandro), vec![
Singleton(TypeSingletonName { name: rc!(Int), params: vec![] }),
Singleton(TypeSingletonName { name: rc!(Option), params: vec![Singleton(TypeSingletonName { name: rc!(a), params: vec![] })] }),
]),
Record{
name: rc!(Esperanza),
members: vec![
(rc!(a), Singleton(TypeSingletonName { name: rc!(Int), params: vec![] })),
(rc!(b), Singleton(TypeSingletonName { name: rc!(String), params: vec![] })),
]
}
]),
mutable: false
}))]));
parse_test!("type Jorge<a> = Diego | Kike(a)", AST(vec![
Meta::new(Declaration(TypeDecl{
name: TypeSingletonName { name: rc!(Jorge), params: vec![Singleton(TypeSingletonName { name: rc!(a), params: vec![] })] },
body: TypeBody(vec![UnitStruct(rc!(Diego)), TupleStruct(rc!(Kike), vec![Singleton(TypeSingletonName { name: rc!(a), params: vec![] })])]),
mutable: false
}
))]));
}
#[test]
fn parsing_bindings() {
parse_test!("let mut a = 10", AST(vec![Meta::new(Declaration(Binding { name: rc!(a), constant: false, type_anno: None, expr: ex!(m NatLiteral(10)) } ))]));
parse_test!("let a = 2 + 2", AST(vec![Meta::new(Declaration(Binding { name: rc!(a), constant: true, type_anno: None, expr: ex!(m binexp!("+", NatLiteral(2), NatLiteral(2))) }) )]));
parse_test!("let a: Nat = 2 + 2", AST(vec![Meta::new(Declaration(
Binding { name: rc!(a), constant: true, type_anno: Some(Singleton(TypeSingletonName { name: rc!(Nat), params: vec![] })),
expr: Meta::new(ex!(binexp!("+", NatLiteral(2), NatLiteral(2)))) }
))]));
}
#[test]
fn parsing_block_expressions() {
parse_test! {
"if a() then { b(); c() }", AST(vec![exst!(
IfExpression {
discriminator: bx! {
Discriminator::Simple(ex!(Call { f: bx!(ex!(m val!("a"))), arguments: vec![]}))
},
body: bx! {
IfExpressionBody::SimpleConditional(
vec![exst!(Call { f: bx!(ex!(m val!("b"))), arguments: vec![]}), exst!(Call { f: bx!(ex!(m val!("c"))), arguments: vec![] })],
None
)
}
}
)])
};
parse_test! {
"if a() then { b(); c() } else { q }", AST(vec![exst!(
IfExpression {
discriminator: bx! {
Discriminator::Simple(ex!(Call { f: bx!(ex!(m val!("a"))), arguments: vec![]}))
},
body: bx! {
IfExpressionBody::SimpleConditional(
vec![exst!(Call { f: bx!(ex!(m val!("b"))), arguments: vec![]}), exst!(Call { f: bx!(ex!(m val!("c"))), arguments: vec![] })],
Some(
vec![exst!(val!("q"))],
)
)
}
}
)])
};
/*
parse_test!("if a() then { b(); c() }", AST(vec![exst!(
IfExpression(bx!(ex!(Call { f: bx!(ex!(val!("a"))), arguments: vec![]})),
vec![exst!(Call { f: bx!(ex!(val!("b"))), arguments: vec![]}), exst!(Call { f: bx!(ex!(val!("c"))), arguments: vec![] })],
None)
)]));
parse_test!(r#"
if true then {
const a = 10
b
} else {
c
}"#,
AST(vec![exst!(IfExpression(bx!(ex!(BoolLiteral(true))),
vec![Declaration(Binding { name: rc!(a), constant: true, expr: ex!(NatLiteral(10)) }),
exst!(val!(rc!(b)))],
Some(vec![exst!(val!(rc!(c)))])))])
);
parse_test!("if a { b } else { c }", AST(vec![exst!(
IfExpression(bx!(ex!(val!("a"))),
vec![exst!(val!("b"))],
Some(vec![exst!(val!("c"))])))]));
parse_test!("if (A {a: 1}) { b } else { c }", AST(vec![exst!(
IfExpression(bx!(ex!(NamedStruct { name: rc!(A), fields: vec![(rc!(a), ex!(NatLiteral(1)))]})),
vec![exst!(val!("b"))],
Some(vec![exst!(val!("c"))])))]));
parse_error!("if A {a: 1} { b } else { c }");
*/
}
#[test]
fn parsing_interfaces() {
parse_test!("interface Unglueable { fn unglue(a: Glue); fn mar(): Glue }", AST(vec![
Meta::new(Declaration(Interface {
name: rc!(Unglueable),
signatures: vec![
Signature {
name: rc!(unglue),
operator: false,
params: vec![
FormalParam { name: rc!(a), anno: Some(Singleton(TypeSingletonName { name: rc!(Glue), params: vec![] })), default: None }
],
type_anno: None
},
Signature { name: rc!(mar), operator: false, params: vec![], type_anno: Some(Singleton(TypeSingletonName { name: rc!(Glue), params: vec![] })) },
]
}))
]));
}
#[test]
fn parsing_impls() {
parse_test!("impl Heh { fn yolo(); fn swagg(); }", AST(vec![
Meta::new(
Declaration(Impl {
type_name: ty!("Heh"),
interface_name: None,
block: vec![
FuncSig(Signature { name: rc!(yolo), operator: false, params: vec![], type_anno: None }),
FuncSig(Signature { name: rc!(swagg), operator: false, params: vec![], type_anno: None })
] }))]));
parse_test!("impl Mondai for Lollerino { fn yolo(); fn swagg(); }", AST(vec![
Meta::new(Declaration(Impl {
type_name: ty!("Lollerino"),
interface_name: Some(TypeSingletonName { name: rc!(Mondai), params: vec![] }),
block: vec![
FuncSig(Signature { name: rc!(yolo), operator: false, params: vec![], type_anno: None}),
FuncSig(Signature { name: rc!(swagg), operator: false, params: vec![], type_anno: None })
] }))]));
parse_test!("impl Hella<T> for (Alpha, Omega) { }", AST(vec![
Meta::new(Declaration(Impl {
type_name: Tuple(vec![ty!("Alpha"), ty!("Omega")]),
interface_name: Some(TypeSingletonName { name: rc!(Hella), params: vec![ty!("T")] }),
block: vec![]
}))
]));
parse_test!("impl Option<WTFMate> { fn oi() }", AST(vec![
Meta::new(
Declaration(Impl {
type_name: Singleton(TypeSingletonName { name: rc!(Option), params: vec![ty!("WTFMate")]}),
interface_name: None,
block: vec![
FuncSig(Signature { name: rc!(oi), operator: false, params: vec![], type_anno: None }),
]
}))]));
}
#[test]
fn parsing_type_annotations() {
parse_test!("let a = b : Int", AST(vec![
Meta::new(
Declaration(Binding { name: rc!(a), constant: true, type_anno: None, expr:
ex!(m val!("b"), ty!("Int")) }))]));
parse_test!("a : Int", AST(vec![
exst!(val!("a"), ty!("Int"))
]));
parse_test!("a : Option<Int>", AST(vec![
exst!(val!("a"), Singleton(TypeSingletonName { name: rc!(Option), params: vec![ty!("Int")] }))
]));
parse_test!("a : KoreanBBQSpecifier<Kimchi, Option<Bulgogi> >", AST(vec![
exst!(val!("a"), Singleton(TypeSingletonName { name: rc!(KoreanBBQSpecifier), params: vec![
ty!("Kimchi"), Singleton(TypeSingletonName { name: rc!(Option), params: vec![ty!("Bulgogi")] })
] }))
]));
parse_test!("a : (Int, Yolo<a>)", AST(vec![
exst!(val!("a"), Tuple(
vec![ty!("Int"), Singleton(TypeSingletonName {
name: rc!(Yolo), params: vec![ty!("a")]
})]))]));
}
#[test]
fn parsing_lambdas() {
parse_test_wrap_ast! { r#"\(x) { x + 1}"#, exst!(
Lambda { params: vec![FormalParam { name: rc!(x), anno: None, default: None } ], type_anno: None, body: vec![exst!(s "x + 1")] }
)
}
parse_test!(r#"\ (x: Int, y) { a;b;c;}"#, AST(vec![
exst!(Lambda {
params: vec![
FormalParam { name: rc!(x), anno: Some(ty!("Int")), default: None },
FormalParam { name: rc!(y), anno: None, default: None }
],
type_anno: None,
body: vec![exst!(s "a"), exst!(s "b"), exst!(s "c")]
})
]));
parse_test!(r#"\(x){y}(1)"#, AST(vec![
exst!(Call { f: bx!(ex!(m
Lambda {
params: vec![
FormalParam { name: rc!(x), anno: None, default: None }
],
type_anno: None,
body: vec![exst!(s "y")] }
)),
arguments: vec![inv!(ex!(NatLiteral(1))).into()] })]));
parse_test_wrap_ast! {
r#"\(x: Int): String { "q" }"#,
exst!(Lambda {
params: vec![
FormalParam { name: rc!(x), anno: Some(ty!("Int")), default: None },
],
type_anno: Some(ty!("String")),
body: vec![exst!(s r#""q""#)]
})
}
}
#[test]
fn single_param_lambda() {
parse_test_wrap_ast! {
r"\x { x + 10 }",
exst!(Lambda {
params: vec![FormalParam { name: rc!(x), anno: None, default: None }],
type_anno: None,
body: vec![exst!(s r"x + 10")]
})
}
parse_test_wrap_ast! {
r"\x: Nat { x + 10 }",
exst!(Lambda {
params: vec![FormalParam { name: rc!(x), anno: Some(ty!("Nat")), default: None }],
type_anno: None,
body: vec![exst!(s r"x + 10")]
})
}
}
#[test]
fn more_advanced_lambdas() {
parse_test! {
r#"fn wahoo() { let a = 10; \(x) { x + a } };
wahoo()(3) "#, AST(vec![
exst!(s r"fn wahoo() { let a = 10; \(x) { x + a } }"),
exst! {
Call {
f: bx!(ex!(m Call { f: bx!(ex!(m val!("wahoo"))), arguments: vec![] })),
arguments: vec![inv!(ex!(NatLiteral(3))).into()],
}
}
])
}
}
#[test]
fn list_literals() {
parse_test! {
"[1,2]", AST(vec![
exst!(ListLiteral(vec![ex!(m NatLiteral(1)), ex!(m NatLiteral(2))]))])
};
}
#[test]
fn while_expr() {
parse_test! {
"while { }", AST(vec![
exst!(WhileExpression { condition: None, body: vec![] })])
}
parse_test! {
"while a == b { }", AST(vec![
exst!(WhileExpression { condition: Some(bx![ex![m binexp!("==", val!("a"), val!("b"))]]), body: vec![] })])
}
}
#[test]
fn for_expr() {
parse_test! {
"for { a <- maybeValue } return 1", AST(vec![
exst!(ForExpression {
enumerators: vec![Enumerator { id: rc!(a), generator: ex!(m val!("maybeValue")) }],
body: bx!(MonadicReturn(Meta::new(ex!(s "1"))))
})])
}
parse_test! {
"for n <- someRange { f(n); }", AST(vec![
exst!(ForExpression { enumerators: vec![Enumerator { id: rc!(n), generator: ex!(m val!("someRange"))}],
body: bx!(ForBody::StatementBlock(vec![exst!(s "f(n)")]))
})])
}
}
#[test]
fn patterns() {
parse_test_wrap_ast! {
"if x is Some(a) then { 4 } else { 9 }", exst!(
IfExpression {
discriminator: bx!(Discriminator::Simple(ex!(s "x"))),
body: bx!(IfExpressionBody::SimplePatternMatch(Pattern::TupleStruct(rc!(Some), vec![Pattern::Literal(PatternLiteral::VarPattern(rc!(a)))]), vec![exst!(s "4")], Some(vec![exst!(s "9")]))) }
)
}
parse_test_wrap_ast! {
"if x is Some(a) then 4 else 9", exst!(
IfExpression {
discriminator: bx!(Discriminator::Simple(ex!(s "x"))),
body: bx!(IfExpressionBody::SimplePatternMatch(Pattern::TupleStruct(rc!(Some), vec![Pattern::Literal(PatternLiteral::VarPattern(rc!(a)))]), vec![exst!(s "4")], Some(vec![exst!(s "9")]))) }
)
}
parse_test_wrap_ast! {
"if x is Something { a, b: x } then { 4 } else { 9 }", exst!(
IfExpression {
discriminator: bx!(Discriminator::Simple(ex!(s "x"))),
body: bx!(IfExpressionBody::SimplePatternMatch(
Pattern::Record(rc!(Something), vec![
(rc!(a),Pattern::Literal(PatternLiteral::StringPattern(rc!(a)))),
(rc!(b),Pattern::Literal(PatternLiteral::VarPattern(rc!(x))))
]),
vec![exst!(s "4")], Some(vec![exst!(s "9")])))
}
)
}
}
#[test]
fn pattern_literals() {
parse_test_wrap_ast! {
"if x is -1 then 1 else 2",
exst!(
IfExpression {
discriminator: bx!(Discriminator::Simple(ex!(s "x"))),
body: bx!(IfExpressionBody::SimplePatternMatch(
Pattern::Literal(PatternLiteral::NumPattern { neg: true, num: NatLiteral(1) }),
vec![exst!(NatLiteral(1))],
Some(vec![exst!(NatLiteral(2))]),
))
}
)
}
parse_test_wrap_ast! {
"if x is 1 then 1 else 2",
exst!(
IfExpression {
discriminator: bx!(Discriminator::Simple(ex!(s "x"))),
body: bx!(IfExpressionBody::SimplePatternMatch(
Pattern::Literal(PatternLiteral::NumPattern { neg: false, num: NatLiteral(1) }),
vec![exst!(s "1")],
Some(vec![exst!(s "2")]),
))
}
)
}
parse_test! {
"if x is true then 1 else 2", AST(vec![
exst!(
IfExpression {
discriminator: bx!(Discriminator::Simple(ex!(s "x"))),
body: bx!(IfExpressionBody::SimplePatternMatch(
Pattern::Literal(PatternLiteral::BoolPattern(true)),
vec![exst!(NatLiteral(1))],
Some(vec![exst!(NatLiteral(2))]),
))
}
)
])
}
parse_test_wrap_ast! {
"if x is \"gnosticism\" then 1 else 2",
exst!(
IfExpression {
discriminator: bx!(Discriminator::Simple(ex!(s "x"))),
body: bx!(IfExpressionBody::SimplePatternMatch(
Pattern::Literal(PatternLiteral::StringPattern(rc!(gnosticism))),
vec![exst!(s "1")],
Some(vec![exst!(s "2")]),
))
}
)
}
}

View File

@@ -3,7 +3,6 @@ use std::rc::Rc;
use crate::ast::*;
use crate::symbol_table::{Symbol, SymbolSpec, SymbolTable};
use crate::builtin::{BinOp, PrefixOp};
use crate::visitor::{ASTVisitor, Visitable};
#[derive(Debug)]
pub struct ReducedAST(pub Vec<Stmt>);
@@ -95,31 +94,6 @@ pub enum Func {
}
}
struct Reducer<'a> {
symbol_table: &'a SymbolTable,
reduced_ast: ReducedAST,
}
impl<'a> Reducer<'a> {
fn new(symbol_table: &'a SymbolTable) -> Reducer {
Reducer { symbol_table, reduced_ast: ReducedAST(vec![]) }
}
fn done(self) -> Result<ReducedAST, String> {
Ok(self.reduced_ast)
}
}
impl<'a> ASTVisitor for Reducer<'a> {
}
pub fn perform_reduction(mut input: AST, symbol_table: &SymbolTable) -> Result<ReducedAST, String> {
let mut reducer = Reducer::new(symbol_table);
let _ = input.visit(&mut reducer).map_err(|e| e.to_string())?;
reducer.done()
}
impl AST {
pub fn reduce(&self, symbol_table: &SymbolTable) -> ReducedAST {
let mut output = vec![];
@@ -144,21 +118,10 @@ fn reduce_block(block: &Block, symbol_table: &SymbolTable) -> Vec<Stmt> {
block.iter().map(|stmt| stmt.node().reduce(symbol_table)).collect()
}
impl InvocationArgument {
fn reduce(&self, symbol_table: &SymbolTable) -> Expr {
use crate::ast::InvocationArgument::*;
match self {
Positional(ex) => ex.reduce(symbol_table),
Keyword { .. } => Expr::UnimplementedSigilValue,
Ignored => Expr::UnimplementedSigilValue,
}
}
}
impl Expression {
fn reduce(&self, symbol_table: &SymbolTable) -> Expr {
use crate::ast::ExpressionKind::*;
let ref input = self.kind;
let ref input = self.0;
match input {
NatLiteral(n) => Expr::Lit(Lit::Nat(*n)),
FloatLiteral(f) => Expr::Lit(Lit::Float(*f)),
@@ -175,7 +138,10 @@ impl Expression {
},
_ => Expr::Val(name.clone()),
},
Call { f, arguments } => reduce_call_expression(f, arguments, symbol_table),
Call { f, arguments } => Expr::Call {
f: Box::new(f.node().reduce(symbol_table)),
args: arguments.iter().map(|arg| arg.node().reduce(symbol_table)).collect(),
},
TupleLiteral(exprs) => Expr::Tuple(exprs.iter().map(|e| e.node().reduce(symbol_table)).collect()),
IfExpression { discriminator, body } => reduce_if_expression(discriminator, body, symbol_table),
Lambda { params, body, .. } => reduce_lambda(params, body, symbol_table),
@@ -191,18 +157,11 @@ impl Expression {
fn reduce_lambda(params: &Vec<FormalParam>, body: &Block, symbol_table: &SymbolTable) -> Expr {
Expr::Func(Func::UserDefined {
name: None,
params: params.iter().map(|param| param.name.clone()).collect(),
params: params.iter().map(|param| param.0.clone()).collect(),
body: reduce_block(body, symbol_table),
})
}
fn reduce_call_expression(func: &Meta<Expression>, arguments: &Vec<Meta<InvocationArgument>>, symbol_table: &SymbolTable) -> Expr {
Expr::Call {
f: Box::new(func.node().reduce(symbol_table)),
args: arguments.iter().map(|arg| arg.node().reduce(symbol_table)).collect(),
}
}
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),
@@ -403,7 +362,7 @@ impl Declaration {
name: name.clone(),
func: Func::UserDefined {
name: Some(name.clone()),
params: params.iter().map(|param| param.name.clone()).collect(),
params: params.iter().map(|param| param.0.clone()).collect(),
body: reduce_block(&statements, symbol_table),
}
},

View File

@@ -1,312 +0,0 @@
use stopwatch::Stopwatch;
use std::time::Duration;
use std::cell::RefCell;
use std::rc::Rc;
use std::collections::HashSet;
use itertools::Itertools;
use schala_repl::{ProgrammingLanguageInterface,
ComputationRequest, ComputationResponse,
LangMetaRequest, LangMetaResponse, GlobalOutputStats,
DebugResponse, DebugAsk};
use crate::{ast, reduced_ast, tokenizing, parsing, eval, typechecking, symbol_table};
/// All the state necessary to parse and execute a Schala program are stored in this struct.
/// `state` represents the execution state for the AST-walking interpreter, the other fields
/// should be self-explanatory.
pub struct Schala {
source_reference: SourceReference,
state: eval::State<'static>,
symbol_table: Rc<RefCell<symbol_table::SymbolTable>>,
type_context: typechecking::TypeContext<'static>,
active_parser: Option<parsing::Parser>,
}
impl Schala {
fn handle_docs(&self, source: String) -> LangMetaResponse {
LangMetaResponse::Docs {
doc_string: format!("Schala item `{}` : <<Schala-lang documentation not yet implemented>>", source)
}
}
}
impl Schala {
/// Creates a new Schala environment *without* any prelude.
fn new_blank_env() -> Schala {
let symbols = Rc::new(RefCell::new(symbol_table::SymbolTable::new()));
Schala {
source_reference: SourceReference::new(),
symbol_table: symbols.clone(),
state: eval::State::new(symbols),
type_context: typechecking::TypeContext::new(),
active_parser: None,
}
}
/// Creates a new Schala environment with the standard prelude, which is defined as ordinary
/// Schala code in the file `prelude.schala`
pub fn new() -> Schala {
let prelude = include_str!("prelude.schala");
let mut s = Schala::new_blank_env();
let request = ComputationRequest { source: prelude, debug_requests: HashSet::default() };
s.run_computation(request);
s
}
fn handle_debug_immediate(&self, request: DebugAsk) -> DebugResponse {
use DebugAsk::*;
match request {
Timing => DebugResponse { ask: Timing, value: format!("Invalid") },
ByStage { stage_name, token } => match &stage_name[..] {
"symbol-table" => {
let value = self.symbol_table.borrow().debug_symbol_table();
DebugResponse {
ask: ByStage { stage_name: format!("symbol-table"), token },
value
}
},
s => {
DebugResponse {
ask: ByStage { stage_name: s.to_string(), token: None },
value: format!("Not-implemented")
}
}
}
}
}
}
fn tokenizing(input: &str, _handle: &mut Schala, comp: Option<&mut PassDebugArtifact>) -> Result<Vec<tokenizing::Token>, String> {
let tokens = tokenizing::tokenize(input);
comp.map(|comp| {
let token_string = tokens.iter().map(|t| t.to_string_with_metadata()).join(", ");
comp.add_artifact(token_string);
});
let errors: Vec<String> = tokens.iter().filter_map(|t| t.get_error()).collect();
if errors.len() == 0 {
Ok(tokens)
} else {
Err(format!("{:?}", errors))
}
}
fn parsing(input: Vec<tokenizing::Token>, handle: &mut Schala, comp: Option<&mut PassDebugArtifact>) -> Result<ast::AST, String> {
use crate::parsing::Parser;
let mut parser = match handle.active_parser.take() {
None => Parser::new(input),
Some(parser) => parser
};
let ast = parser.parse();
let trace = parser.format_parse_trace();
comp.map(|comp| {
let debug_info = match comp.parsing.as_ref().unwrap_or(&ParsingDebugType::CompactAST) {
ParsingDebugType::CompactAST => format!("{:?}", ast),
ParsingDebugType::ExpandedAST => format!("{:#?}", ast),
ParsingDebugType::Trace => format!("{}", trace[0]) //TODO fix this
};
comp.add_artifact(debug_info);
});
ast.map_err(|err| format_parse_error(err, handle))
}
fn format_parse_error(error: parsing::ParseError, handle: &mut Schala) -> String {
let line_num = error.token.line_num;
let ch = error.token.char_num;
let line_from_program = handle.source_reference.get_line(line_num);
let location_pointer = format!("{}^", " ".repeat(ch));
let line_num_digits = format!("{}", line_num).chars().count();
let space_padding = " ".repeat(line_num_digits);
format!(r#"
{error_msg}
{space_padding} |
{line_num} | {}
{space_padding} | {}
"#, line_from_program, location_pointer, error_msg=error.msg, space_padding=space_padding, line_num=line_num)
}
fn symbol_table(input: ast::AST, handle: &mut Schala, comp: Option<&mut PassDebugArtifact>) -> Result<ast::AST, String> {
let add = handle.symbol_table.borrow_mut().add_top_level_symbols(&input);
match add {
Ok(()) => {
let debug = handle.symbol_table.borrow().debug_symbol_table();
comp.map(|comp| comp.add_artifact(debug));
Ok(input)
},
Err(msg) => Err(msg)
}
}
fn typechecking(input: ast::AST, handle: &mut Schala, comp: Option<&mut PassDebugArtifact>) -> Result<ast::AST, String> {
let result = handle.type_context.typecheck(&input);
comp.map(|comp| {
comp.add_artifact(match result {
Ok(ty) => ty.to_string(),
Err(err) => format!("Type error: {}", err.msg)
});
});
Ok(input)
}
fn ast_reducing(input: ast::AST, handle: &mut Schala, comp: Option<&mut PassDebugArtifact>) -> Result<reduced_ast::ReducedAST, String> {
let ref symbol_table = handle.symbol_table.borrow();
//let output = input.reduce(symbol_table);
let output = reduced_ast::perform_reduction(input, symbol_table);
comp.map(|comp| comp.add_artifact(format!("{:?}", output)));
output
}
fn eval(input: reduced_ast::ReducedAST, handle: &mut Schala, comp: Option<&mut PassDebugArtifact>) -> Result<String, String> {
comp.map(|comp| comp.add_artifact(handle.state.debug_print()));
let evaluation_outputs = handle.state.evaluate(input, true);
let text_output: Result<Vec<String>, String> = evaluation_outputs
.into_iter()
.collect();
let eval_output: Result<String, String> = text_output
.map(|v| { v.into_iter().intersperse(format!("\n")).collect() });
eval_output
}
/// Represents lines of source code
struct SourceReference {
lines: Option<Vec<String>>
}
impl SourceReference {
fn new() -> SourceReference {
SourceReference { lines: None }
}
fn load_new_source(&mut self, source: &str) {
//TODO this is a lot of heap allocations - maybe there's a way to make it more efficient?
self.lines = Some(source.lines().map(|s| s.to_string()).collect()); }
fn get_line(&self, line: usize) -> String {
self.lines.as_ref().and_then(|x| x.get(line).map(|s| s.to_string())).unwrap_or(format!("NO LINE FOUND"))
}
}
enum ParsingDebugType {
CompactAST,
ExpandedAST,
Trace
}
#[derive(Default)]
struct PassDebugArtifact {
parsing: Option<ParsingDebugType>,
artifacts: Vec<String>
}
impl PassDebugArtifact {
fn add_artifact(&mut self, artifact: String) {
self.artifacts.push(artifact)
}
}
fn stage_names() -> Vec<&'static str> {
vec![
"tokenizing",
"parsing",
"symbol-table",
"typechecking",
"ast-reduction",
"ast-walking-evaluation"
]
}
impl ProgrammingLanguageInterface for Schala {
fn get_language_name(&self) -> String { format!("Schala") }
fn get_source_file_suffix(&self) -> String { format!("schala") }
fn run_computation(&mut self, request: ComputationRequest) -> ComputationResponse {
struct PassToken<'a> {
schala: &'a mut Schala,
stage_durations: &'a mut Vec<(String, Duration)>,
sw: &'a Stopwatch,
debug_requests: &'a HashSet<DebugAsk>,
debug_responses: &'a mut Vec<DebugResponse>,
}
fn output_wrapper<Input, Output, F>(n: usize, func: F, input: Input, token: &mut PassToken) -> Result<Output, String>
where F: Fn(Input, &mut Schala, Option<&mut PassDebugArtifact>) -> Result<Output, String>
{
let stage_names = stage_names();
let cur_stage_name = stage_names[n];
let ask = token.debug_requests.iter().find(|ask| ask.is_for_stage(cur_stage_name));
let mut debug_artifact = ask.and_then(|ask| match ask {
DebugAsk::ByStage { token, .. } => token.as_ref(),
_ => None
}).map(|token| {
let parsing = if cur_stage_name != "parsing" {
None
} else {
Some(match &token[..] {
"compact" => ParsingDebugType::CompactAST,
"expanded" => ParsingDebugType::ExpandedAST,
"trace" => ParsingDebugType::Trace,
_ => ParsingDebugType::CompactAST,
})
};
PassDebugArtifact { parsing, ..Default::default() }
});
let output = func(input, token.schala, debug_artifact.as_mut());
token.stage_durations.push((cur_stage_name.to_string(), token.sw.elapsed()));
if let Some(artifact) = debug_artifact {
for value in artifact.artifacts.into_iter() {
let resp = DebugResponse { ask: ask.unwrap().clone(), value };
token.debug_responses.push(resp);
}
}
output
}
let ComputationRequest { source, debug_requests } = request;
self.source_reference.load_new_source(source);
let sw = Stopwatch::start_new();
let mut stage_durations = Vec::new();
let mut debug_responses = Vec::new();
let mut tok = PassToken { schala: self, stage_durations: &mut stage_durations, sw: &sw, debug_requests: &debug_requests, debug_responses: &mut debug_responses };
let main_output: Result<String, String> = Ok(source)
.and_then(|source| output_wrapper(0, tokenizing, source, &mut tok))
.and_then(|tokens| output_wrapper(1, parsing, tokens, &mut tok))
.and_then(|ast| output_wrapper(2, symbol_table, ast, &mut tok))
.and_then(|ast| output_wrapper(3, typechecking, ast, &mut tok))
.and_then(|ast| output_wrapper(4, ast_reducing, ast, &mut tok))
.and_then(|reduced_ast| output_wrapper(5, eval, reduced_ast, &mut tok));
let total_duration = sw.elapsed();
let global_output_stats = GlobalOutputStats {
total_duration, stage_durations
};
ComputationResponse {
main_output,
global_output_stats,
debug_responses,
}
}
fn request_meta(&mut self, request: LangMetaRequest) -> LangMetaResponse {
match request {
LangMetaRequest::StageNames => LangMetaResponse::StageNames(stage_names().iter().map(|s| s.to_string()).collect()),
LangMetaRequest::Docs { source } => self.handle_docs(source),
LangMetaRequest::ImmediateDebug(debug_request) =>
LangMetaResponse::ImmediateDebug(self.handle_debug_immediate(debug_request)),
LangMetaRequest::Custom { .. } => LangMetaResponse::Custom { kind: format!("not-implemented"), value: format!("") }
}
}
}

View File

@@ -169,9 +169,9 @@ impl SymbolTable {
fn add_function_signature(&mut self, signature: &Signature, scope_name_stack: &mut Vec<ScopeSegment>) -> Result<(), String> {
let mut local_type_context = LocalTypeContext::new();
let types = signature.params.iter().map(|param| match param.anno {
Some(ref type_identifier) => Rc::new(format!("{:?}", type_identifier)),
None => local_type_context.new_universal_type()
let types = signature.params.iter().map(|param| match param {
(_, Some(type_identifier)) => Rc::new(format!("{:?}", type_identifier)),
(_, None) => local_type_context.new_universal_type()
}).collect();
self.add_new_symbol(&signature.name, scope_name_stack, SymbolSpec::Func(types));
Ok(())

View File

@@ -15,7 +15,7 @@ pub enum TokenKind {
Pipe, Backslash,
Comma, Period, Colon, Underscore,
Slash, Equals,
Slash,
Operator(Rc<String>),
DigitGroup(Rc<String>), HexLiteral(Rc<String>), BinNumberSigil,
@@ -118,7 +118,7 @@ type CharData = (usize, usize, char);
pub fn tokenize(input: &str) -> Vec<Token> {
let mut tokens: Vec<Token> = Vec::new();
let mut input = input.lines().enumerate()
let mut input = input.lines().enumerate()
.intersperse((0, "\n"))
.flat_map(|(line_idx, ref line)| {
line.chars().enumerate().map(move |(ch_idx, ch)| (line_idx, ch_idx, ch))
@@ -238,7 +238,7 @@ fn handle_alphabetic(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>
fn handle_operator(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenKind {
match c {
'<' | '>' | '|' | '.' | '=' => {
'<' | '>' | '|' | '.' => {
let ref next = input.peek().map(|&(_, _, c)| { c });
if !next.map(|n| { is_operator(&n) }).unwrap_or(false) {
return match c {
@@ -246,7 +246,6 @@ fn handle_operator(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>)
'>' => RAngleBracket,
'|' => Pipe,
'.' => Period,
'=' => Equals,
_ => unreachable!(),
}
}
@@ -299,7 +298,7 @@ mod schala_tokenizer_tests {
let a = tokenize("let a: A<B> = c ++ d");
let token_kinds: Vec<TokenKind> = a.into_iter().map(move |t| t.kind).collect();
assert_eq!(token_kinds, vec![Keyword(Let), ident!("a"), Colon, ident!("A"),
LAngleBracket, ident!("B"), RAngleBracket, Equals, ident!("c"), op!("++"), ident!("d")]);
LAngleBracket, ident!("B"), RAngleBracket, op!("="), ident!("c"), op!("++"), ident!("d")]);
}
#[test]

View File

@@ -289,22 +289,14 @@ impl<'a> TypeContext<'a> {
Ok(ty!(Unit))
}
fn invoc(&mut self, invoc: &InvocationArgument) -> InferResult<Type> {
use InvocationArgument::*;
match invoc {
Positional(expr) => self.expr(expr),
_ => Ok(ty!(Nat)) //TODO this is wrong
}
}
fn expr(&mut self, expr: &Expression) -> InferResult<Type> {
match expr {
Expression { kind, type_anno: Some(anno) } => {
let t1 = self.expr_type(kind)?;
Expression(expr_type, Some(anno)) => {
let t1 = self.expr_type(expr_type)?;
let t2 = self.get_type_from_name(anno)?;
self.unify(t2, t1)
},
Expression { kind, type_anno: None } => self.expr_type(kind)
Expression(expr_type, None) => self.expr_type(expr_type)
}
}
@@ -369,7 +361,7 @@ impl<'a> TypeContext<'a> {
fn lambda(&mut self, params: &Vec<FormalParam>, type_anno: &Option<TypeIdentifier>, _body: &Block) -> InferResult<Type> {
let argument_types: InferResult<Vec<Type>> = params.iter().map(|param: &FormalParam| {
if let FormalParam { anno: Some(type_identifier), .. } = param {
if let (_, Some(type_identifier)) = param {
self.get_type_from_name(type_identifier)
} else {
Ok(Type::Var(self.fresh_type_variable()))
@@ -384,9 +376,9 @@ impl<'a> TypeContext<'a> {
Ok(ty!(argument_types, ret_type))
}
fn call(&mut self, f: &Expression, args: &Vec<Meta<InvocationArgument>>) -> InferResult<Type> {
fn call(&mut self, f: &Expression, args: &Vec<Meta<Expression>>) -> InferResult<Type> {
let tf = self.expr(f)?;
let arg_types: InferResult<Vec<Type>> = args.iter().map(|ex| self.invoc(ex.node())).collect();
let arg_types: InferResult<Vec<Type>> = args.iter().map(|ex| self.expr(ex.node())).collect();
let arg_types = arg_types?;
self.handle_apply(tf, arg_types)
}

View File

@@ -1,205 +0,0 @@
use std::error::Error;
use crate::ast::*;
use crate::builtin::{PrefixOp, BinOp};
pub type VResult = Result<(), Box<dyn Error>>;
pub trait ASTVisitor {
fn post_ast(&mut self, _: &mut AST) -> VResult {
Ok(())
}
fn post_statement(&mut self, _: &mut Statement) -> VResult {
Ok(())
}
fn post_expression(&mut self, _: &mut Expression) -> VResult {
Ok(())
}
}
pub trait Visitable {
fn visit(&mut self, v: &mut dyn ASTVisitor) -> VResult;
}
impl <T: Visitable> Visitable for &mut T {
fn visit(&mut self, v: &mut dyn ASTVisitor) -> VResult {
self.visit(v)
}
}
impl<T: Visitable> Visitable for Option<T> {
fn visit(&mut self, v: &mut dyn ASTVisitor) -> VResult {
match self {
Some(t) => t.visit(v),
None => Ok(())
}
}
}
impl<T: Visitable> Visitable for Vec<T> {
fn visit(&mut self, v: &mut dyn ASTVisitor) -> VResult {
for item in self {
item.visit(v)?;
}
Ok(())
}
}
impl <T: Visitable> Visitable for Box<T> {
fn visit(&mut self, v: &mut dyn ASTVisitor) -> VResult {
self.visit(v)
}
}
impl <T: Visitable> Visitable for Meta<T> {
fn visit(&mut self, v: &mut dyn ASTVisitor) -> VResult {
self.node_mut().visit(v)
}
}
impl Visitable for AST {
fn visit(&mut self, v: &mut dyn ASTVisitor) -> VResult {
for s in self.0.iter_mut() {
s.visit(v)?
}
v.post_ast(self)
}
}
impl Visitable for Statement {
fn visit(&mut self, v: &mut dyn ASTVisitor) -> VResult {
match self {
Statement::ExpressionStatement(meta) => meta.visit(v),
Statement::Declaration(decl) => decl.visit(v),
}
}
}
impl Visitable for Declaration {
fn visit(&mut self, _v: &mut dyn ASTVisitor) -> VResult {
use Declaration::*;
Ok(match self {
FuncSig(sig) => (),
FuncDecl(sig, block) => (),
TypeDecl { .. } => (),
TypeAlias(_, _) => (),
Binding { .. } => (),
Impl { .. } => (),
Interface { .. } => (),
})
}
}
impl Visitable for Expression {
fn visit(&mut self, v: &mut dyn ASTVisitor) -> VResult {
let _ = self.kind.visit(v)?;
let _ = self.type_anno.visit(v)?;
v.post_expression(self)
}
}
impl Visitable for ExpressionKind {
fn visit(&mut self, v: &mut dyn ASTVisitor) -> VResult {
use ExpressionKind::*;
Ok(match self {
NatLiteral(_) | FloatLiteral(_) | StringLiteral(_)
| BoolLiteral(_) => (),
BinExp(op, lhs, rhs) => {
let _ = op.visit(v)?;
let _ = lhs.visit(v)?;
let _ = rhs.visit(v)?;
()
},
PrefixExp(op, expr) => {
let _ = op.visit(v)?;
let _ = expr.visit(v)?;
()
},
TupleLiteral(exprs) => exprs.visit(v)?,
Value(_) => (),
NamedStruct { fields, .. } => {
let mut x: Vec<_> = fields.iter_mut().map(|(_, expr)| expr).collect();
x.visit(v)?
},
Call { f, arguments } => {
let _ = f.visit(v)?;
arguments.visit(v)?
},
Index { indexee, indexers } => {
let _ = indexee.visit(v)?;
indexers.visit(v)?
},
IfExpression { discriminator, body } => {
let _ = discriminator.visit(v)?;
body.visit(v)?
},
WhileExpression { condition, body } => {
let _ = condition.visit(v)?;
body.visit(v)?
},
ForExpression { enumerators, body } => {
let _ = enumerators.visit(v)?;
body.visit(v)?
},
ListLiteral(expressions) => expressions.visit(v)?,
Lambda { params, type_anno, body } => {
let _ = params.visit(v)?;
let _ = type_anno.visit(v)?;
body.visit(v)?
},
})
}
}
impl Visitable for IfExpressionBody {
fn visit(&mut self, v: &mut dyn ASTVisitor) -> VResult {
Ok(())
}
}
impl Visitable for Discriminator {
fn visit(&mut self, v: &mut dyn ASTVisitor) -> VResult {
Ok(())
}
}
impl Visitable for InvocationArgument {
fn visit(&mut self, v: &mut dyn ASTVisitor) -> VResult {
Ok(())
}
}
impl Visitable for BinOp {
fn visit(&mut self, v: &mut dyn ASTVisitor) -> VResult {
Ok(())
}
}
impl Visitable for PrefixOp {
fn visit(&mut self, v: &mut dyn ASTVisitor) -> VResult {
Ok(())
}
}
impl Visitable for TypeIdentifier {
fn visit(&mut self, _v: &mut dyn ASTVisitor) -> VResult {
Ok(())
}
}
impl Visitable for Enumerator {
fn visit(&mut self, _v: &mut dyn ASTVisitor) -> VResult {
Ok(())
}
}
impl Visitable for ForBody {
fn visit(&mut self, _v: &mut dyn ASTVisitor) -> VResult {
Ok(())
}
}
impl Visitable for FormalParam {
fn visit(&mut self, _v: &mut dyn ASTVisitor) -> VResult {
Ok(())
}
}

View File

@@ -11,13 +11,13 @@ itertools = "0.5.8"
getopts = "0.2.18"
lazy_static = "0.2.8"
maplit = "*"
colored = "1.8"
colored = "1.7"
serde = "1.0.91"
serde_derive = "1.0.91"
serde_json = "1.0.15"
phf = "0.7.12"
includedir = "0.2.0"
linefeed = "0.6.0"
linefeed = "0.5.0"
regex = "0.2"
[build-dependencies]

View File

@@ -38,16 +38,7 @@ pub struct GlobalOutputStats {
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize)]
pub enum DebugAsk {
Timing,
ByStage { stage_name: String, token: Option<String> },
}
impl DebugAsk {
pub fn is_for_stage(&self, name: &str) -> bool {
match self {
DebugAsk::ByStage { stage_name, .. } if stage_name == name => true,
_ => false
}
}
ByStage { stage_name: String },
}
pub struct DebugResponse {

View File

@@ -51,7 +51,7 @@ pub fn start_repl(langs: Vec<Box<dyn ProgrammingLanguageInterface>>) {
};
}
fn run_noninteractive(filename: &str, languages: Vec<Box<dyn ProgrammingLanguageInterface>>) {
fn run_noninteractive(filename: &str, languages: Vec<Box<ProgrammingLanguageInterface>>) {
let path = Path::new(filename);
let ext = path.extension().and_then(|e| e.to_str()).unwrap_or_else(|| {
println!("Source file lacks extension");

View File

@@ -1,99 +0,0 @@
use super::{Repl, InterpreterDirectiveOutput};
use crate::repl::directive_actions::DirectiveAction;
use colored::*;
/// A CommandTree is either a `Terminal` or a `NonTerminal`. When command parsing reaches the first
/// Terminal, it will use the `DirectiveAction` found there to find an appropriate function to execute,
/// and then execute it with any remaining arguments
#[derive(Clone)]
pub enum CommandTree {
Terminal {
name: String,
children: Vec<CommandTree>,
help_msg: Option<String>,
action: DirectiveAction,
},
NonTerminal {
name: String,
children: Vec<CommandTree>,
help_msg: Option<String>,
action: DirectiveAction,
},
Top(Vec<CommandTree>),
}
impl CommandTree {
pub fn nonterm_no_further_tab_completions(s: &str, help: Option<&str>) -> CommandTree {
CommandTree::NonTerminal {name: s.to_string(), help_msg: help.map(|x| x.to_string()), children: vec![], action: DirectiveAction::Null }
}
pub fn terminal(s: &str, help: Option<&str>, children: Vec<CommandTree>, action: DirectiveAction) -> CommandTree {
CommandTree::Terminal {name: s.to_string(), help_msg: help.map(|x| x.to_string()), children, action}
}
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,
action: DirectiveAction::Null
}
}
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<CommandTree> {
use CommandTree::*;
match self {
Terminal { children, .. } |
NonTerminal { children, .. } |
Top(children) => children
}
}
pub fn get_subcommands(&self) -> Vec<&str> {
self.get_children().iter().map(|x| x.get_cmd()).collect()
}
pub fn perform(&self, repl: &mut Repl, arguments: &Vec<&str>) -> InterpreterDirectiveOutput {
let mut dir_pointer: &CommandTree = self;
let mut idx = 0;
let res: Result<(DirectiveAction, usize), String> = loop {
match dir_pointer {
CommandTree::Top(subcommands) | CommandTree::NonTerminal { children: subcommands, .. } => {
let next_command = match arguments.get(idx) {
Some(cmd) => cmd,
None => break Err(format!("Command requires arguments"))
};
idx += 1;
match subcommands.iter().find(|sc| sc.get_cmd() == *next_command) {
Some(command_tree) => {
dir_pointer = command_tree;
},
None => break Err(format!("Command {} not found", next_command))
};
},
CommandTree::Terminal { action, .. } => {
break Ok((action.clone(), idx));
},
}
};
match res {
Ok((action, idx)) => action.perform(repl, &arguments[idx..]),
Err(err) => Some(err.red().to_string())
}
}
}

View File

@@ -1,133 +0,0 @@
use super::{Repl, InterpreterDirectiveOutput};
use crate::repl::help::help;
use crate::language::{LangMetaRequest, LangMetaResponse, DebugAsk, DebugResponse};
use itertools::Itertools;
use std::fmt::Write as FmtWrite;
#[derive(Debug, Clone)]
pub enum DirectiveAction {
Null,
Help,
QuitProgram,
ListPasses,
ShowImmediate,
Show,
Hide,
TotalTimeOff,
TotalTimeOn,
StageTimeOff,
StageTimeOn,
Doc,
}
impl DirectiveAction {
pub fn perform(&self, repl: &mut Repl, arguments: &[&str]) -> InterpreterDirectiveOutput {
use DirectiveAction::*;
match self {
Null => None,
Help => help(repl, arguments),
QuitProgram => {
repl.save_before_exit();
::std::process::exit(0)
},
ListPasses => {
let language_state = repl.get_cur_language_state();
let pass_names = match language_state.request_meta(LangMetaRequest::StageNames) {
LangMetaResponse::StageNames(names) => names,
_ => vec![],
};
let mut buf = String::new();
for pass in pass_names.iter().map(|name| Some(name)).intersperse(None) {
match pass {
Some(pass) => write!(buf, "{}", pass).unwrap(),
None => write!(buf, " -> ").unwrap(),
}
}
Some(buf)
},
ShowImmediate => {
let cur_state = repl.get_cur_language_state();
let stage_name = match arguments.get(1) {
Some(s) => s.to_string(),
None => return Some(format!("Must specify a thing to debug")),
};
let meta = LangMetaRequest::ImmediateDebug(DebugAsk::ByStage { stage_name: stage_name.clone(), token: None });
let meta_response = cur_state.request_meta(meta);
let response = match meta_response {
LangMetaResponse::ImmediateDebug(DebugResponse { ask, value }) => match ask {
DebugAsk::ByStage { stage_name: ref this_stage_name, ..} if *this_stage_name == stage_name => value,
_ => return Some(format!("Wrong debug stage"))
},
_ => return Some(format!("Invalid language meta response")),
};
Some(response)
},
Show => {
let this_stage_name = match arguments.get(0) {
Some(s) => s.to_string(),
None => return Some(format!("Must specify a stage to show")),
};
let token = arguments.get(1).map(|s| s.to_string());
repl.options.debug_asks.retain(|ask| match ask {
DebugAsk::ByStage { stage_name, .. } if *stage_name == this_stage_name => false,
_ => true
});
let ask = DebugAsk::ByStage { stage_name: this_stage_name, token };
repl.options.debug_asks.insert(ask);
None
},
Hide => {
let stage_name_to_remove = match arguments.get(0) {
Some(s) => s.to_string(),
None => return Some(format!("Must specify a stage to hide")),
};
repl.options.debug_asks.retain(|ask| match ask {
DebugAsk::ByStage { stage_name, .. } if *stage_name == stage_name_to_remove => false,
_ => true
});
None
},
TotalTimeOff => total_time_off(repl, arguments),
TotalTimeOn => total_time_on(repl, arguments),
StageTimeOff => stage_time_off(repl, arguments),
StageTimeOn => stage_time_on(repl, arguments),
Doc => doc(repl, arguments),
}
}
}
fn total_time_on(repl: &mut Repl, _: &[&str]) -> InterpreterDirectiveOutput {
repl.options.show_total_time = true;
None
}
fn total_time_off(repl: &mut Repl, _: &[&str]) -> InterpreterDirectiveOutput {
repl.options.show_total_time = false;
None
}
fn stage_time_on(repl: &mut Repl, _: &[&str]) -> InterpreterDirectiveOutput {
repl.options.show_stage_times = true;
None
}
fn stage_time_off(repl: &mut Repl, _: &[&str]) -> InterpreterDirectiveOutput {
repl.options.show_stage_times = false;
None
}
fn doc(repl: &mut Repl, arguments: &[&str]) -> InterpreterDirectiveOutput {
arguments.get(0).map(|cmd| {
let source = cmd.to_string();
let meta = LangMetaRequest::Docs { source };
let cur_state = repl.get_cur_language_state();
match cur_state.request_meta(meta) {
LangMetaResponse::Docs { doc_string } => Some(doc_string),
_ => Some(format!("Invalid doc response"))
}
}).unwrap_or(Some(format!(":docs needs an argument")))
}

View File

@@ -1,55 +1,126 @@
use crate::repl::command_tree::CommandTree;
use crate::repl::directive_actions::DirectiveAction;
use std::fmt::Write as FmtWrite;
use itertools::Itertools;
pub fn directives_from_pass_names(pass_names: &Vec<String>) -> CommandTree {
let passes_directives: Vec<CommandTree> = pass_names.iter()
.map(|pass_name| {
if pass_name == "parsing" {
CommandTree::nonterm(pass_name, None, vec![
CommandTree::nonterm_no_further_tab_completions("compact", None),
CommandTree::nonterm_no_further_tab_completions("expanded", None),
CommandTree::nonterm_no_further_tab_completions("trace", None),
])
} else {
CommandTree::nonterm_no_further_tab_completions(pass_name, None)
}
})
use crate::repl::Repl;
use crate::repl::old_command_tree::OldCommandTree;
use crate::language::{LangMetaRequest, LangMetaResponse, DebugAsk, DebugResponse};
pub fn directives_from_pass_names(pass_names: &Vec<String>) -> OldCommandTree {
let passes_directives: Vec<OldCommandTree> = pass_names.iter()
.map(|pass_name| { OldCommandTree::nonterm_no_further_tab_completions(pass_name, None) })
.collect();
CommandTree::Top(get_list(&passes_directives, true))
}
fn get_list(passes_directives: &Vec<CommandTree>, include_help: bool) -> Vec<CommandTree> {
use DirectiveAction::*;
vec![
CommandTree::terminal("exit", Some("exit the REPL"), vec![], QuitProgram),
CommandTree::terminal("quit", Some("exit the REPL"), vec![], QuitProgram),
CommandTree::terminal("help", Some("Print this help message"), if include_help { get_list(passes_directives, false) } else { vec![] }, Help),
CommandTree::nonterm("debug",
OldCommandTree::Top(vec![
OldCommandTree::terminal("exit", Some("exit the REPL"), vec![], Box::new(|repl: &mut Repl, _cmds: &[&str]| {
repl.save_before_exit();
::std::process::exit(0)
})),
OldCommandTree::terminal("quit", Some("exit the REPL"), vec![], Box::new(|repl: &mut Repl, _cmds: &[&str]| {
repl.save_before_exit();
::std::process::exit(0)
})),
OldCommandTree::terminal("help", Some("Print this help message"), vec![], Box::new(|repl: &mut Repl, cmds: &[&str]| {
Some(repl.print_help_message(cmds))
})),
OldCommandTree::nonterm("debug",
Some("Configure debug information"),
vec![
CommandTree::terminal("list-passes", Some("List all registered compiler passes"), vec![], ListPasses),
CommandTree::terminal("show-immediate", None, passes_directives.clone(), ShowImmediate),
CommandTree::terminal("show", Some("Show debug output for a specific pass"), passes_directives.clone(), Show),
CommandTree::terminal("hide", Some("Hide debug output for a specific pass"), passes_directives.clone(), Hide),
CommandTree::nonterm("total-time", None, vec![
CommandTree::terminal("on", None, vec![], TotalTimeOn),
CommandTree::terminal("off", None, vec![], TotalTimeOff),
OldCommandTree::terminal("list-passes", Some("List all registered compiler passes"), vec![], Box::new(|repl: &mut Repl, _cmds: &[&str]| {
let language_state = repl.get_cur_language_state();
let pass_names = match language_state.request_meta(LangMetaRequest::StageNames) {
LangMetaResponse::StageNames(names) => names,
_ => vec![],
};
let mut buf = String::new();
for pass in pass_names.iter().map(|name| Some(name)).intersperse(None) {
match pass {
Some(pass) => write!(buf, "{}", pass).unwrap(),
None => write!(buf, " -> ").unwrap(),
}
}
Some(buf)
})),
OldCommandTree::terminal("show-immediate", None, passes_directives.clone(),
Box::new(|repl: &mut Repl, cmds: &[&str]| {
let cur_state = repl.get_cur_language_state();
let stage_name = match cmds.get(1) {
Some(s) => s.to_string(),
None => return Some(format!("Must specify a thing to debug")),
};
let meta = LangMetaRequest::ImmediateDebug(DebugAsk::ByStage { stage_name: stage_name.clone() });
let response = match cur_state.request_meta(meta) {
LangMetaResponse::ImmediateDebug(DebugResponse { ask, value }) => {
if (ask != DebugAsk::ByStage { stage_name: stage_name }) {
return Some(format!("Didn't get debug stage requested"));
}
value
},
_ => return Some(format!("Invalid language meta response")),
};
Some(response)
})),
OldCommandTree::terminal("show", None, passes_directives.clone(), Box::new(|repl: &mut Repl, cmds: &[&str]| {
let stage_name = match cmds.get(0) {
Some(s) => s.to_string(),
None => return Some(format!("Must specify a stage to show")),
};
let ask = DebugAsk::ByStage { stage_name };
repl.options.debug_asks.insert(ask);
None
})),
OldCommandTree::terminal("hide", None, passes_directives.clone(), Box::new(|repl: &mut Repl, cmds: &[&str]| {
let stage_name = match cmds.get(0) {
Some(s) => s.to_string(),
None => return Some(format!("Must specify a stage to hide")),
};
let ask = DebugAsk::ByStage { stage_name };
repl.options.debug_asks.remove(&ask);
None
})),
OldCommandTree::nonterm("total-time", None, vec![
OldCommandTree::terminal("on", None, vec![], Box::new(|repl: &mut Repl, _: &[&str]| {
repl.options.show_total_time = true;
None
})),
OldCommandTree::terminal("off", None, vec![], Box::new(turn_off)),
]),
CommandTree::nonterm("stage-times", Some("Computation time per-stage"), vec![
CommandTree::terminal("on", None, vec![], StageTimeOn),
CommandTree::terminal("off", None, vec![], StageTimeOff),
OldCommandTree::nonterm("stage-times", Some("Computation time per-stage"), vec![
OldCommandTree::terminal("on", None, vec![], Box::new(|repl: &mut Repl, _: &[&str]| {
repl.options.show_stage_times = true;
None
})),
OldCommandTree::terminal("off", None, vec![], Box::new(|repl: &mut Repl, _: &[&str]| {
repl.options.show_stage_times = false;
None
})),
])
]
),
CommandTree::nonterm("lang",
OldCommandTree::nonterm("lang",
Some("switch between languages, or go directly to a langauge by name"),
vec![
CommandTree::nonterm_no_further_tab_completions("next", None),
CommandTree::nonterm_no_further_tab_completions("prev", None),
CommandTree::nonterm("go", None, vec![]),
OldCommandTree::nonterm_no_further_tab_completions("next", None),
OldCommandTree::nonterm_no_further_tab_completions("prev", None),
OldCommandTree::nonterm("go", None, vec![]),
]
),
CommandTree::terminal("doc", Some("Get language-specific help for an item"), vec![], Doc),
]
OldCommandTree::terminal("doc", Some("Get language-specific help for an item"), vec![], Box::new(|repl: &mut Repl, cmds: &[&str]| {
cmds.get(0).map(|cmd| {
let source = cmd.to_string();
let meta = LangMetaRequest::Docs { source };
let cur_state = repl.get_cur_language_state();
match cur_state.request_meta(meta) {
LangMetaResponse::Docs { doc_string } => Some(doc_string),
_ => Some(format!("Invalid doc response"))
}
}).unwrap_or(Some(format!(":docs needs an argument")))
}))
])
}
fn turn_off(repl: &mut Repl, _cmds: &[&str]) -> Option<String> {
repl.options.show_total_time = false;
None
}

View File

@@ -1,54 +0,0 @@
use std::fmt::Write as FmtWrite;
use colored::*;
use super::command_tree::CommandTree;
use super::{Repl, InterpreterDirectiveOutput};
pub fn help(repl: &mut Repl, arguments: &[&str]) -> InterpreterDirectiveOutput {
match arguments {
[] => return global_help(repl),
commands => {
let dirs = repl.get_directives();
Some(match get_directive_from_commands(commands, &dirs) {
None => format!("Directive `{}` not found", commands.last().unwrap()),
Some(dir) => {
let mut buf = String::new();
writeln!(buf, "`{}` - {}", dir.get_cmd(), dir.get_help()).unwrap();
buf
}
})
}
}
}
fn get_directive_from_commands<'a>(commands: &[&str], dirs: &'a CommandTree) -> Option<&'a CommandTree> {
let mut directive_list = dirs.get_children();
let mut matched_directive = None;
for cmd in commands {
let found = directive_list.iter().find(|directive| directive.get_cmd() == *cmd);
if let Some(dir) = found {
directive_list = dir.get_children();
}
matched_directive = found;
}
matched_directive
}
fn global_help(repl: &mut Repl) -> InterpreterDirectiveOutput {
let mut buf = String::new();
let sigil = repl.interpreter_directive_sigil;
writeln!(buf, "{} version {}", "Schala REPL".bright_red().bold(), crate::VERSION_STRING).unwrap();
writeln!(buf, "-----------------------").unwrap();
for directive in repl.get_directives().get_children() {
writeln!(buf, "{}{} - {}", sigil, directive.get_cmd(), directive.get_help()).unwrap();
}
let ref lang = repl.get_cur_language_state();
writeln!(buf, "").unwrap();
writeln!(buf, "Language-specific help for {}", lang.get_language_name()).unwrap();
writeln!(buf, "-----------------------").unwrap();
Some(buf)
}

View File

@@ -1,43 +1,47 @@
use std::fmt::Write as FmtWrite;
use std::sync::Arc;
use std::collections::HashSet;
use colored::*;
use crate::language::{ProgrammingLanguageInterface,
ComputationRequest, LangMetaResponse, LangMetaRequest};
ComputationRequest, ComputationResponse,
DebugAsk, LangMetaResponse, LangMetaRequest};
mod command_tree;
use self::command_tree::CommandTree;
mod old_command_tree;
use self::old_command_tree::{OldCommandTree, BoxedCommandFunction};
mod repl_options;
use repl_options::ReplOptions;
mod directive_actions;
mod directives;
use directives::directives_from_pass_names;
mod help;
mod response;
use response::ReplResponse;
const HISTORY_SAVE_FILE: &'static str = ".schala_history";
const OPTIONS_SAVE_FILE: &'static str = ".schala_repl";
type InterpreterDirectiveOutput = Option<String>;
pub struct Repl {
pub interpreter_directive_sigil: char,
interpreter_directive_sigil: char,
line_reader: ::linefeed::interface::Interface<::linefeed::terminal::DefaultTerminal>,
language_states: Vec<Box<dyn ProgrammingLanguageInterface>>,
language_states: Vec<Box<ProgrammingLanguageInterface>>,
options: ReplOptions,
directives: OldCommandTree,
}
impl Repl {
pub fn new(initial_states: Vec<Box<dyn ProgrammingLanguageInterface>>) -> Repl {
pub fn new(mut initial_states: Vec<Box<ProgrammingLanguageInterface>>) -> Repl {
use linefeed::Interface;
let line_reader = Interface::new("schala-repl").unwrap();
let interpreter_directive_sigil = ':';
let pass_names = match initial_states[0].request_meta(LangMetaRequest::StageNames) {
LangMetaResponse::StageNames(names) => names,
_ => vec![],
};
Repl {
interpreter_directive_sigil,
line_reader,
language_states: initial_states,
options: ReplOptions::new(),
directives: directives_from_pass_names(&pass_names)
}
}
@@ -73,16 +77,12 @@ impl Repl {
Ok(Eof) | Ok(Signal(_)) => break,
Ok(Input(ref input)) => {
self.line_reader.add_history_unique(input.to_string());
match input.chars().nth(0) {
Some(ch) if ch == self.interpreter_directive_sigil => match self.handle_interpreter_directive(input) {
Some(directive_output) => println!("<> {}", directive_output),
None => (),
},
_ => {
for repl_response in self.handle_input(input) {
println!("{}", repl_response);
}
}
let output = match input.chars().nth(0) {
Some(ch) if ch == self.interpreter_directive_sigil => self.handle_interpreter_directive(input),
_ => Some(self.handle_input(input)),
};
if let Some(o) = output {
println!("=> {}", o);
}
}
}
@@ -101,40 +101,133 @@ impl Repl {
self.options.save_to_file(OPTIONS_SAVE_FILE);
}
fn handle_interpreter_directive(&mut self, input: &str) -> InterpreterDirectiveOutput {
fn get_function_from_directives<'a>(directives: &'a OldCommandTree, commands: &Vec<&str>) -> Result<(&'a BoxedCommandFunction, usize), String> {
let mut dir_pointer: &OldCommandTree = &directives;
let mut idx = 0;
loop {
match dir_pointer {
OldCommandTree::Top(subcommands) | OldCommandTree::NonTerminal { children: subcommands, .. } => {
let next_command = match commands.get(idx) {
Some(cmd) => cmd,
None => break Err(format!("Command requires arguments"))
};
idx += 1;
match subcommands.iter().find(|sc| sc.get_cmd() == *next_command) {
Some(command_tree) => {
dir_pointer = command_tree;
},
None => break Err(format!("Command {} not found", next_command))
};
},
OldCommandTree::Terminal { function, .. } => {
break Ok((function, idx));
},
}
}
}
fn handle_interpreter_directive(&mut self, input: &str) -> Option<String> {
let mut iter = input.chars();
iter.next();
let arguments: Vec<&str> = iter
let commands: Vec<&str> = iter
.as_str()
.split_whitespace()
.collect();
if arguments.len() < 1 {
if commands.len() < 1 {
return None;
}
let directives = self.get_directives();
directives.perform(self, &arguments)
let result: Result<(&BoxedCommandFunction, _), String> = Repl::get_function_from_directives(&directives, &commands);
match result {
Ok((f, idx)) => f(self, &commands[idx..]),
Err(err) => Some(err.red().to_string())
}
}
fn get_cur_language_state(&mut self) -> &mut Box<dyn ProgrammingLanguageInterface> {
fn print_help_message(&mut self, commands_passed_to_help: &[&str] ) -> String {
let mut buf = String::new();
let directives = match self.get_directives() {
OldCommandTree::Top(children) => children,
_ => panic!("Top-level OldCommandTree not Top")
};
match commands_passed_to_help {
[] => {
writeln!(buf, "MetaInterpreter options").unwrap();
writeln!(buf, "-----------------------").unwrap();
for directive in directives {
let trailer = " ";
writeln!(buf, "{}{}- {}", directive.get_cmd(), trailer, directive.get_help()).unwrap();
}
let ref lang = self.get_cur_language_state();
writeln!(buf, "").unwrap();
writeln!(buf, "Language-specific help for {}", lang.get_language_name()).unwrap();
writeln!(buf, "-----------------------").unwrap();
},
_ => {
writeln!(buf, "Command-specific help not available yet").unwrap();
}
};
buf
}
fn get_cur_language_state(&mut self) -> &mut Box<ProgrammingLanguageInterface> {
//TODO this is obviously not complete
&mut self.language_states[0]
}
fn handle_input(&mut self, input: &str) -> Vec<ReplResponse> {
fn handle_input(&mut self, input: &str) -> String {
let mut debug_requests = HashSet::new();
for ask in self.options.debug_asks.iter() {
debug_requests.insert(ask.clone());
}
let request = ComputationRequest { source: input, debug_requests };
let request = ComputationRequest {
source: input,
debug_requests,
};
let ref mut language_state = self.get_cur_language_state();
let response = language_state.run_computation(request);
response::handle_computation_response(response, &self.options)
self.handle_computation_response(response)
}
fn get_directives(&mut self) -> CommandTree {
fn handle_computation_response(&mut self, response: ComputationResponse) -> String {
let mut buf = String::new();
if self.options.show_total_time {
buf.push_str(&format!("Total duration: {:?}\n", response.global_output_stats.total_duration));
}
if self.options.show_stage_times {
buf.push_str(&format!("{:?}\n", response.global_output_stats.stage_durations));
}
for debug_resp in response.debug_responses {
let stage_name = match debug_resp.ask {
DebugAsk::ByStage { stage_name } => stage_name,
_ => continue,
};
let s = format!("{} - {}\n", stage_name, debug_resp.value);
buf.push_str(&s);
}
buf.push_str(&match response.main_output {
Ok(s) => s,
Err(e) => format!("{} {}", "Error".red(), e)
});
buf
}
fn get_directives(&mut self) -> OldCommandTree {
let language_state = self.get_cur_language_state();
let pass_names = match language_state.request_meta(LangMetaRequest::StageNames) {
LangMetaResponse::StageNames(names) => names,
@@ -148,14 +241,14 @@ impl Repl {
struct TabCompleteHandler {
sigil: char,
top_level_commands: CommandTree,
top_level_commands: OldCommandTree,
}
use linefeed::complete::{Completion, Completer};
use linefeed::terminal::Terminal;
impl TabCompleteHandler {
fn new(sigil: char, top_level_commands: CommandTree) -> TabCompleteHandler {
fn new(sigil: char, top_level_commands: OldCommandTree) -> TabCompleteHandler {
TabCompleteHandler {
top_level_commands,
sigil,
@@ -173,17 +266,17 @@ impl<T: Terminal> Completer<T> for TabCompleteHandler {
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);
let mut command_tree: Option<&OldCommandTree> = Some(&self.top_level_commands);
loop {
match words.next() {
None => {
let top = match command_tree {
Some(CommandTree::Top(_)) => true,
Some(OldCommandTree::Top(_)) => true,
_ => false
};
let word = if top { word.get(1..).unwrap() } else { word };
for cmd in command_tree.map(|x| x.get_subcommands()).unwrap_or(vec![]).into_iter() {
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),
@@ -195,10 +288,10 @@ impl<T: Terminal> Completer<T> for TabCompleteHandler {
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 { children, .. } => children.iter().find(|c| c.get_cmd() == s),
let new_ptr: Option<&OldCommandTree> = command_tree.and_then(|cm| match cm {
OldCommandTree::Top(children) => children.iter().find(|c| c.get_cmd() == s),
OldCommandTree::NonTerminal { children, .. } => children.iter().find(|c| c.get_cmd() == s),
OldCommandTree::Terminal { children, .. } => children.iter().find(|c| c.get_cmd() == s),
});
command_tree = new_ptr;
}

View File

@@ -0,0 +1,73 @@
use super::Repl;
pub type BoxedCommandFunction = Box<(fn(&mut Repl, &[&str]) -> Option<String>)>;
/// A OldCommandTree is either a `Terminal` or a `NonTerminal`. When command parsing reaches the first
/// Terminal, it will execute the `BoxedCommandFunction` found there with any remaining arguments
#[derive(Clone)]
pub enum OldCommandTree {
Terminal {
name: String,
children: Vec<OldCommandTree>,
help_msg: Option<String>,
function: BoxedCommandFunction,
},
NonTerminal {
name: String,
children: Vec<OldCommandTree>,
help_msg: Option<String>,
},
Top(Vec<OldCommandTree>),
}
impl OldCommandTree {
pub fn nonterm_no_further_tab_completions(s: &str, help: Option<&str>) -> OldCommandTree {
OldCommandTree::NonTerminal {name: s.to_string(), help_msg: help.map(|x| x.to_string()), children: vec![] }
}
pub fn terminal(s: &str, help: Option<&str>, children: Vec<OldCommandTree>, function: BoxedCommandFunction) -> OldCommandTree {
OldCommandTree::Terminal {name: s.to_string(), help_msg: help.map(|x| x.to_string()), function, children }
}
pub fn nonterm(s: &str, help: Option<&str>, children: Vec<OldCommandTree>) -> OldCommandTree {
OldCommandTree::NonTerminal {
name: s.to_string(),
help_msg: help.map(|x| x.to_string()),
children,
}
}
/*
pub fn nonterm_with_function(s: &str, help: Option<&str>, children: Vec<OldCommandTree>, func: BoxedCommandFunction) -> OldCommandTree {
OldCommandTree::NonTerminal {
name: s.to_string(),
help_msg: help.map(|x| x.to_string()),
children,
function: Some(func),
}
}
*/
pub fn get_cmd(&self) -> &str {
match self {
OldCommandTree::Terminal { name, .. } => name.as_str(),
OldCommandTree::NonTerminal {name, ..} => name.as_str(),
OldCommandTree::Top(_) => "",
}
}
pub fn get_help(&self) -> &str {
match self {
OldCommandTree::Terminal { help_msg, ..} => help_msg.as_ref().map(|s| s.as_str()).unwrap_or(""),
OldCommandTree::NonTerminal { help_msg, .. } => help_msg.as_ref().map(|s| s.as_str()).unwrap_or(""),
OldCommandTree::Top(_) => ""
}
}
pub fn get_children(&self) -> Vec<&str> {
use OldCommandTree::*;
match self {
Terminal { children, .. } |
NonTerminal { children, .. } |
Top(children) => children.iter().map(|x| x.get_cmd()).collect()
}
}
}

View File

@@ -1,67 +0,0 @@
use colored::*;
use std::fmt;
use std::fmt::Write;
use super::ReplOptions;
use crate::language::{ DebugAsk, ComputationResponse};
pub struct ReplResponse {
label: Option<String>,
text: String,
color: Option<Color>
}
impl fmt::Display for ReplResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut buf = String::new();
if let Some(ref label) = self.label {
write!(buf, "({})", label).unwrap();
}
write!(buf, "=> {}", self.text).unwrap();
write!(f, "{}", match self.color {
Some(c) => buf.color(c),
None => buf.normal()
})
}
}
pub fn handle_computation_response(response: ComputationResponse, options: &ReplOptions) -> Vec<ReplResponse> {
let mut responses = vec![];
if options.show_total_time {
responses.push(ReplResponse {
label: Some("Total time".to_string()),
text: format!("{:?}", response.global_output_stats.total_duration),
color: None,
});
}
if options.show_stage_times {
responses.push(ReplResponse {
label: Some("Stage times".to_string()),
text: format!("{:?}", response.global_output_stats.stage_durations),
color: None,
});
}
for debug_resp in response.debug_responses {
let stage_name = match debug_resp.ask {
DebugAsk::ByStage { stage_name, .. } => stage_name,
_ => continue,
};
responses.push(ReplResponse {
label: Some(stage_name.to_string()),
text: debug_resp.value,
color: Some(Color::Red),
});
}
responses.push(match response.main_output {
Ok(s) => ReplResponse { label: None, text: s, color: None },
Err(e) => ReplResponse { label: Some("Error".to_string()), text: e, color: Some(Color::Red) },
});
responses
}

View File

@@ -9,7 +9,7 @@ use schala_repl::{ProgrammingLanguageInterface, start_repl};
extern { }
fn main() {
let langs: Vec<Box<dyn ProgrammingLanguageInterface>> = vec![Box::new(schala_lang::Schala::new())];
let langs: Vec<Box<ProgrammingLanguageInterface>> = vec![Box::new(schala_lang::Schala::new())];
start_repl(langs);
}