Compare commits
53 Commits
new-comman
...
visitor-pa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d24d48825 | ||
|
|
c3c515284d | ||
|
|
0903277b69 | ||
|
|
94c4dec9a9 | ||
|
|
42bc4f091c | ||
|
|
be2dcb5301 | ||
|
|
88029fc55a | ||
|
|
37c77d93d7 | ||
|
|
b62968379a | ||
|
|
aa705b4eee | ||
|
|
d67ccf5c7a | ||
|
|
d9330bed26 | ||
|
|
efe65edfe6 | ||
|
|
7c9154de53 | ||
|
|
10e40669b5 | ||
|
|
ca37e006b9 | ||
|
|
6d3f5f4b81 | ||
|
|
e3bd108e6c | ||
|
|
2ec3b21ebf | ||
|
|
b6e3469573 | ||
|
|
32fe7430a4 | ||
|
|
c332747c3e | ||
|
|
33c2786ea1 | ||
|
|
30498d5c98 | ||
|
|
bc01a5ded8 | ||
|
|
71386be80e | ||
|
|
ccdc02bbd0 | ||
|
|
3a207cf7a7 | ||
|
|
66f71606ef | ||
|
|
53ce31ea8c | ||
|
|
4c688ce8b2 | ||
|
|
40579d80ce | ||
|
|
fa1257e2cd | ||
|
|
e9fd20bfe5 | ||
|
|
dfbd951aaf | ||
|
|
6b47ecf2d7 | ||
|
|
a8b9f5046e | ||
|
|
83e05fe382 | ||
|
|
5271429715 | ||
|
|
f88f2e8550 | ||
|
|
7097775a4a | ||
|
|
32d082e119 | ||
|
|
376fa1d1d1 | ||
|
|
6fb9b4c2d3 | ||
|
|
f1d1042916 | ||
|
|
207f73d607 | ||
|
|
8dc0ad2348 | ||
|
|
bb39c59db2 | ||
|
|
10bfeab7e4 | ||
|
|
fe08e64860 | ||
|
|
fd517351de | ||
|
|
e12ff6f30b | ||
|
|
176b286332 |
@@ -59,6 +59,11 @@ much like Haskell
|
|||||||
Here's a partial list of resources I've made use of in the process
|
Here's a partial list of resources I've made use of in the process
|
||||||
of learning how to write a programming language.
|
of learning how to write a programming language.
|
||||||
|
|
||||||
|
### General
|
||||||
|
|
||||||
|
http://thume.ca/2019/04/18/writing-a-compiler-in-rust/
|
||||||
|
http://thume.ca/2019/07/14/a-tour-of-metaprogramming-models-for-generics/
|
||||||
|
|
||||||
### Type-checking
|
### Type-checking
|
||||||
https://skillsmatter.com/skillscasts/10868-inside-the-rust-compiler
|
https://skillsmatter.com/skillscasts/10868-inside-the-rust-compiler
|
||||||
https://www.youtube.com/watch?v=il3gD7XMdmA
|
https://www.youtube.com/watch?v=il3gD7XMdmA
|
||||||
|
|||||||
20
TODO.md
20
TODO.md
@@ -7,8 +7,6 @@
|
|||||||
- cf. the notation mentioned in the cardelli paper, the debug information for the `typechecking` pass should
|
- 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
|
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)
|
- 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
|
-should have an Idris-like `cast To From` function
|
||||||
@@ -17,8 +15,6 @@ print the generated type variable for every subexpression in an expression
|
|||||||
|
|
||||||
-idea: the `type` declaration should have some kind of GADT-like syntax
|
-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
|
- 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
|
instead of treating this as a type error, promote the bound variable to an option type
|
||||||
|
|
||||||
@@ -69,6 +65,8 @@ ex.
|
|||||||
|
|
||||||
## Playing around with conditional syntax ideas
|
## Playing around with conditional syntax ideas
|
||||||
|
|
||||||
|
-
|
||||||
|
|
||||||
- if/match playground
|
- if/match playground
|
||||||
|
|
||||||
simple if
|
simple if
|
||||||
@@ -107,17 +105,3 @@ if the only two guard patterns are true and false, then the abbreviated syntax:
|
|||||||
`'if' discriminator 'then' block_or_expr 'else' block_or_expr`
|
`'if' discriminator 'then' block_or_expr 'else' block_or_expr`
|
||||||
can replace `'if' discriminator '{' 'true' 'then' block_or_expr; 'false' 'then' block_or_expr '}'`
|
can replace `'if' discriminator '{' 'true' 'then' block_or_expr; 'false' 'then' block_or_expr '}'`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
3
VISITOR_NOTES
Normal file
3
VISITOR_NOTES
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
-each terminal node in the AST requires a method on ASTVisitor
|
||||||
|
-this can maybe be done with a macro?
|
||||||
@@ -43,7 +43,13 @@ pub enum Statement {
|
|||||||
|
|
||||||
pub type Block = Vec<Meta<Statement>>;
|
pub type Block = Vec<Meta<Statement>>;
|
||||||
pub type ParamName = Rc<String>;
|
pub type ParamName = Rc<String>;
|
||||||
pub type FormalParam = (ParamName, Option<TypeIdentifier>);
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub struct FormalParam {
|
||||||
|
pub name: ParamName,
|
||||||
|
pub default: Option<Expression>,
|
||||||
|
pub anno: Option<TypeIdentifier>
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum Declaration {
|
pub enum Declaration {
|
||||||
@@ -94,8 +100,20 @@ pub enum Variant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct Expression(pub ExpressionKind, pub Option<TypeIdentifier>);
|
pub struct Expression {
|
||||||
|
pub kind: ExpressionKind,
|
||||||
|
pub type_anno: 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)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum TypeIdentifier {
|
pub enum TypeIdentifier {
|
||||||
@@ -125,7 +143,7 @@ pub enum ExpressionKind {
|
|||||||
},
|
},
|
||||||
Call {
|
Call {
|
||||||
f: Box<Meta<Expression>>,
|
f: Box<Meta<Expression>>,
|
||||||
arguments: Vec<Meta<Expression>>,
|
arguments: Vec<Meta<InvocationArgument>>,
|
||||||
},
|
},
|
||||||
Index {
|
Index {
|
||||||
indexee: Box<Meta<Expression>>,
|
indexee: Box<Meta<Expression>>,
|
||||||
@@ -151,6 +169,16 @@ pub enum ExpressionKind {
|
|||||||
ListLiteral(Vec<Meta<Expression>>),
|
ListLiteral(Vec<Meta<Expression>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum InvocationArgument {
|
||||||
|
Positional(Expression),
|
||||||
|
Keyword {
|
||||||
|
name: Rc<String>,
|
||||||
|
expr: Expression,
|
||||||
|
},
|
||||||
|
Ignored
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum Discriminator {
|
pub enum Discriminator {
|
||||||
Simple(Expression),
|
Simple(Expression),
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ impl BinOp {
|
|||||||
Slash => "/",
|
Slash => "/",
|
||||||
LAngleBracket => "<",
|
LAngleBracket => "<",
|
||||||
RAngleBracket => ">",
|
RAngleBracket => ">",
|
||||||
|
Equals => "=",
|
||||||
_ => return None
|
_ => return None
|
||||||
};
|
};
|
||||||
Some(BinOp::from_sigil(s))
|
Some(BinOp::from_sigil(s))
|
||||||
@@ -47,6 +48,7 @@ impl BinOp {
|
|||||||
Slash => "/",
|
Slash => "/",
|
||||||
LAngleBracket => "<",
|
LAngleBracket => "<",
|
||||||
RAngleBracket => ">",
|
RAngleBracket => ">",
|
||||||
|
Equals => "=",
|
||||||
_ => return None
|
_ => return None
|
||||||
};
|
};
|
||||||
let default = 10_000_000;
|
let default = 10_000_000;
|
||||||
|
|||||||
@@ -532,8 +532,10 @@ mod eval_tests {
|
|||||||
fn test_basic_eval() {
|
fn test_basic_eval() {
|
||||||
test_in_fresh_env!("1 + 2", "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", "Unit");
|
||||||
|
/*
|
||||||
test_in_fresh_env!("let mut a = 1; a = 2; a", "2");
|
test_in_fresh_env!("let mut a = 1; a = 2; a", "2");
|
||||||
test_in_fresh_env!(r#"("a", 1 + 2)"#, r#"("a", 3)"#);
|
test_in_fresh_env!(r#"("a", 1 + 2)"#, r#"("a", 3)"#);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#![feature(slice_patterns, box_patterns, box_syntax)]
|
#![feature(slice_patterns, box_patterns, box_syntax)]
|
||||||
|
|
||||||
//! `schala-lang` is where the Schala programming language is actually implemented.
|
//! `schala-lang` is where the Schala programming language is actually implemented.
|
||||||
//! It defines the `Schala` type, which contains the state for a Schala REPL, and implements
|
//! The crate defines the `Schala` type, which contains the state for a Schala REPL, and implements
|
||||||
//! `ProgrammingLanguageInterface` and the chain of compiler passes for it.
|
//! `ProgrammingLanguageInterface` and the chain of compiler passes for it.
|
||||||
|
|
||||||
extern crate itertools;
|
extern crate itertools;
|
||||||
@@ -17,28 +17,10 @@ extern crate schala_repl;
|
|||||||
extern crate schala_lang_codegen;
|
extern crate schala_lang_codegen;
|
||||||
extern crate ena;
|
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) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod util;
|
mod util;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod typechecking;
|
mod typechecking;
|
||||||
|
|
||||||
mod tokenizing;
|
mod tokenizing;
|
||||||
mod ast;
|
mod ast;
|
||||||
mod parsing;
|
mod parsing;
|
||||||
@@ -46,287 +28,7 @@ mod symbol_table;
|
|||||||
mod builtin;
|
mod builtin;
|
||||||
mod reduced_ast;
|
mod reduced_ast;
|
||||||
mod eval;
|
mod eval;
|
||||||
|
mod schala;
|
||||||
|
mod visitor;
|
||||||
|
|
||||||
/// All bits of state necessary to parse and execute a Schala program are stored in this struct.
|
pub use schala::Schala;
|
||||||
/// `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 } => 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
662
schala-lang/language/src/parsing/test.rs
Normal file
662
schala-lang/language/src/parsing/test.rs
Normal file
@@ -0,0 +1,662 @@
|
|||||||
|
#![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")]),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -118,10 +118,21 @@ fn reduce_block(block: &Block, symbol_table: &SymbolTable) -> Vec<Stmt> {
|
|||||||
block.iter().map(|stmt| stmt.node().reduce(symbol_table)).collect()
|
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 {
|
impl Expression {
|
||||||
fn reduce(&self, symbol_table: &SymbolTable) -> Expr {
|
fn reduce(&self, symbol_table: &SymbolTable) -> Expr {
|
||||||
use crate::ast::ExpressionKind::*;
|
use crate::ast::ExpressionKind::*;
|
||||||
let ref input = self.0;
|
let ref input = self.kind;
|
||||||
match input {
|
match input {
|
||||||
NatLiteral(n) => Expr::Lit(Lit::Nat(*n)),
|
NatLiteral(n) => Expr::Lit(Lit::Nat(*n)),
|
||||||
FloatLiteral(f) => Expr::Lit(Lit::Float(*f)),
|
FloatLiteral(f) => Expr::Lit(Lit::Float(*f)),
|
||||||
@@ -138,10 +149,7 @@ impl Expression {
|
|||||||
},
|
},
|
||||||
_ => Expr::Val(name.clone()),
|
_ => Expr::Val(name.clone()),
|
||||||
},
|
},
|
||||||
Call { f, arguments } => Expr::Call {
|
Call { f, arguments } => reduce_call_expression(f, arguments, symbol_table),
|
||||||
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()),
|
TupleLiteral(exprs) => Expr::Tuple(exprs.iter().map(|e| e.node().reduce(symbol_table)).collect()),
|
||||||
IfExpression { discriminator, body } => reduce_if_expression(discriminator, body, symbol_table),
|
IfExpression { discriminator, body } => reduce_if_expression(discriminator, body, symbol_table),
|
||||||
Lambda { params, body, .. } => reduce_lambda(params, body, symbol_table),
|
Lambda { params, body, .. } => reduce_lambda(params, body, symbol_table),
|
||||||
@@ -157,11 +165,18 @@ impl Expression {
|
|||||||
fn reduce_lambda(params: &Vec<FormalParam>, body: &Block, symbol_table: &SymbolTable) -> Expr {
|
fn reduce_lambda(params: &Vec<FormalParam>, body: &Block, symbol_table: &SymbolTable) -> Expr {
|
||||||
Expr::Func(Func::UserDefined {
|
Expr::Func(Func::UserDefined {
|
||||||
name: None,
|
name: None,
|
||||||
params: params.iter().map(|param| param.0.clone()).collect(),
|
params: params.iter().map(|param| param.name.clone()).collect(),
|
||||||
body: reduce_block(body, symbol_table),
|
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 {
|
fn reduce_if_expression(discriminator: &Discriminator, body: &IfExpressionBody, symbol_table: &SymbolTable) -> Expr {
|
||||||
let cond = Box::new(match *discriminator {
|
let cond = Box::new(match *discriminator {
|
||||||
Discriminator::Simple(ref expr) => expr.reduce(symbol_table),
|
Discriminator::Simple(ref expr) => expr.reduce(symbol_table),
|
||||||
@@ -362,7 +377,7 @@ impl Declaration {
|
|||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
func: Func::UserDefined {
|
func: Func::UserDefined {
|
||||||
name: Some(name.clone()),
|
name: Some(name.clone()),
|
||||||
params: params.iter().map(|param| param.0.clone()).collect(),
|
params: params.iter().map(|param| param.name.clone()).collect(),
|
||||||
body: reduce_block(&statements, symbol_table),
|
body: reduce_block(&statements, symbol_table),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
305
schala-lang/language/src/schala.rs
Normal file
305
schala-lang/language/src/schala.rs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 = handle.active_parser.take().unwrap_or_else(|| Parser::new(input));
|
||||||
|
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> {
|
||||||
|
handle.symbol_table.borrow_mut().add_top_level_symbols(&input).map(|()| {
|
||||||
|
comp.map(|comp| {
|
||||||
|
let debug = handle.symbol_table.borrow().debug_symbol_table();
|
||||||
|
comp.add_artifact(debug);
|
||||||
|
});
|
||||||
|
input
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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!("") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -169,9 +169,9 @@ impl SymbolTable {
|
|||||||
|
|
||||||
fn add_function_signature(&mut self, signature: &Signature, scope_name_stack: &mut Vec<ScopeSegment>) -> Result<(), String> {
|
fn add_function_signature(&mut self, signature: &Signature, scope_name_stack: &mut Vec<ScopeSegment>) -> Result<(), String> {
|
||||||
let mut local_type_context = LocalTypeContext::new();
|
let mut local_type_context = LocalTypeContext::new();
|
||||||
let types = signature.params.iter().map(|param| match param {
|
let types = signature.params.iter().map(|param| match param.anno {
|
||||||
(_, Some(type_identifier)) => Rc::new(format!("{:?}", type_identifier)),
|
Some(ref type_identifier) => Rc::new(format!("{:?}", type_identifier)),
|
||||||
(_, None) => local_type_context.new_universal_type()
|
None => local_type_context.new_universal_type()
|
||||||
}).collect();
|
}).collect();
|
||||||
self.add_new_symbol(&signature.name, scope_name_stack, SymbolSpec::Func(types));
|
self.add_new_symbol(&signature.name, scope_name_stack, SymbolSpec::Func(types));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub enum TokenKind {
|
|||||||
Pipe, Backslash,
|
Pipe, Backslash,
|
||||||
|
|
||||||
Comma, Period, Colon, Underscore,
|
Comma, Period, Colon, Underscore,
|
||||||
Slash,
|
Slash, Equals,
|
||||||
|
|
||||||
Operator(Rc<String>),
|
Operator(Rc<String>),
|
||||||
DigitGroup(Rc<String>), HexLiteral(Rc<String>), BinNumberSigil,
|
DigitGroup(Rc<String>), HexLiteral(Rc<String>), BinNumberSigil,
|
||||||
@@ -118,7 +118,7 @@ type CharData = (usize, usize, char);
|
|||||||
pub fn tokenize(input: &str) -> Vec<Token> {
|
pub fn tokenize(input: &str) -> Vec<Token> {
|
||||||
let mut tokens: Vec<Token> = Vec::new();
|
let mut tokens: Vec<Token> = Vec::new();
|
||||||
|
|
||||||
let mut input = input.lines().enumerate()
|
let mut input = input.lines().enumerate()
|
||||||
.intersperse((0, "\n"))
|
.intersperse((0, "\n"))
|
||||||
.flat_map(|(line_idx, ref line)| {
|
.flat_map(|(line_idx, ref line)| {
|
||||||
line.chars().enumerate().map(move |(ch_idx, ch)| (line_idx, ch_idx, ch))
|
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 {
|
fn handle_operator(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenKind {
|
||||||
match c {
|
match c {
|
||||||
'<' | '>' | '|' | '.' => {
|
'<' | '>' | '|' | '.' | '=' => {
|
||||||
let ref next = input.peek().map(|&(_, _, c)| { c });
|
let ref next = input.peek().map(|&(_, _, c)| { c });
|
||||||
if !next.map(|n| { is_operator(&n) }).unwrap_or(false) {
|
if !next.map(|n| { is_operator(&n) }).unwrap_or(false) {
|
||||||
return match c {
|
return match c {
|
||||||
@@ -246,6 +246,7 @@ fn handle_operator(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>)
|
|||||||
'>' => RAngleBracket,
|
'>' => RAngleBracket,
|
||||||
'|' => Pipe,
|
'|' => Pipe,
|
||||||
'.' => Period,
|
'.' => Period,
|
||||||
|
'=' => Equals,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -298,7 +299,7 @@ mod schala_tokenizer_tests {
|
|||||||
let a = tokenize("let a: A<B> = c ++ d");
|
let a = tokenize("let a: A<B> = c ++ d");
|
||||||
let token_kinds: Vec<TokenKind> = a.into_iter().map(move |t| t.kind).collect();
|
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"),
|
assert_eq!(token_kinds, vec![Keyword(Let), ident!("a"), Colon, ident!("A"),
|
||||||
LAngleBracket, ident!("B"), RAngleBracket, op!("="), ident!("c"), op!("++"), ident!("d")]);
|
LAngleBracket, ident!("B"), RAngleBracket, Equals, ident!("c"), op!("++"), ident!("d")]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -289,14 +289,22 @@ impl<'a> TypeContext<'a> {
|
|||||||
Ok(ty!(Unit))
|
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> {
|
fn expr(&mut self, expr: &Expression) -> InferResult<Type> {
|
||||||
match expr {
|
match expr {
|
||||||
Expression(expr_type, Some(anno)) => {
|
Expression { kind, type_anno: Some(anno) } => {
|
||||||
let t1 = self.expr_type(expr_type)?;
|
let t1 = self.expr_type(kind)?;
|
||||||
let t2 = self.get_type_from_name(anno)?;
|
let t2 = self.get_type_from_name(anno)?;
|
||||||
self.unify(t2, t1)
|
self.unify(t2, t1)
|
||||||
},
|
},
|
||||||
Expression(expr_type, None) => self.expr_type(expr_type)
|
Expression { kind, type_anno: None } => self.expr_type(kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,7 +369,7 @@ impl<'a> TypeContext<'a> {
|
|||||||
|
|
||||||
fn lambda(&mut self, params: &Vec<FormalParam>, type_anno: &Option<TypeIdentifier>, _body: &Block) -> InferResult<Type> {
|
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| {
|
let argument_types: InferResult<Vec<Type>> = params.iter().map(|param: &FormalParam| {
|
||||||
if let (_, Some(type_identifier)) = param {
|
if let FormalParam { anno: Some(type_identifier), .. } = param {
|
||||||
self.get_type_from_name(type_identifier)
|
self.get_type_from_name(type_identifier)
|
||||||
} else {
|
} else {
|
||||||
Ok(Type::Var(self.fresh_type_variable()))
|
Ok(Type::Var(self.fresh_type_variable()))
|
||||||
@@ -376,9 +384,9 @@ impl<'a> TypeContext<'a> {
|
|||||||
Ok(ty!(argument_types, ret_type))
|
Ok(ty!(argument_types, ret_type))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, f: &Expression, args: &Vec<Meta<Expression>>) -> InferResult<Type> {
|
fn call(&mut self, f: &Expression, args: &Vec<Meta<InvocationArgument>>) -> InferResult<Type> {
|
||||||
let tf = self.expr(f)?;
|
let tf = self.expr(f)?;
|
||||||
let arg_types: InferResult<Vec<Type>> = args.iter().map(|ex| self.expr(ex.node())).collect();
|
let arg_types: InferResult<Vec<Type>> = args.iter().map(|ex| self.invoc(ex.node())).collect();
|
||||||
let arg_types = arg_types?;
|
let arg_types = arg_types?;
|
||||||
self.handle_apply(tf, arg_types)
|
self.handle_apply(tf, arg_types)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ use std::collections::HashMap;
|
|||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::cmp::Eq;
|
use std::cmp::Eq;
|
||||||
|
|
||||||
|
macro_rules! bx {
|
||||||
|
($e:expr) => { Box::new($e) }
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct ScopeStack<'a, T: 'a, V: 'a> where T: Hash + Eq {
|
pub struct ScopeStack<'a, T: 'a, V: 'a> where T: Hash + Eq {
|
||||||
parent: Option<&'a ScopeStack<'a, T, V>>,
|
parent: Option<&'a ScopeStack<'a, T, V>>,
|
||||||
|
|||||||
57
schala-lang/language/src/visitor.rs
Normal file
57
schala-lang/language/src/visitor.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::builtin::{BinOp, PrefixOp};
|
||||||
|
use crate::ast::{Expression, ExpressionKind, InvocationArgument, Meta};
|
||||||
|
|
||||||
|
pub trait ExpressionKindVisitor<T> {
|
||||||
|
fn nat_literal(&mut self, n: &u64) -> T;
|
||||||
|
fn float_literal(&mut self, f: &f64) -> T;
|
||||||
|
fn string_literal(&mut self, s: Rc<String>) -> T;
|
||||||
|
fn bool_literal(&mut self, b: &bool) -> T;
|
||||||
|
fn binexp(&mut self, op: &BinOp, lhs: &Expression, rhs: &Expression) -> T;
|
||||||
|
fn prefix_exp(&mut self, op: &PrefixOp, arg: &Expression) -> T;
|
||||||
|
fn value(&mut self, value_name: Rc<String>) -> T;
|
||||||
|
fn call_expression(&mut self, f: &Expression, arguments: &Vec<Meta<InvocationArgument>>) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dispatch<T>(input: &ExpressionKind, visitor: &mut dyn ExpressionKindVisitor<T>) -> T {
|
||||||
|
use ExpressionKind::*;
|
||||||
|
match input {
|
||||||
|
NatLiteral(n) => visitor.nat_literal(n),
|
||||||
|
FloatLiteral(f) => visitor.float_literal(f),
|
||||||
|
StringLiteral(s) => visitor.string_literal(s.clone()),
|
||||||
|
BoolLiteral(b) => visitor.bool_literal(b),
|
||||||
|
BinExp(op, box lhs, box rhs) => visitor.binexp(op, lhs.node(), rhs.node()),
|
||||||
|
PrefixExp(op, box arg) => visitor.prefix_exp(op, arg.node()),
|
||||||
|
Value(val_name) => visitor.value(val_name.clone()),
|
||||||
|
Call { box f, arguments } => visitor.call_expression(f.node(), arguments),
|
||||||
|
_ => panic!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
struct NumberSummer;
|
||||||
|
|
||||||
|
impl ExpressionKindVisitor<u64> for NumberSummer {
|
||||||
|
fn nat_literal(&mut self, n: &u64) -> u64 { n.clone() }
|
||||||
|
fn float_literal(&mut self, f: &f64) -> u64
|
||||||
|
fn string_literal(&mut self, s: Rc<String>) -> u64 { 0 }
|
||||||
|
fn binexp(&mut self, op: &BinOp, lhs: &Expression, rhs: &Expression) -> u64 {
|
||||||
|
let lhs = dispatch(&lhs.kind, self);
|
||||||
|
let rhs = dispatch(&rhs.kind, self);
|
||||||
|
lhs + rhs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn yolo_swagg() {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
let mut t = NumberSummer;
|
||||||
|
|
||||||
|
let x = ExpressionKind::NatLiteral(4);
|
||||||
|
|
||||||
|
let result = dispatch(&x, &mut t);
|
||||||
|
assert_eq!(result, 4);
|
||||||
|
}
|
||||||
|
*/
|
||||||
@@ -11,13 +11,13 @@ itertools = "0.5.8"
|
|||||||
getopts = "0.2.18"
|
getopts = "0.2.18"
|
||||||
lazy_static = "0.2.8"
|
lazy_static = "0.2.8"
|
||||||
maplit = "*"
|
maplit = "*"
|
||||||
colored = "1.7"
|
colored = "1.8"
|
||||||
serde = "1.0.91"
|
serde = "1.0.91"
|
||||||
serde_derive = "1.0.91"
|
serde_derive = "1.0.91"
|
||||||
serde_json = "1.0.15"
|
serde_json = "1.0.15"
|
||||||
phf = "0.7.12"
|
phf = "0.7.12"
|
||||||
includedir = "0.2.0"
|
includedir = "0.2.0"
|
||||||
linefeed = "0.5.0"
|
linefeed = "0.6.0"
|
||||||
regex = "0.2"
|
regex = "0.2"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|||||||
@@ -38,7 +38,16 @@ pub struct GlobalOutputStats {
|
|||||||
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
pub enum DebugAsk {
|
pub enum DebugAsk {
|
||||||
Timing,
|
Timing,
|
||||||
ByStage { stage_name: String },
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DebugResponse {
|
pub struct DebugResponse {
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ pub fn start_repl(langs: Vec<Box<dyn ProgrammingLanguageInterface>>) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_noninteractive(filename: &str, languages: Vec<Box<ProgrammingLanguageInterface>>) {
|
fn run_noninteractive(filename: &str, languages: Vec<Box<dyn ProgrammingLanguageInterface>>) {
|
||||||
let path = Path::new(filename);
|
let path = Path::new(filename);
|
||||||
let ext = path.extension().and_then(|e| e.to_str()).unwrap_or_else(|| {
|
let ext = path.extension().and_then(|e| e.to_str()).unwrap_or_else(|| {
|
||||||
println!("Source file lacks extension");
|
println!("Source file lacks extension");
|
||||||
|
|||||||
@@ -1,32 +1,34 @@
|
|||||||
use super::Repl;
|
use super::{Repl, InterpreterDirectiveOutput};
|
||||||
|
use crate::repl::directive_actions::DirectiveAction;
|
||||||
pub type BoxedCommandFunction = Box<(fn(&mut Repl, &[&str]) -> Option<String>)>;
|
use colored::*;
|
||||||
|
|
||||||
/// A CommandTree is either a `Terminal` or a `NonTerminal`. When command parsing reaches the first
|
/// A CommandTree 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
|
/// 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)]
|
#[derive(Clone)]
|
||||||
pub enum CommandTree {
|
pub enum CommandTree {
|
||||||
Terminal {
|
Terminal {
|
||||||
name: String,
|
name: String,
|
||||||
children: Vec<CommandTree>,
|
children: Vec<CommandTree>,
|
||||||
help_msg: Option<String>,
|
help_msg: Option<String>,
|
||||||
function: BoxedCommandFunction,
|
action: DirectiveAction,
|
||||||
},
|
},
|
||||||
NonTerminal {
|
NonTerminal {
|
||||||
name: String,
|
name: String,
|
||||||
children: Vec<CommandTree>,
|
children: Vec<CommandTree>,
|
||||||
help_msg: Option<String>,
|
help_msg: Option<String>,
|
||||||
|
action: DirectiveAction,
|
||||||
},
|
},
|
||||||
Top(Vec<CommandTree>),
|
Top(Vec<CommandTree>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommandTree {
|
impl CommandTree {
|
||||||
pub fn nonterm_no_further_tab_completions(s: &str, help: Option<&str>) -> 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![] }
|
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>, function: BoxedCommandFunction) -> CommandTree {
|
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()), function, children }
|
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 {
|
pub fn nonterm(s: &str, help: Option<&str>, children: Vec<CommandTree>) -> CommandTree {
|
||||||
@@ -34,20 +36,10 @@ impl CommandTree {
|
|||||||
name: s.to_string(),
|
name: s.to_string(),
|
||||||
help_msg: help.map(|x| x.to_string()),
|
help_msg: help.map(|x| x.to_string()),
|
||||||
children,
|
children,
|
||||||
|
action: DirectiveAction::Null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
pub fn nonterm_with_function(s: &str, help: Option<&str>, children: Vec<CommandTree>, func: BoxedCommandFunction) -> CommandTree {
|
|
||||||
CommandTree::NonTerminal {
|
|
||||||
name: s.to_string(),
|
|
||||||
help_msg: help.map(|x| x.to_string()),
|
|
||||||
children,
|
|
||||||
function: Some(func),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub fn get_cmd(&self) -> &str {
|
pub fn get_cmd(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
CommandTree::Terminal { name, .. } => name.as_str(),
|
CommandTree::Terminal { name, .. } => name.as_str(),
|
||||||
@@ -62,12 +54,46 @@ impl CommandTree {
|
|||||||
CommandTree::Top(_) => ""
|
CommandTree::Top(_) => ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_children(&self) -> Vec<&str> {
|
pub fn get_children(&self) -> &Vec<CommandTree> {
|
||||||
use CommandTree::*;
|
use CommandTree::*;
|
||||||
match self {
|
match self {
|
||||||
Terminal { children, .. } |
|
Terminal { children, .. } |
|
||||||
NonTerminal { children, .. } |
|
NonTerminal { children, .. } |
|
||||||
Top(children) => children.iter().map(|x| x.get_cmd()).collect()
|
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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
133
schala-repl/src/repl/directive_actions.rs
Normal file
133
schala-repl/src/repl/directive_actions.rs
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
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(0) {
|
||||||
|
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")))
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,100 +1,44 @@
|
|||||||
use std::fmt::Write as FmtWrite;
|
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
use crate::repl::Repl;
|
|
||||||
use crate::repl::command_tree::CommandTree;
|
use crate::repl::command_tree::CommandTree;
|
||||||
use crate::language::{LangMetaRequest, LangMetaResponse, DebugAsk, DebugResponse};
|
use crate::repl::directive_actions::DirectiveAction;
|
||||||
|
|
||||||
pub fn directives_from_pass_names(pass_names: &Vec<String>) -> CommandTree {
|
pub fn directives_from_pass_names(pass_names: &Vec<String>) -> CommandTree {
|
||||||
let passes_directives: Vec<CommandTree> = pass_names.iter()
|
let passes_directives: Vec<CommandTree> = pass_names.iter()
|
||||||
.map(|pass_name| { CommandTree::nonterm_no_further_tab_completions(pass_name, None) })
|
.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)
|
||||||
|
}
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
CommandTree::Top(get_list(&passes_directives, true))
|
||||||
|
}
|
||||||
|
|
||||||
CommandTree::Top(vec![
|
fn get_list(passes_directives: &Vec<CommandTree>, include_help: bool) -> Vec<CommandTree> {
|
||||||
CommandTree::terminal("exit", Some("exit the REPL"), vec![], Box::new(|repl: &mut Repl, _cmds: &[&str]| {
|
use DirectiveAction::*;
|
||||||
repl.save_before_exit();
|
|
||||||
::std::process::exit(0)
|
vec![
|
||||||
})),
|
CommandTree::terminal("exit", Some("exit the REPL"), vec![], QuitProgram),
|
||||||
CommandTree::terminal("quit", Some("exit the REPL"), vec![], Box::new(|repl: &mut Repl, _cmds: &[&str]| {
|
CommandTree::terminal("quit", Some("exit the REPL"), vec![], QuitProgram),
|
||||||
repl.save_before_exit();
|
CommandTree::terminal("help", Some("Print this help message"), if include_help { get_list(passes_directives, false) } else { vec![] }, Help),
|
||||||
::std::process::exit(0)
|
|
||||||
})),
|
|
||||||
CommandTree::terminal("help", Some("Print this help message"), vec![], Box::new(|repl: &mut Repl, cmds: &[&str]| {
|
|
||||||
Some(repl.print_help_message(cmds))
|
|
||||||
})),
|
|
||||||
CommandTree::nonterm("debug",
|
CommandTree::nonterm("debug",
|
||||||
Some("Configure debug information"),
|
Some("Configure debug information"),
|
||||||
vec![
|
vec![
|
||||||
CommandTree::terminal("list-passes", Some("List all registered compiler passes"), vec![], Box::new(|repl: &mut Repl, _cmds: &[&str]| {
|
CommandTree::terminal("list-passes", Some("List all registered compiler passes"), vec![], ListPasses),
|
||||||
let language_state = repl.get_cur_language_state();
|
CommandTree::terminal("show-immediate", None, passes_directives.clone(), ShowImmediate),
|
||||||
let pass_names = match language_state.request_meta(LangMetaRequest::StageNames) {
|
CommandTree::terminal("show", Some("Show debug output for a specific pass"), passes_directives.clone(), Show),
|
||||||
LangMetaResponse::StageNames(names) => names,
|
CommandTree::terminal("hide", Some("Hide debug output for a specific pass"), passes_directives.clone(), Hide),
|
||||||
_ => 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)
|
|
||||||
})),
|
|
||||||
CommandTree::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)
|
|
||||||
})),
|
|
||||||
CommandTree::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
|
|
||||||
})),
|
|
||||||
CommandTree::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
|
|
||||||
})),
|
|
||||||
CommandTree::nonterm("total-time", None, vec![
|
CommandTree::nonterm("total-time", None, vec![
|
||||||
CommandTree::terminal("on", None, vec![], Box::new(|repl: &mut Repl, _: &[&str]| {
|
CommandTree::terminal("on", None, vec![], TotalTimeOn),
|
||||||
repl.options.show_total_time = true;
|
CommandTree::terminal("off", None, vec![], TotalTimeOff),
|
||||||
None
|
|
||||||
})),
|
|
||||||
CommandTree::terminal("off", None, vec![], Box::new(turn_off)),
|
|
||||||
]),
|
]),
|
||||||
CommandTree::nonterm("stage-times", Some("Computation time per-stage"), vec![
|
CommandTree::nonterm("stage-times", Some("Computation time per-stage"), vec![
|
||||||
CommandTree::terminal("on", None, vec![], Box::new(|repl: &mut Repl, _: &[&str]| {
|
CommandTree::terminal("on", None, vec![], StageTimeOn),
|
||||||
repl.options.show_stage_times = true;
|
CommandTree::terminal("off", None, vec![], StageTimeOff),
|
||||||
None
|
|
||||||
})),
|
|
||||||
CommandTree::terminal("off", None, vec![], Box::new(|repl: &mut Repl, _: &[&str]| {
|
|
||||||
repl.options.show_stage_times = false;
|
|
||||||
None
|
|
||||||
})),
|
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
@@ -106,21 +50,6 @@ pub fn directives_from_pass_names(pass_names: &Vec<String>) -> CommandTree {
|
|||||||
CommandTree::nonterm("go", None, vec![]),
|
CommandTree::nonterm("go", None, vec![]),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
CommandTree::terminal("doc", Some("Get language-specific help for an item"), vec![], Box::new(|repl: &mut Repl, cmds: &[&str]| {
|
CommandTree::terminal("doc", Some("Get language-specific help for an item"), vec![], Doc),
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|||||||
54
schala-repl/src/repl/help.rs
Normal file
54
schala-repl/src/repl/help.rs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
@@ -1,47 +1,43 @@
|
|||||||
use std::fmt::Write as FmtWrite;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use colored::*;
|
|
||||||
use crate::language::{ProgrammingLanguageInterface,
|
use crate::language::{ProgrammingLanguageInterface,
|
||||||
ComputationRequest, ComputationResponse,
|
ComputationRequest, LangMetaResponse, LangMetaRequest};
|
||||||
DebugAsk, LangMetaResponse, LangMetaRequest};
|
|
||||||
|
|
||||||
mod command_tree;
|
mod command_tree;
|
||||||
use self::command_tree::{CommandTree, BoxedCommandFunction};
|
use self::command_tree::CommandTree;
|
||||||
mod repl_options;
|
mod repl_options;
|
||||||
use repl_options::ReplOptions;
|
use repl_options::ReplOptions;
|
||||||
|
mod directive_actions;
|
||||||
mod directives;
|
mod directives;
|
||||||
use directives::directives_from_pass_names;
|
use directives::directives_from_pass_names;
|
||||||
|
mod help;
|
||||||
|
mod response;
|
||||||
|
use response::ReplResponse;
|
||||||
|
|
||||||
const HISTORY_SAVE_FILE: &'static str = ".schala_history";
|
const HISTORY_SAVE_FILE: &'static str = ".schala_history";
|
||||||
const OPTIONS_SAVE_FILE: &'static str = ".schala_repl";
|
const OPTIONS_SAVE_FILE: &'static str = ".schala_repl";
|
||||||
|
|
||||||
|
type InterpreterDirectiveOutput = Option<String>;
|
||||||
|
|
||||||
pub struct Repl {
|
pub struct Repl {
|
||||||
interpreter_directive_sigil: char,
|
pub interpreter_directive_sigil: char,
|
||||||
line_reader: ::linefeed::interface::Interface<::linefeed::terminal::DefaultTerminal>,
|
line_reader: ::linefeed::interface::Interface<::linefeed::terminal::DefaultTerminal>,
|
||||||
language_states: Vec<Box<ProgrammingLanguageInterface>>,
|
language_states: Vec<Box<dyn ProgrammingLanguageInterface>>,
|
||||||
options: ReplOptions,
|
options: ReplOptions,
|
||||||
directives: CommandTree,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Repl {
|
impl Repl {
|
||||||
pub fn new(mut initial_states: Vec<Box<ProgrammingLanguageInterface>>) -> Repl {
|
pub fn new(initial_states: Vec<Box<dyn ProgrammingLanguageInterface>>) -> Repl {
|
||||||
use linefeed::Interface;
|
use linefeed::Interface;
|
||||||
let line_reader = Interface::new("schala-repl").unwrap();
|
let line_reader = Interface::new("schala-repl").unwrap();
|
||||||
let interpreter_directive_sigil = ':';
|
let interpreter_directive_sigil = ':';
|
||||||
|
|
||||||
let pass_names = match initial_states[0].request_meta(LangMetaRequest::StageNames) {
|
|
||||||
LangMetaResponse::StageNames(names) => names,
|
|
||||||
_ => vec![],
|
|
||||||
};
|
|
||||||
|
|
||||||
Repl {
|
Repl {
|
||||||
interpreter_directive_sigil,
|
interpreter_directive_sigil,
|
||||||
line_reader,
|
line_reader,
|
||||||
language_states: initial_states,
|
language_states: initial_states,
|
||||||
options: ReplOptions::new(),
|
options: ReplOptions::new(),
|
||||||
directives: directives_from_pass_names(&pass_names)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,12 +73,16 @@ impl Repl {
|
|||||||
Ok(Eof) | Ok(Signal(_)) => break,
|
Ok(Eof) | Ok(Signal(_)) => break,
|
||||||
Ok(Input(ref input)) => {
|
Ok(Input(ref input)) => {
|
||||||
self.line_reader.add_history_unique(input.to_string());
|
self.line_reader.add_history_unique(input.to_string());
|
||||||
let output = match input.chars().nth(0) {
|
match input.chars().nth(0) {
|
||||||
Some(ch) if ch == self.interpreter_directive_sigil => self.handle_interpreter_directive(input),
|
Some(ch) if ch == self.interpreter_directive_sigil => match self.handle_interpreter_directive(input) {
|
||||||
_ => Some(self.handle_input(input)),
|
Some(directive_output) => println!("<> {}", directive_output),
|
||||||
};
|
None => (),
|
||||||
if let Some(o) = output {
|
},
|
||||||
println!("=> {}", o);
|
_ => {
|
||||||
|
for repl_response in self.handle_input(input) {
|
||||||
|
println!("{}", repl_response);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,130 +101,37 @@ impl Repl {
|
|||||||
self.options.save_to_file(OPTIONS_SAVE_FILE);
|
self.options.save_to_file(OPTIONS_SAVE_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_function_from_directives<'a>(directives: &'a CommandTree, commands: &Vec<&str>) -> Result<(&'a BoxedCommandFunction, usize), String> {
|
fn handle_interpreter_directive(&mut self, input: &str) -> InterpreterDirectiveOutput {
|
||||||
let mut dir_pointer: &CommandTree = &directives;
|
|
||||||
let mut idx = 0;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match dir_pointer {
|
|
||||||
CommandTree::Top(subcommands) | CommandTree::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))
|
|
||||||
};
|
|
||||||
},
|
|
||||||
CommandTree::Terminal { function, .. } => {
|
|
||||||
break Ok((function, idx));
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_interpreter_directive(&mut self, input: &str) -> Option<String> {
|
|
||||||
let mut iter = input.chars();
|
let mut iter = input.chars();
|
||||||
iter.next();
|
iter.next();
|
||||||
let commands: Vec<&str> = iter
|
let arguments: Vec<&str> = iter
|
||||||
.as_str()
|
.as_str()
|
||||||
.split_whitespace()
|
.split_whitespace()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if commands.len() < 1 {
|
if arguments.len() < 1 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let directives = self.get_directives();
|
let directives = self.get_directives();
|
||||||
let result: Result<(&BoxedCommandFunction, _), String> = Repl::get_function_from_directives(&directives, &commands);
|
directives.perform(self, &arguments)
|
||||||
match result {
|
|
||||||
Ok((f, idx)) => f(self, &commands[idx..]),
|
|
||||||
Err(err) => Some(err.red().to_string())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_help_message(&mut self, commands_passed_to_help: &[&str] ) -> String {
|
fn get_cur_language_state(&mut self) -> &mut Box<dyn ProgrammingLanguageInterface> {
|
||||||
let mut buf = String::new();
|
|
||||||
let directives = match self.get_directives() {
|
|
||||||
CommandTree::Top(children) => children,
|
|
||||||
_ => panic!("Top-level CommandTree 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
|
//TODO this is obviously not complete
|
||||||
&mut self.language_states[0]
|
&mut self.language_states[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_input(&mut self, input: &str) -> String {
|
fn handle_input(&mut self, input: &str) -> Vec<ReplResponse> {
|
||||||
let mut debug_requests = HashSet::new();
|
let mut debug_requests = HashSet::new();
|
||||||
for ask in self.options.debug_asks.iter() {
|
for ask in self.options.debug_asks.iter() {
|
||||||
debug_requests.insert(ask.clone());
|
debug_requests.insert(ask.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let request = ComputationRequest {
|
let request = ComputationRequest { source: input, debug_requests };
|
||||||
source: input,
|
|
||||||
debug_requests,
|
|
||||||
};
|
|
||||||
|
|
||||||
let ref mut language_state = self.get_cur_language_state();
|
let ref mut language_state = self.get_cur_language_state();
|
||||||
let response = language_state.run_computation(request);
|
let response = language_state.run_computation(request);
|
||||||
|
response::handle_computation_response(response, &self.options)
|
||||||
self.handle_computation_response(response)
|
|
||||||
}
|
|
||||||
|
|
||||||
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) -> CommandTree {
|
fn get_directives(&mut self) -> CommandTree {
|
||||||
@@ -276,7 +183,7 @@ impl<T: Terminal> Completer<T> for TabCompleteHandler {
|
|||||||
_ => false
|
_ => false
|
||||||
};
|
};
|
||||||
let word = if top { word.get(1..).unwrap() } else { word };
|
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() {
|
for cmd in command_tree.map(|x| x.get_subcommands()).unwrap_or(vec![]).into_iter() {
|
||||||
if cmd.starts_with(word) {
|
if cmd.starts_with(word) {
|
||||||
completions.push(Completion {
|
completions.push(Completion {
|
||||||
completion: format!("{}{}", if top { ":" } else { "" }, cmd),
|
completion: format!("{}{}", if top { ":" } else { "" }, cmd),
|
||||||
|
|||||||
67
schala-repl/src/repl/response.rs
Normal file
67
schala-repl/src/repl/response.rs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ use schala_repl::{ProgrammingLanguageInterface, start_repl};
|
|||||||
extern { }
|
extern { }
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let langs: Vec<Box<ProgrammingLanguageInterface>> = vec![Box::new(schala_lang::Schala::new())];
|
let langs: Vec<Box<dyn ProgrammingLanguageInterface>> = vec![Box::new(schala_lang::Schala::new())];
|
||||||
start_repl(langs);
|
start_repl(langs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user