Compare commits

...

7 Commits

Author SHA1 Message Date
Greg Shuflin
91a7abf4cd Clippy lints for tokenizing.rs 2021-10-19 21:27:05 -07:00
Greg Shuflin
0c6c4ef47e Symbol table clippy 2021-10-19 21:18:57 -07:00
Greg Shuflin
355ed3c749 Rename FQSN -> Fqsn 2021-10-19 21:14:15 -07:00
Greg Shuflin
c0a3a03045 Clippy on eval.rs 2021-10-19 21:06:59 -07:00
Greg Shuflin
f8c2e57b37 Clippy on reduced_ast.rs 2021-10-19 20:56:52 -07:00
Greg Shuflin
49a50deb04 Run rustfmt on schala.rs 2021-10-19 20:50:43 -07:00
Greg Shuflin
052a2feb23 schala.rs - clippy lints 2021-10-19 20:45:59 -07:00
8 changed files with 282 additions and 264 deletions

View File

@ -14,11 +14,12 @@ pub struct State<'a> {
impl<'a> State<'a> {
pub fn new() -> State<'a> {
let values = ScopeStack::new(Some(format!("global")));
let values = ScopeStack::new(Some("global".to_string()));
State { values }
}
fn new_frame(&'a self, items: &'a Vec<Node>, bound_vars: &BoundVars) -> State<'a> {
#[allow(clippy::ptr_arg)]
fn new_frame(&'a self, items: &'a [Node], bound_vars: &BoundVars) -> State<'a> {
let mut inner_state = State {
values: self.values.new_scope(None),
};
@ -47,7 +48,7 @@ enum Node {
fn paren_wrapped_vec(terms: impl Iterator<Item=String>) -> String {
let mut buf = String::new();
write!(buf, "(").unwrap();
for term in terms.map(|e| Some(e)).intersperse(None) {
for term in terms.map(Some).intersperse(None) {
match term {
Some(e) => write!(buf, "{}", e).unwrap(),
None => write!(buf, ", ").unwrap(),
@ -62,16 +63,13 @@ impl Node {
fn to_repl(&self) -> String {
match self {
Node::Expr(e) => e.to_repl(),
Node::PrimObject { name, items, .. } if items.len() == 0 => format!("{}", name),
Node::PrimObject { name, items, .. } if items.is_empty() => format!("{}", name),
Node::PrimObject { name, items, .. } => format!("{}{}", name, paren_wrapped_vec(items.iter().map(|x| x.to_repl()))),
Node::PrimTuple { items } => format!("{}", paren_wrapped_vec(items.iter().map(|x| x.to_repl()))),
Node::PrimTuple { items } => paren_wrapped_vec(items.iter().map(|x| x.to_repl())),
}
}
fn is_true(&self) -> bool {
match self {
Node::Expr(Expr::Lit(crate::reduced_ast::Lit::Bool(true))) => true,
_ => false,
}
matches!(self, Node::Expr(Expr::Lit(crate::reduced_ast::Lit::Bool(true))))
}
}
@ -86,9 +84,10 @@ enum ValueEntry {
type EvalResult<T> = Result<T, String>;
impl Expr {
fn to_node(self) -> Node {
Node::Expr(self)
}
#[allow(clippy::wrong_self_convention)]
fn to_node(self) -> Node {
Node::Expr(self)
}
fn to_repl(&self) -> String {
use self::Lit::*;
use self::Func::*;
@ -103,7 +102,7 @@ impl Expr {
},
Expr::Func(f) => match f {
BuiltIn(builtin) => format!("<built-in function '{:?}'>", builtin),
UserDefined { name: None, .. } => format!("<function>"),
UserDefined { name: None, .. } => "<function>".to_string(),
UserDefined { name: Some(name), .. } => format!("<function '{}'>", name),
},
Expr::Constructor { type_name, arity, .. } => {
@ -174,7 +173,7 @@ impl<'a> State<'a> {
match stmt {
Stmt::Binding { name, constant, expr } => {
let val = self.expression(Node::Expr(expr))?;
self.values.insert(name.clone(), ValueEntry::Binding { constant, val });
self.values.insert(name, ValueEntry::Binding { constant, val });
Ok(None)
},
Stmt::Expr(expr) => Ok(Some(self.expression(expr.to_node())?)),
@ -214,7 +213,7 @@ impl<'a> State<'a> {
Unit => Ok(Node::Expr(Unit)),
CaseMatch { box cond, alternatives } => self.case_match_expression(cond, alternatives),
ConditionalTargetSigilValue => Ok(Node::Expr(ConditionalTargetSigilValue)),
UnimplementedSigilValue => Err(format!("Sigil value eval not implemented")),
UnimplementedSigilValue => Err("Sigil value eval not implemented".to_string()),
ReductionError(err) => Err(format!("Reduction error: {}", err)),
}
}
@ -237,9 +236,9 @@ impl<'a> State<'a> {
let evaled_args = args.into_iter().map(|expr| self.expression(Node::Expr(expr))).collect::<Result<Vec<Node>,_>>()?;
//let evaled_args = vec![];
Ok(Node::PrimObject {
name: name.clone(),
name,
items: evaled_args,
tag
tag
})
}
@ -286,7 +285,7 @@ impl<'a> State<'a> {
(Multiply, Lit(Nat(l)), Lit(Nat(r))) => Lit(Nat(l * r)),
(Divide, Lit(Nat(l)), Lit(Nat(r))) => Lit(Float((*l as f64)/ (*r as f64))),
(Quotient, Lit(Nat(l)), Lit(Nat(r))) => if *r == 0 {
return Err(format!("divide by zero"));
return Err("Divide-by-zero error".to_string());
} else {
Lit(Nat(l / r))
},
@ -322,8 +321,8 @@ impl<'a> State<'a> {
(prefix, &[Node::Expr(ref arg)]) => match (prefix, arg) {
(BooleanNot, Lit(Bool(true))) => Lit(Bool(false)),
(BooleanNot, Lit(Bool(false))) => Lit(Bool(true)),
(Negate, Lit(Nat(n))) => Lit(Int(-1*(*n as i64))),
(Negate, Lit(Int(n))) => Lit(Int(-1*(*n as i64))),
(Negate, Lit(Nat(n))) => Lit(Int(-(*n as i64))),
(Negate, Lit(Int(n))) => Lit(Int(-(*n as i64))),
(Increment, Lit(Int(n))) => Lit(Int(*n)),
(Increment, Lit(Nat(n))) => Lit(Nat(*n)),
_ => return Err("No valid prefix op".to_string())
@ -352,25 +351,25 @@ impl<'a> State<'a> {
Ok(match cond {
Node::Expr(Expr::Lit(Lit::Bool(true))) => self.block(then_clause)?,
Node::Expr(Expr::Lit(Lit::Bool(false))) => self.block(else_clause)?,
_ => return Err(format!("Conditional with non-boolean condition"))
_ => return Err("Conditional with non-boolean condition".to_string())
})
}
fn assign_expression(&mut self, val: Expr, expr: Expr) -> EvalResult<Node> {
let name = match val {
Expr::Sym(name) => name,
_ => return Err(format!("Trying to assign to a non-value")),
_ => return Err("Trying to assign to a non-value".to_string()),
};
let constant = match self.values.lookup(&name) {
None => return Err(format!("Constant {} is undefined", name)),
Some(ValueEntry::Binding { constant, .. }) => constant.clone(),
Some(ValueEntry::Binding { constant, .. }) => *constant,
};
if constant {
return Err(format!("trying to update {}, a non-mutable binding", name));
}
let val = self.expression(Node::Expr(expr))?;
self.values.insert(name.clone(), ValueEntry::Binding { constant: false, val });
self.values.insert(name, ValueEntry::Binding { constant: false, val });
Ok(Node::Expr(Expr::Unit))
}
@ -390,8 +389,7 @@ impl<'a> State<'a> {
//TODO need to handle recursive subpatterns
let all_subpatterns_pass = |state: &mut State, subpatterns: &Vec<Option<Subpattern>>, items: &Vec<Node>| -> EvalResult<bool> {
if subpatterns.len() == 0 {
if subpatterns.is_empty() {
return Ok(true)
}
@ -401,7 +399,7 @@ impl<'a> State<'a> {
for (maybe_subp, cond) in subpatterns.iter().zip(items.iter()) {
if let Some(subp) = maybe_subp {
if !state.guard_passes(&subp.guard, &cond)? {
if !state.guard_passes(&subp.guard, cond)? {
return Ok(false)
}
}
@ -436,7 +434,7 @@ impl<'a> State<'a> {
}
},
Node::Expr(ref _e) => {
if let None = alt.matchable.tag {
if alt.matchable.tag.is_none() {
return self.block(alt.item)
}
}

View File

@ -12,6 +12,9 @@
//! then ReducedAST shouldn't be duplicating information that can be queried at runtime from the
//! symbol table. But I think the former might make sense since ultimately the bytecode will be
//! built from the ReducedAST.
#![allow(clippy::enum_variant_names)]
use std::rc::Rc;
use std::str::FromStr;
use std::convert::TryFrom;
@ -141,6 +144,7 @@ impl<'a> Reducer<'a> {
}
}
#[allow(clippy::ptr_arg)]
fn block(&mut self, block: &Block) -> Vec<Stmt> {
block.iter().map(|stmt| self.statement(stmt)).collect()
}
@ -189,11 +193,11 @@ impl<'a> Reducer<'a> {
};
match spec {
SymbolSpec::RecordConstructor { .. } => Expr::ReductionError(format!("AST reducer doesn't expect a RecordConstructor here")),
SymbolSpec::RecordConstructor { .. } => Expr::ReductionError("AST reducer doesn't expect a RecordConstructor here".to_string()),
SymbolSpec::DataConstructor { index, arity, type_name } => Expr::Constructor {
type_name: type_name.clone(),
name: local_name.clone(),
tag: index.clone(),
tag: *index,
arity: *arity,
},
SymbolSpec::Func(_) => Expr::Sym(local_name.clone()),
@ -201,7 +205,8 @@ impl<'a> Reducer<'a> {
}
}
fn reduce_lambda(&mut self, params: &Vec<FormalParam>, body: &Block) -> Expr {
#[allow(clippy::ptr_arg)]
fn reduce_lambda(&mut self, params: &[FormalParam], body: &Block) -> Expr {
Expr::Func(Func::UserDefined {
name: None,
params: params.iter().map(|param| param.name.clone()).collect(),
@ -209,7 +214,7 @@ impl<'a> Reducer<'a> {
})
}
fn reduce_named_struct(&mut self, name: &QualifiedName, fields: &Vec<(Rc<String>, Expression)>) -> Expr {
fn reduce_named_struct(&mut self, name: &QualifiedName, fields: &[(Rc<String>, Expression)]) -> Expr {
let symbol = match self.symbol_table.lookup_symbol(&name.id) {
Some(fqsn) => fqsn,
None => return Expr::ReductionError(format!("FQSN lookup for name {:?} failed", name)),
@ -236,7 +241,7 @@ impl<'a> Reducer<'a> {
Expr::Call { f, args }
}
fn reduce_call_expression(&mut self, func: &Expression, arguments: &Vec<InvocationArgument>) -> Expr {
fn reduce_call_expression(&mut self, func: &Expression, arguments: &[ InvocationArgument ]) -> Expr {
Expr::Call {
f: Box::new(self.expression(func)),
args: arguments.iter().map(|arg| self.invocation_argument(arg)).collect(),
@ -246,23 +251,23 @@ impl<'a> Reducer<'a> {
fn reduce_if_expression(&mut self, discriminator: Option<&Expression>, body: &IfExpressionBody) -> Expr {
let cond = Box::new(match discriminator {
Some(expr) => self.expression(expr),
None => return Expr::ReductionError(format!("blank cond if-expr not supported")),
None => return Expr::ReductionError("blank cond if-expr not supported".to_string()),
});
match body {
IfExpressionBody::SimpleConditional { then_case, else_case } => {
let then_clause = self.block(&then_case);
let then_clause = self.block(then_case);
let else_clause = match else_case.as_ref() {
None => vec![],
Some(stmts) => self.block(&stmts),
Some(stmts) => self.block(stmts),
};
Expr::Conditional { cond, then_clause, else_clause }
},
IfExpressionBody::SimplePatternMatch { pattern, then_case, else_case } => {
let then_clause = self.block(&then_case);
let then_clause = self.block(then_case);
let else_clause = match else_case.as_ref() {
None => vec![],
Some(stmts) => self.block(&stmts),
Some(stmts) => self.block(stmts),
};
let alternatives = vec![
@ -368,7 +373,7 @@ impl<'a> Reducer<'a> {
* x is SomeBigOldEnum(_, x, Some(t))
*/
fn handle_symbol(symbol: Option<&Symbol>, inner_patterns: &Vec<Pattern>, symbol_table: &SymbolTable) -> Subpattern {
fn handle_symbol(symbol: Option<&Symbol>, inner_patterns: &[Pattern], symbol_table: &SymbolTable) -> Subpattern {
use self::Pattern::*;
let tag = symbol.map(|symbol| match symbol.spec {
SymbolSpec::DataConstructor { index, .. } => index,
@ -451,7 +456,7 @@ impl Pattern {
// if symbol is Some, treat this as a symbol pattern. If it's None, treat it
// as a variable.
match symbol_table.lookup_symbol(id) {
Some(symbol) => handle_symbol(Some(symbol), &vec![], symbol_table),
Some(symbol) => handle_symbol(Some(symbol), &[], symbol_table),
None => {
println!("Components: {:?}", components);
let name = if components.len() == 1 {
@ -480,8 +485,8 @@ impl PatternLiteral {
let comparison = Expr::Lit(match (neg, num) {
(false, ExpressionKind::NatLiteral(n)) => Lit::Nat(*n),
(false, ExpressionKind::FloatLiteral(f)) => Lit::Float(*f),
(true, ExpressionKind::NatLiteral(n)) => Lit::Int(-1*(*n as i64)),
(true, ExpressionKind::FloatLiteral(f)) => Lit::Float(-1.0*f),
(true, ExpressionKind::NatLiteral(n)) => Lit::Int(-(*n as i64)),
(true, ExpressionKind::FloatLiteral(f)) => Lit::Float(-f),
_ => panic!("This should never happen")
});
let guard = Some(Expr::Call {

View File

@ -1,192 +1,203 @@
use stopwatch::Stopwatch;
use std::cell::RefCell;
use std::rc::Rc;
use schala_repl::{ProgrammingLanguageInterface,
ComputationRequest, ComputationResponse,
LangMetaRequest, LangMetaResponse, GlobalOutputStats};
use crate::{reduced_ast, tokenizing, parsing, eval, typechecking, symbol_table};
use crate::error::SchalaError;
pub type SymbolTableHandle = Rc<RefCell<symbol_table::SymbolTable>>;
use crate::{eval, parsing, reduced_ast, symbol_table, tokenizing, typechecking};
use schala_repl::{
ComputationRequest, ComputationResponse, GlobalOutputStats, LangMetaRequest, LangMetaResponse,
ProgrammingLanguageInterface,
};
/// All the state necessary to parse and execute a Schala program are stored in this struct.
#[allow(dead_code)]
pub struct Schala {
/// Holds a reference to the original source code, parsed into line and character
source_reference: SourceReference,
/// Execution state for AST-walking interpreter
state: eval::State<'static>,
/// Keeps track of symbols and scopes
symbol_table: SymbolTableHandle,
/// Contains information for type-checking
type_context: typechecking::TypeContext<'static>,
/// Schala Parser
active_parser: parsing::Parser,
/// Holds a reference to the original source code, parsed into line and character
source_reference: SourceReference,
/// Execution state for AST-walking interpreter
state: eval::State<'static>,
/// Keeps track of symbols and scopes
symbol_table: symbol_table::SymbolTable,
/// Contains information for type-checking
type_context: typechecking::TypeContext<'static>,
/// Schala Parser
active_parser: parsing::Parser,
}
impl Schala {
//TODO implement documentation for language items
/*
fn handle_docs(&self, source: String) -> LangMetaResponse {
LangMetaResponse::Docs {
doc_string: format!("Schala item `{}` : <<Schala-lang documentation not yet implemented>>", source)
//TODO implement documentation for language items
/*
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(),
type_context: typechecking::TypeContext::new(),
active_parser: parsing::Parser::new()
}
}
/// 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!("../source-files/prelude.schala");
let mut env = Schala::new_blank_env();
let response = env.run_pipeline(prelude);
if let Err(err) = response {
panic!("Error in prelude, panicking: {}", err.display());
}
env
}
/// This is where the actual action of interpreting/compilation happens.
/// Note: this should eventually use a query-based system for parallelization, cf.
/// https://rustc-dev-guide.rust-lang.org/overview.html
fn run_pipeline(&mut self, source: &str) -> Result<String, SchalaError> {
// 1st stage - tokenization
// TODO tokenize should return its own error type
let tokens = tokenizing::tokenize(source);
if let Some(err) = SchalaError::from_tokens(&tokens) {
return Err(err)
/// Creates a new Schala environment *without* any prelude.
fn new_blank_env() -> Schala {
Schala {
source_reference: SourceReference::new(),
symbol_table: symbol_table::SymbolTable::new(),
state: eval::State::new(),
type_context: typechecking::TypeContext::new(),
active_parser: parsing::Parser::new(),
}
}
//2nd stage - parsing
self.active_parser.add_new_tokens(tokens);
let ast = self.active_parser.parse()
.map_err(|err| SchalaError::from_parse_error(err, &self.source_reference))?;
/// Creates a new Schala environment with the standard prelude, which is defined as ordinary
/// Schala code in the file `prelude.schala`
#[allow(clippy::new_without_default)]
pub fn new() -> Schala {
let prelude = include_str!("../source-files/prelude.schala");
let mut env = Schala::new_blank_env();
//Perform all symbol table work
self.symbol_table.borrow_mut().process_ast(&ast)
.map_err(|err| SchalaError::from_symbol_table(err))?;
let response = env.run_pipeline(prelude);
if let Err(err) = response {
panic!("Error in prelude, panicking: {}", err.display());
}
env
}
// Typechecking
// TODO typechecking not working
let _overall_type = self.type_context.typecheck(&ast)
.map_err(SchalaError::from_type_error);
/// This is where the actual action of interpreting/compilation happens.
/// Note: this should eventually use a query-based system for parallelization, cf.
/// https://rustc-dev-guide.rust-lang.org/overview.html
fn run_pipeline(&mut self, source: &str) -> Result<String, SchalaError> {
// 1st stage - tokenization
// TODO tokenize should return its own error type
let tokens = tokenizing::tokenize(source);
if let Some(err) = SchalaError::from_tokens(&tokens) {
return Err(err);
}
// Reduce AST - TODO this doesn't produce an error yet, but probably should
let symbol_table = self.symbol_table.borrow();
let reduced_ast = reduced_ast::reduce(&ast, &symbol_table);
//2nd stage - parsing
self.active_parser.add_new_tokens(tokens);
let ast = self
.active_parser
.parse()
.map_err(|err| SchalaError::from_parse_error(err, &self.source_reference))?;
// Tree-walking evaluator. TODO fix this
let evaluation_outputs = self.state.evaluate(reduced_ast, true);
let text_output: Result<Vec<String>, String> = evaluation_outputs
.into_iter()
.collect();
//Perform all symbol table work
self.symbol_table
.process_ast(&ast)
.map_err(SchalaError::from_symbol_table)?;
let text_output: Result<Vec<String>, SchalaError> = text_output
.map_err(|err| SchalaError::from_string(err, Stage::Evaluation));
// Typechecking
// TODO typechecking not working
let _overall_type = self
.type_context
.typecheck(&ast)
.map_err(SchalaError::from_type_error);
let eval_output: String = text_output
.map(|v| { Iterator::intersperse(v.into_iter(), "\n".to_owned()).collect() })?;
// Reduce AST - TODO this doesn't produce an error yet, but probably should
let reduced_ast = reduced_ast::reduce(&ast, &self.symbol_table);
Ok(eval_output)
}
// Tree-walking evaluator. TODO fix this
let evaluation_outputs = self.state.evaluate(reduced_ast, true);
let text_output: Result<Vec<String>, String> = evaluation_outputs.into_iter().collect();
let text_output: Result<Vec<String>, SchalaError> =
text_output.map_err(|err| SchalaError::from_string(err, Stage::Evaluation));
let eval_output: String =
text_output.map(|v| Iterator::intersperse(v.into_iter(), "\n".to_owned()).collect())?;
Ok(eval_output)
}
}
/// Represents lines of source code
pub(crate) struct SourceReference {
lines: Option<Vec<String>>
lines: Option<Vec<String>>,
}
impl SourceReference {
fn new() -> SourceReference {
SourceReference { lines: None }
}
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 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());
}
pub 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"))
}
pub fn get_line(&self, line: usize) -> String {
self.lines
.as_ref()
.and_then(|x| x.get(line).map(|s| s.to_string()))
.unwrap_or_else(|| "NO LINE FOUND".to_string())
}
}
#[allow(dead_code)]
#[derive(Clone, Copy, Debug)]
pub(crate) enum Stage {
Tokenizing,
Parsing,
Symbols,
ScopeResolution,
Typechecking,
AstReduction,
Evaluation,
Tokenizing,
Parsing,
Symbols,
ScopeResolution,
Typechecking,
AstReduction,
Evaluation,
}
fn stage_names() -> Vec<&'static str> {
vec![
"tokenizing",
"parsing",
"symbol-table",
"scope-resolution",
"typechecking",
"ast-reduction",
"ast-walking-evaluation"
]
vec![
"tokenizing",
"parsing",
"symbol-table",
"typechecking",
"ast-reduction",
"ast-walking-evaluation",
]
}
impl ProgrammingLanguageInterface for Schala {
type Config = ();
fn language_name() -> String {
"Schala".to_owned()
}
fn source_file_suffix() -> String {
"schala".to_owned()
}
fn run_computation(&mut self, request: ComputationRequest<Self::Config>) -> ComputationResponse {
let ComputationRequest { source, debug_requests: _, config: _ } = request;
self.source_reference.load_new_source(source);
let sw = Stopwatch::start_new();
let main_output = self.run_pipeline(source)
.map_err(|schala_err| schala_err.display());
let global_output_stats = GlobalOutputStats {
total_duration: sw.elapsed(),
stage_durations: vec![]
};
ComputationResponse {
main_output,
global_output_stats,
debug_responses: vec![]
type Config = ();
fn language_name() -> String {
"Schala".to_owned()
}
}
fn request_meta(&mut self, request: LangMetaRequest) -> LangMetaResponse {
match request {
LangMetaRequest::StageNames => LangMetaResponse::StageNames(stage_names().iter().map(|s| s.to_string()).collect()),
_ => LangMetaResponse::Custom { kind: format!("not-implemented"), value: format!("") }
fn source_file_suffix() -> String {
"schala".to_owned()
}
fn run_computation(
&mut self,
request: ComputationRequest<Self::Config>,
) -> ComputationResponse {
let ComputationRequest {
source,
debug_requests: _,
config: _,
} = request;
self.source_reference.load_new_source(source);
let sw = Stopwatch::start_new();
let main_output = self
.run_pipeline(source)
.map_err(|schala_err| schala_err.display());
let global_output_stats = GlobalOutputStats {
total_duration: sw.elapsed(),
stage_durations: vec![],
};
ComputationResponse {
main_output,
global_output_stats,
debug_responses: vec![],
}
}
fn request_meta(&mut self, request: LangMetaRequest) -> LangMetaResponse {
match request {
LangMetaRequest::StageNames => {
LangMetaResponse::StageNames(stage_names().iter().map(|s| s.to_string()).collect())
}
_ => LangMetaResponse::Custom {
kind: "not-implemented".to_string(),
value: "".to_string(),
},
}
}
}
}

View File

@ -15,28 +15,28 @@ mod test;
/// Fully-qualified symbol name
#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
pub struct FQSN {
//TODO FQSN's need to be cheaply cloneable
pub struct Fqsn {
//TODO Fqsn's need to be cheaply cloneable
scopes: Vec<Scope>, //TODO rename to ScopeSegment
}
impl FQSN {
impl Fqsn {
fn from_scope_stack(scopes: &[Scope], new_name: Rc<String>) -> Self {
let mut v = Vec::new();
for s in scopes {
v.push(s.clone());
}
v.push(Scope::Name(new_name));
FQSN { scopes: v }
Fqsn { scopes: v }
}
#[cfg(test)]
fn from_strs(strs: &[&str]) -> FQSN {
fn from_strs(strs: &[&str]) -> Fqsn {
let mut scopes = vec![];
for s in strs {
scopes.push(Scope::Name(Rc::new(s.to_string())));
}
FQSN {
Fqsn {
scopes
}
}
@ -55,11 +55,11 @@ enum Scope {
#[derive(Debug, Clone)]
pub enum SymbolError {
DuplicateName {
prev_name: FQSN,
prev_name: Fqsn,
location: Location
},
DuplicateRecord {
type_name: FQSN,
type_name: Fqsn,
location: Location,
member: String,
}
@ -84,7 +84,7 @@ struct TypeKind;
/// Keeps track of what names were used in a given namespace.
struct NameTable<K> {
table: HashMap<FQSN, NameSpec<K>>
table: HashMap<Fqsn, NameSpec<K>>
}
impl<K> NameTable<K> {
@ -92,7 +92,7 @@ impl<K> NameTable<K> {
Self { table: HashMap::new() }
}
fn register(&mut self, name: FQSN, spec: NameSpec<K>) -> Result<(), SymbolError> {
fn register(&mut self, name: Fqsn, spec: NameSpec<K>) -> Result<(), SymbolError> {
match self.table.entry(name.clone()) {
Entry::Occupied(o) => {
Err(SymbolError::DuplicateName { prev_name: name, location: o.get().location })
@ -114,14 +114,14 @@ pub struct SymbolTable {
fq_names: NameTable<NameKind>, //Note that presence of two tables implies that a type and other binding with the same name can co-exist
types: NameTable<TypeKind>,
/// A map of the `ItemId`s of instances of use of names to their fully-canonicalized FQSN form.
/// A map of the `ItemId`s of instances of use of names to their fully-canonicalized Fqsn form.
/// Updated by the item id resolver.
id_to_fqsn: HashMap<ItemId, FQSN>,
id_to_fqsn: HashMap<ItemId, Fqsn>,
/// A map of the FQSN of an AST definition to a Symbol data structure, which contains
/// A map of the Fqsn of an AST definition to a Symbol data structure, which contains
/// some basic information about what that symbol is and (ideally) references to other tables
/// (e.g. typechecking tables) with more information about that symbol.
fqsn_to_symbol: HashMap<FQSN, Symbol>,
fqsn_to_symbol: HashMap<Fqsn, Symbol>,
}
impl SymbolTable {
@ -203,7 +203,7 @@ impl SymbolTable {
/// Register a new mapping of a fully-qualified symbol name (e.g. `Option::Some`)
/// to a Symbol, a descriptor of what that name refers to.
fn add_symbol(&mut self, fqsn: FQSN, symbol: Symbol) {
fn add_symbol(&mut self, fqsn: Fqsn, symbol: Symbol) {
self.symbol_trie.insert(&fqsn);
self.fqsn_to_symbol.insert(fqsn, symbol);
}
@ -230,7 +230,7 @@ impl SymbolTable {
for statement in statements {
let Statement { id: _, kind, location } = statement; //TODO I'm not sure if I need to do anything with this ID
let location = *location;
if let Err(err) = self.add_single_statement(kind, location, &scope_stack) {
if let Err(err) = self.add_single_statement(kind, location, scope_stack) {
errors.push(err);
} else { // If there's an error with a name, don't recurse into subscopes of that name
let recursive_errs = match kind {
@ -260,10 +260,10 @@ impl SymbolTable {
errors
}
fn add_single_statement(&mut self, kind: &StatementKind, location: Location, scope_stack: &Vec<Scope>) -> Result<(), SymbolError> {
fn add_single_statement(&mut self, kind: &StatementKind, location: Location, scope_stack: &[Scope]) -> Result<(), SymbolError> {
match kind {
StatementKind::Declaration(Declaration::FuncSig(signature)) => {
let fq_function = FQSN::from_scope_stack(scope_stack.as_ref(), signature.name.clone());
let fq_function = Fqsn::from_scope_stack(scope_stack, signature.name.clone());
self.fq_names.register(fq_function.clone(), NameSpec { location, kind: NameKind::Function })?;
self.types.register(fq_function.clone(), NameSpec { location, kind: TypeKind } )?;
@ -274,7 +274,7 @@ impl SymbolTable {
}
StatementKind::Declaration(Declaration::FuncDecl(signature, ..)) => {
let fn_name = &signature.name;
let fq_function = FQSN::from_scope_stack(scope_stack.as_ref(), fn_name.clone());
let fq_function = Fqsn::from_scope_stack(scope_stack, fn_name.clone());
self.fq_names.register(fq_function.clone(), NameSpec { location, kind: NameKind::Function })?;
self.types.register(fq_function.clone(), NameSpec { location, kind: TypeKind } )?;
@ -284,11 +284,11 @@ impl SymbolTable {
});
},
StatementKind::Declaration(Declaration::TypeDecl { name, .. }) => {
let fq_type = FQSN::from_scope_stack(scope_stack.as_ref(), name.name.clone());
let fq_type = Fqsn::from_scope_stack(scope_stack, name.name.clone());
self.types.register(fq_type, NameSpec { location, kind: TypeKind } )?;
},
StatementKind::Declaration(Declaration::Binding { name, .. }) => {
let fq_binding = FQSN::from_scope_stack(scope_stack.as_ref(), name.clone());
let fq_binding = Fqsn::from_scope_stack(scope_stack, name.clone());
self.fq_names.register(fq_binding.clone(), NameSpec { location, kind: NameKind::Binding })?;
self.add_symbol(fq_binding, Symbol {
local_name: name.clone(),
@ -296,7 +296,7 @@ impl SymbolTable {
});
}
StatementKind::Module(ModuleSpecifier { name, .. }) => {
let fq_module = FQSN::from_scope_stack(scope_stack.as_ref(), name.clone());
let fq_module = Fqsn::from_scope_stack(scope_stack, name.clone());
self.fq_names.register(fq_module, NameSpec { location, kind: NameKind::Module })?;
},
_ => (),
@ -308,7 +308,7 @@ impl SymbolTable {
let mut member_errors = vec![];
let mut errors = vec![];
let mut register = |fqsn: FQSN, spec: SymbolSpec| {
let mut register = |fqsn: Fqsn, spec: SymbolSpec| {
let name_spec = NameSpec { location, kind: TypeKind };
if let Err(err) = self.types.register(fqsn.clone(), name_spec) {
errors.push(err);
@ -329,7 +329,7 @@ impl SymbolTable {
for (index, variant) in variants.iter().enumerate() {
match variant {
Variant::UnitStruct(name) => {
let fq_name = FQSN::from_scope_stack(scope_stack.as_ref(), name.clone());
let fq_name = Fqsn::from_scope_stack(scope_stack.as_ref(), name.clone());
let spec = SymbolSpec::DataConstructor {
index,
arity: 0,
@ -338,7 +338,7 @@ impl SymbolTable {
register(fq_name, spec);
},
Variant::TupleStruct(name, items) => {
let fq_name = FQSN::from_scope_stack(scope_stack.as_ref(), name.clone());
let fq_name = Fqsn::from_scope_stack(scope_stack.as_ref(), name.clone());
let spec = SymbolSpec::DataConstructor {
index,
arity: items.len(),
@ -347,7 +347,7 @@ impl SymbolTable {
register(fq_name, spec);
},
Variant::Record { name, members } => {
let fq_name = FQSN::from_scope_stack(scope_stack.as_ref(), name.clone());
let fq_name = Fqsn::from_scope_stack(scope_stack.as_ref(), name.clone());
let mut seen_members = HashMap::new();
for (member_name, _) in members.iter() {

View File

@ -1,31 +1,31 @@
use std::rc::Rc;
use crate::symbol_table::{SymbolTable, FQSN, Scope};
use crate::symbol_table::{SymbolTable, Fqsn, Scope};
use crate::ast::*;
use crate::util::ScopeStack;
type FQSNPrefix = Vec<Scope>;
type FqsnPrefix = Vec<Scope>;
pub struct Resolver<'a> {
symbol_table: &'a mut super::SymbolTable,
name_scope_stack: ScopeStack<'a, Rc<String>, FQSNPrefix>,
name_scope_stack: ScopeStack<'a, Rc<String>, FqsnPrefix>,
}
impl<'a> Resolver<'a> {
pub fn new(symbol_table: &'a mut SymbolTable) -> Self {
let name_scope_stack: ScopeStack<'a, Rc<String>, FQSNPrefix> = ScopeStack::new(None);
let name_scope_stack: ScopeStack<'a, Rc<String>, FqsnPrefix> = ScopeStack::new(None);
Self { symbol_table, name_scope_stack }
}
pub fn resolve(&mut self, ast: &AST) {
walk_ast(self, ast);
}
fn lookup_name_in_scope(&self, sym_name: &QualifiedName) -> FQSN {
fn lookup_name_in_scope(&self, sym_name: &QualifiedName) -> Fqsn {
let QualifiedName { components, .. } = sym_name;
let first_component = &components[0];
match self.name_scope_stack.lookup(first_component) {
None => {
FQSN {
Fqsn {
scopes: components.iter()
.map(|name| Scope::Name(name.clone()))
.collect()
@ -33,10 +33,10 @@ impl<'a> Resolver<'a> {
},
Some(fqsn_prefix) => {
let mut full_name = fqsn_prefix.clone();
let rest_of_name: FQSNPrefix = components[1..].iter().map(|name| Scope::Name(name.clone())).collect();
let rest_of_name: FqsnPrefix = components[1..].iter().map(|name| Scope::Name(name.clone())).collect();
full_name.extend_from_slice(&rest_of_name);
FQSN {
Fqsn {
scopes: full_name
}
}
@ -59,7 +59,7 @@ impl<'a> ASTVisitor for Resolver<'a> {
let ImportSpecifier { ref path_components, ref imported_names, .. } = &import_spec;
match imported_names {
ImportedNames::All => {
let prefix = FQSN {
let prefix = Fqsn {
scopes: path_components.iter().map(|c| Scope::Name(c.clone())).collect()
};
let members = self.symbol_table.symbol_trie.get_children(&prefix);
@ -77,7 +77,7 @@ impl<'a> ASTVisitor for Resolver<'a> {
self.name_scope_stack.insert(name.clone(), fqsn_prefix);
}
ImportedNames::List(ref names) => {
let fqsn_prefix: FQSNPrefix = path_components.iter()
let fqsn_prefix: FqsnPrefix = path_components.iter()
.map(|c| Scope::Name(c.clone()))
.collect();
for name in names.iter() {
@ -88,12 +88,12 @@ impl<'a> ASTVisitor for Resolver<'a> {
}
fn qualified_name(&mut self, qualified_name: &QualifiedName) {
let fqsn = self.lookup_name_in_scope(&qualified_name);
let fqsn = self.lookup_name_in_scope(qualified_name);
self.symbol_table.id_to_fqsn.insert(qualified_name.id.clone(), fqsn);
}
fn named_struct(&mut self, qualified_name: &QualifiedName, _fields: &Vec<(Rc<String>, Expression)>) {
let fqsn = self.lookup_name_in_scope(&qualified_name);
let fqsn = self.lookup_name_in_scope(qualified_name);
self.symbol_table.id_to_fqsn.insert(qualified_name.id.clone(), fqsn);
}

View File

@ -1,12 +1,12 @@
use radix_trie::{Trie, TrieCommon, TrieKey};
use super::{Scope, FQSN};
use super::{Scope, Fqsn};
use std::hash::{Hasher, Hash};
use std::collections::hash_map::DefaultHasher;
#[derive(Debug)]
pub struct SymbolTrie(Trie<FQSN, ()>);
pub struct SymbolTrie(Trie<Fqsn, ()>);
impl TrieKey for FQSN {
impl TrieKey for Fqsn {
fn encode_bytes(&self) -> Vec<u8> {
let mut hasher = DefaultHasher::new();
let mut output = vec![];
@ -24,16 +24,16 @@ impl SymbolTrie {
SymbolTrie(Trie::new())
}
pub fn insert(&mut self, fqsn: &FQSN) {
pub fn insert(&mut self, fqsn: &Fqsn) {
self.0.insert(fqsn.clone(), ());
}
pub fn get_children(&self, fqsn: &FQSN) -> Vec<FQSN> {
pub fn get_children(&self, fqsn: &Fqsn) -> Vec<Fqsn> {
let subtrie = match self.0.subtrie(fqsn) {
Some(s) => s,
None => return vec![]
};
let output: Vec<FQSN> = subtrie.keys().filter(|cur_key| **cur_key != *fqsn).map(|fqsn| fqsn.clone()).collect();
let output: Vec<Fqsn> = subtrie.keys().filter(|cur_key| **cur_key != *fqsn).cloned().collect();
output
}
}
@ -41,10 +41,10 @@ impl SymbolTrie {
#[cfg(test)]
mod test {
use super::*;
use crate::symbol_table::FQSN;
use crate::symbol_table::Fqsn;
fn make_fqsn(strs: &[&str]) -> FQSN {
FQSN::from_strs(strs)
fn make_fqsn(strs: &[&str]) -> Fqsn {
Fqsn::from_strs(strs)
}
#[test]

View File

@ -10,8 +10,8 @@ fn add_symbols(src: &str) -> (SymbolTable, Result<(), Vec<SymbolError>>) {
(symbol_table, result)
}
fn make_fqsn(strs: &[&str]) -> FQSN {
FQSN::from_strs(strs)
fn make_fqsn(strs: &[&str]) -> Fqsn {
Fqsn::from_strs(strs)
}
@ -20,8 +20,8 @@ fn basic_symbol_table() {
let src = "let a = 10; fn b() { 20 }";
let (symbols, _) = add_symbols(src);
fn make_fqsn(strs: &[&str]) -> FQSN {
FQSN::from_strs(strs)
fn make_fqsn(strs: &[&str]) -> Fqsn {
Fqsn::from_strs(strs)
}
symbols.fq_names.table.get(&make_fqsn(&["b"])).unwrap();
@ -45,7 +45,7 @@ fn no_function_definition_duplicates() {
let errs = output.unwrap_err();
assert_matches!(&errs[..], [
SymbolError::DuplicateName { prev_name, ..}
] if prev_name == &FQSN::from_strs(&["a"])
] if prev_name == &Fqsn::from_strs(&["a"])
);
}
@ -64,7 +64,7 @@ fn no_variable_definition_duplicates() {
assert_matches!(&errs[..], [
SymbolError::DuplicateName { prev_name: pn1, ..},
SymbolError::DuplicateName { prev_name: pn2, ..}
] if pn1 == &FQSN::from_strs(&["a"]) && pn2 == &FQSN::from_strs(&["x"])
] if pn1 == &Fqsn::from_strs(&["a"]) && pn2 == &Fqsn::from_strs(&["x"])
);
}
@ -87,7 +87,7 @@ fn no_variable_definition_duplicates_in_function() {
let errs = output.unwrap_err();
assert_matches!(&errs[..], [
SymbolError::DuplicateName { prev_name: pn1, ..},
] if pn1 == &FQSN::from_strs(&["q", "x"])
] if pn1 == &Fqsn::from_strs(&["q", "x"])
);
}
@ -202,7 +202,7 @@ fn duplicate_modules() {
assert_matches!(&errs[..], [
SymbolError::DuplicateName { prev_name: pn1, ..},
] if pn1 == &FQSN::from_strs(&["a"])
] if pn1 == &Fqsn::from_strs(&["a"])
);
}
@ -227,7 +227,7 @@ fn duplicate_struct_members() {
assert_matches!(&errs[..], [
SymbolError::DuplicateRecord {
type_name, member, ..},
] if type_name == &FQSN::from_strs(&["Tarak", "Tarak"]) && member == "mets"
] if type_name == &Fqsn::from_strs(&["Tarak", "Tarak"]) && member == "mets"
);
}

View File

@ -135,7 +135,7 @@ pub fn tokenize(input: &str) -> Vec<Token> {
let mut tokens: Vec<Token> = Vec::new();
let mut input = Iterator::intersperse(input.lines().enumerate(), (0, "\n"))
.flat_map(|(line_idx, ref line)| {
.flat_map(|(line_idx, line)| {
line.chars().enumerate().map(move |(ch_idx, ch)| (line_idx, ch_idx, ch))
})
.peekable();
@ -144,11 +144,11 @@ pub fn tokenize(input: &str) -> Vec<Token> {
let cur_tok_kind = match c {
'/' => match input.peek().map(|t| t.2) {
Some('/') => {
while let Some((_, _, c)) = input.next() {
if c == '\n' {
break;
for (_, _, c) in input.by_ref() {
if c == '\n' {
break;
}
}
}
continue;
},
Some('*') => {
@ -194,18 +194,20 @@ pub fn tokenize(input: &str) -> Vec<Token> {
}
fn handle_digit(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenKind {
if c == '0' && input.peek().map_or(false, |&(_, _, c)| { c == 'x' }) {
input.next();
let rest: String = input.peeking_take_while(|&(_, _, ref c)| c.is_digit(16) || *c == '_').map(|(_, _, c)| { c }).collect();
HexLiteral(Rc::new(rest))
} else if c == '0' && input.peek().map_or(false, |&(_, _, c)| { c == 'b' }) {
input.next();
BinNumberSigil
} else {
let mut buf = c.to_string();
buf.extend(input.peeking_take_while(|&(_, _, ref c)| c.is_digit(10)).map(|(_, _, c)| { c }));
DigitGroup(Rc::new(buf))
}
let next_ch = input.peek().map(|&(_, _, c)| c);
if c == '0' && next_ch == Some('x') {
input.next();
let rest: String = input.peeking_take_while(|&(_, _, ref c)| c.is_digit(16) || *c == '_').map(|(_, _, c)| { c }).collect();
HexLiteral(Rc::new(rest))
} else if c == '0' && next_ch == Some('b') {
input.next();
BinNumberSigil
} else {
let mut buf = c.to_string();
buf.extend(input.peeking_take_while(|&(_, _, ref c)| c.is_digit(10)).map(|(_, _, c)| { c }));
DigitGroup(Rc::new(buf))
}
}
fn handle_quote(input: &mut Peekable<impl Iterator<Item=CharData>>, quote_prefix: Option<&str>) -> TokenKind {
@ -236,7 +238,8 @@ fn handle_quote(input: &mut Peekable<impl Iterator<Item=CharData>>, quote_prefix
fn handle_alphabetic(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenKind {
let mut buf = String::new();
buf.push(c);
if c == '_' && input.peek().map(|&(_, _, c)| { !c.is_alphabetic() }).unwrap_or(true) {
let next_is_alphabetic = input.peek().map(|&(_, _, c)| !c.is_alphabetic()).unwrap_or(true);
if c == '_' && next_is_alphabetic {
return TokenKind::Underscore
}
@ -263,8 +266,9 @@ fn handle_alphabetic(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>
fn handle_operator(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenKind {
match c {
'<' | '>' | '|' | '.' | '=' => {
let ref next = input.peek().map(|&(_, _, c)| { c });
if !next.map(|n| { is_operator(&n) }).unwrap_or(false) {
let next = &input.peek().map(|&(_, _, c)| { c });
let next_is_op = next.map(|n| { is_operator(&n) }).unwrap_or(false);
if !next_is_op {
return match c {
'<' => LAngleBracket,
'>' => RAngleBracket,