12 Commits

Author SHA1 Message Date
greg
bc87f8cd90 Initial work 2020-03-04 11:25:23 -08:00
greg
a0955e07dc Fix attribute 2020-02-12 22:14:21 -08:00
greg
afcb10bb72 Add random idea 2019-11-18 03:11:00 -08:00
greg
8de625e540 Got rid of symbol table from eval 2019-11-10 03:28:31 -08:00
greg
a2bd9a3985 Remove symbol table from evaluator 2019-11-09 19:52:05 -08:00
greg
e4a1a23f4d Moved sym lookup logic from eval to ast reducer 2019-11-09 19:49:02 -08:00
greg
2cd325ba12 Add plan of attack notes 2019-11-08 18:56:15 -08:00
greg
8218007f1c Commit this temporary fix 2019-11-08 18:53:38 -08:00
greg
040ab11873 Move reduction of values into separate method 2019-11-07 03:28:18 -08:00
greg
b967fa1911 to_repl() doesn't need symbol table handle 2019-11-07 02:42:17 -08:00
greg
4c718ed977 Add TODO for symbol resolver 2019-11-06 18:41:37 -08:00
greg
d20acf7166 Add tokenization for string literal prefixes 2019-11-05 02:22:11 -08:00
13 changed files with 125 additions and 96 deletions

5
Cargo.lock generated
View File

@@ -461,6 +461,10 @@ dependencies = [
"autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "partis"
version = "0.1.0"
[[package]]
name = "phf"
version = "0.7.24"
@@ -739,6 +743,7 @@ name = "schala"
version = "0.1.0"
dependencies = [
"includedir_codegen 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"partis 0.1.0",
"schala-lang 0.1.0",
"schala-repl 0.1.0",
]

View File

@@ -7,6 +7,7 @@ authors = ["greg <greg.shuflin@protonmail.com>"]
schala-repl = { path = "schala-repl" }
schala-lang = { path = "schala-lang/language" }
partis = { path="partis" }
# maaru-lang = { path = "maaru" }
# rukka-lang = { path = "rukka" }
# robo-lang = { path = "robo" }

14
TODO.md
View File

@@ -1,3 +1,11 @@
# Plan of attack
1. modify visitor so it can handle scopes
-this is needed both to handle import scope correctly
-and also to support making FQSNs aware of function parameters
2. Once FQSNs are aware of function parameters, most of the Rc<String> things in eval.rs can go away
# TODO items
-use 'let' sigil in patterns for variables :
@@ -8,6 +16,9 @@
}
```
-idea: what if there was something like React jsx syntas built in? i.e. a way to automatically transform some kind of markup
into a function call, cf. `<h1 prop="arg">` -> h1(prop=arg)
## General code cleanup
- I think I can restructure the parser to get rid of most instances of expect!, at least at the beginning of a rule
DONE -experiment with storing metadata via ItemIds on AST nodes (cf. https://rust-lang.github.io/rustc-guide/hir.html, https://github.com/rust-lang/rust/blob/master/src/librustc/hir/mod.rs )
@@ -19,6 +30,9 @@ DONE -experiment with storing metadata via ItemIds on AST nodes (cf. https://rus
-look at https://gitlab.haskell.org/ghc/ghc/wikis/pattern-synonyms
2) the non-value-returning, default one like in rustc (cf. https://github.com/rust-unofficial/patterns/blob/master/patterns/visitor.md)
-parser error - should report subset of AST parsed *so far*
- what if you used python 'def' syntax to define a function? what error message makes sense here?
## Reduction
- make a good type for actual language builtins to avoid string comparisons

9
partis/Cargo.toml Normal file
View File

@@ -0,0 +1,9 @@
[package]
name = "partis"
version = "0.1.0"
authors = ["greg <greg.shuflin@protonmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

17
partis/src/lib.rs Normal file
View File

@@ -0,0 +1,17 @@
struct ParseError { }
enum ParseResult<'a, T> {
Success(T, &'a str),
Failure(ParseError),
Incomplete,
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

View File

@@ -14,13 +14,12 @@ mod test;
pub struct State<'a> {
values: ScopeStack<'a, Rc<String>, ValueEntry>,
symbol_table_handle: SymbolTableHandle,
}
impl<'a> State<'a> {
pub fn new(symbol_table_handle: SymbolTableHandle) -> State<'a> {
pub fn new() -> State<'a> {
let values = ScopeStack::new(Some(format!("global")));
State { values, symbol_table_handle }
State { values }
}
pub fn debug_print(&self) -> String {
@@ -30,7 +29,6 @@ impl<'a> State<'a> {
fn new_frame(&'a self, items: &'a Vec<Node>, bound_vars: &BoundVars) -> State<'a> {
let mut inner_state = State {
values: self.values.new_scope(None),
symbol_table_handle: self.symbol_table_handle.clone(),
};
for (bound_var, val) in bound_vars.iter().zip(items.iter()) {
if let Some(bv) = bound_var.as_ref() {
@@ -69,12 +67,12 @@ fn paren_wrapped_vec(terms: impl Iterator<Item=String>) -> String {
impl Node {
fn to_repl(&self, symbol_table: &SymbolTable) -> String {
fn to_repl(&self) -> String {
match self {
Node::Expr(e) => e.to_repl(symbol_table),
Node::Expr(e) => e.to_repl(),
Node::PrimObject { name, items, .. } if items.len() == 0 => format!("{}", name),
Node::PrimObject { name, items, .. } => format!("{}{}", name, paren_wrapped_vec(items.iter().map(|x| x.to_repl(symbol_table)))),
Node::PrimTuple { items } => format!("{}", paren_wrapped_vec(items.iter().map(|x| x.to_repl(symbol_table)))),
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()))),
}
}
fn is_true(&self) -> bool {
@@ -99,12 +97,10 @@ impl Expr {
fn to_node(self) -> Node {
Node::Expr(self)
}
fn to_repl(&self, symbol_table: &SymbolTable) -> String {
fn to_repl(&self) -> String {
use self::Lit::*;
use self::Func::*;
let _ = symbol_table;
match self {
Expr::Lit(ref l) => match l {
Nat(n) => format!("{}", n),
@@ -121,7 +117,7 @@ impl Expr {
Expr::Constructor { type_name, arity, .. } => {
format!("<constructor for `{}` arity {}>", type_name, arity)
},
Expr::Tuple(exprs) => paren_wrapped_vec(exprs.iter().map(|x| x.to_repl(symbol_table))),
Expr::Tuple(exprs) => paren_wrapped_vec(exprs.iter().map(|x| x.to_repl())),
_ => format!("{:?}", self),
}
}
@@ -156,8 +152,7 @@ impl<'a> State<'a> {
for statement in ast.0 {
match self.statement(statement) {
Ok(Some(ref output)) if repl => {
let ref symbol_table = self.symbol_table_handle.borrow();
acc.push(Ok(output.to_repl(symbol_table)))
acc.push(Ok(output.to_repl()))
},
Ok(_) => (),
Err(error) => {
@@ -211,7 +206,10 @@ impl<'a> State<'a> {
Node::Expr(expr) => match expr {
literal @ Lit(_) => Ok(Node::Expr(literal)),
Call { box f, args } => self.call_expression(f, args),
Sym(v) => self.handle_sym(v),
Sym(name) => Ok(match self.values.lookup(&name) {
Some(ValueEntry::Binding { val, .. }) => val.clone(),
None => return Err(format!("Could not look up symbol {}", name))
}),
Constructor { arity, ref name, tag, .. } if arity == 0 => Ok(Node::PrimObject { name: name.clone(), tag, items: vec![] }),
constructor @ Constructor { .. } => Ok(Node::Expr(constructor)),
func @ Func(_) => Ok(Node::Expr(func)),
@@ -263,7 +261,6 @@ impl<'a> State<'a> {
}
let mut func_state = State {
values: self.values.new_scope(name.map(|n| format!("{}", n))),
symbol_table_handle: self.symbol_table_handle.clone(),
};
for (param, val) in params.into_iter().zip(args.into_iter()) {
let val = func_state.expression(Node::Expr(val))?;
@@ -342,13 +339,11 @@ impl<'a> State<'a> {
/* builtin functions */
(IOPrint, &[ref anything]) => {
let ref symbol_table = self.symbol_table_handle.borrow();
print!("{}", anything.to_repl(symbol_table));
print!("{}", anything.to_repl());
Expr::Unit.to_node()
},
(IOPrintLn, &[ref anything]) => {
let ref symbol_table = self.symbol_table_handle.borrow();
println!("{}", anything.to_repl(symbol_table));
println!("{}", anything.to_repl());
Expr::Unit.to_node()
},
(IOGetLine, &[]) => {
@@ -457,46 +452,4 @@ impl<'a> State<'a> {
}
Err(format!("{:?} failed pattern match", cond))
}
//TODO if I don't need to lookup by name here...
fn handle_sym(&mut self, name: Rc<String>) -> EvalResult<Node> {
use self::ValueEntry::*;
use self::Func::*;
//TODO add a layer of indirection here to talk to the symbol table first, and only then look up
//in the values table
let symbol_table = self.symbol_table_handle.borrow();
let value = symbol_table.lookup_by_fqsn(&fqsn!(name ; tr));
Ok(match value {
Some(Symbol { local_name, spec, .. }) => match spec {
//TODO I'll need this type_name later to do a table lookup
SymbolSpec::DataConstructor { type_name: _type_name, type_args, .. } => {
if type_args.len() == 0 {
Node::PrimObject { name: local_name.clone(), tag: 0, items: vec![] }
} else {
return Err(format!("This data constructor thing not done"))
}
},
SymbolSpec::Func(_) => match self.values.lookup(&name) {
Some(Binding { val: Node::Expr(Expr::Func(UserDefined { name, params, body })), .. }) => {
Node::Expr(Expr::Func(UserDefined { name: name.clone(), params: params.clone(), body: body.clone() }))
},
_ => unreachable!(),
},
SymbolSpec::RecordConstructor { .. } => return Err(format!("This shouldn't be a record!")),
SymbolSpec::Binding => match self.values.lookup(&name) {
Some(Binding { val, .. }) => val.clone(),
None => return Err(format!("Symbol {} exists in symbol table but not in evaluator table", name))
}
SymbolSpec::Type { name } => return Err(format!("Symbol {} not in scope", name)),
},
//TODO ideally this should be returning a runtime error if this is ever None, but it's not
//handling all bindings correctly yet
//None => return Err(format!("Couldn't find value {}", name)),
None => match self.values.lookup(&name) {
Some(Binding { val, .. }) => val.clone(),
None => return Err(format!("Couldn't find value {}", name)),
}
})
}
}

View File

@@ -12,14 +12,14 @@ fn evaluate_all_outputs(input: &str) -> Vec<Result<String, String>> {
let (mut ast, source_map) = crate::util::quick_ast(input);
let source_map = Rc::new(RefCell::new(source_map));
let symbol_table = Rc::new(RefCell::new(SymbolTable::new(source_map)));
let mut state = State::new(symbol_table);
state.symbol_table_handle.borrow_mut().add_top_level_symbols(&ast).unwrap();
symbol_table.borrow_mut().add_top_level_symbols(&ast).unwrap();
{
let mut scope_resolver = ScopeResolver::new(state.symbol_table_handle.clone());
let mut scope_resolver = ScopeResolver::new(symbol_table.clone());
let _ = scope_resolver.resolve(&mut ast);
}
let reduced = reduce(&ast, &state.symbol_table_handle.borrow());
let reduced = reduce(&ast, &symbol_table.borrow());
let mut state = State::new();
let all_output = state.evaluate(reduced, true);
all_output
}

View File

@@ -1,5 +1,4 @@
#![feature(trace_macros)]
#![feature(custom_attribute)]
//#![feature(unrestricted_attribute_tokens)]
#![feature(slice_patterns, box_patterns, box_syntax)]

View File

@@ -991,7 +991,7 @@ impl Parser {
self.token_handler.next();
Pattern::Literal(PatternLiteral::BoolPattern(false))
},
StrLiteral(s) => {
StrLiteral { s, .. } => {
self.token_handler.next();
Pattern::Literal(PatternLiteral::StringPattern(s))
},
@@ -1140,7 +1140,7 @@ impl Parser {
let id = self.id_store.fresh();
Ok(Expression::new(id, BoolLiteral(false)))
},
StrLiteral(s) => {
StrLiteral {s, ..} => {
self.token_handler.next();
let id = self.id_store.fresh();
Ok(Expression::new(id, StringLiteral(s.clone())))

View File

@@ -42,9 +42,9 @@ pub enum Stmt {
pub enum Expr {
Unit,
Lit(Lit),
Sym(Rc<String>), //a Sym is anything that can be looked up by name at runtime - i.e. a function or variable address
Tuple(Vec<Expr>),
Func(Func),
Sym(Rc<String>),
Constructor {
type_name: Rc<String>,
name: Rc<String>,
@@ -56,7 +56,7 @@ pub enum Expr {
args: Vec<Expr>,
},
Assign {
val: Box<Expr>,
val: Box<Expr>, //TODO this probably can't be a val
expr: Box<Expr>,
},
Conditional {
@@ -164,25 +164,7 @@ impl<'a> Reducer<'a> {
BoolLiteral(b) => Expr::Lit(Lit::Bool(*b)),
BinExp(binop, lhs, rhs) => self.binop(binop, lhs, rhs),
PrefixExp(op, arg) => self.prefix(op, arg),
Value(qualified_name) => {
let ref id = qualified_name.id;
let ref sym_name = match symbol_table.get_fqsn_from_id(id) {
Some(fqsn) => fqsn,
None => return Expr::ReductionError(format!("FQSN lookup for Value {:?} failed", qualified_name)),
};
//TODO this probably needs to change
let FullyQualifiedSymbolName(ref v) = sym_name;
let name = v.last().unwrap().name.clone();
match symbol_table.lookup_by_fqsn(&sym_name) {
Some(Symbol { spec: SymbolSpec::DataConstructor { index, type_args, type_name}, .. }) => Expr::Constructor {
type_name: type_name.clone(),
name: name.clone(),
tag: index.clone(),
arity: type_args.len(),
},
_ => Expr::Sym(name.clone()),
}
},
Value(qualified_name) => self.value(qualified_name),
Call { f, arguments } => self.reduce_call_expression(f, arguments),
TupleLiteral(exprs) => Expr::Tuple(exprs.iter().map(|e| self.expression(e)).collect()),
IfExpression { discriminator, body } => self.reduce_if_expression(deref_optional_box(discriminator), body),
@@ -195,6 +177,38 @@ impl<'a> Reducer<'a> {
}
}
fn value(&mut self, qualified_name: &QualifiedName) -> Expr {
let symbol_table = self.symbol_table;
let ref id = qualified_name.id;
let ref sym_name = match symbol_table.get_fqsn_from_id(id) {
Some(fqsn) => fqsn,
None => return Expr::ReductionError(format!("FQSN lookup for Value {:?} failed", qualified_name)),
};
//TODO this probably needs to change
let FullyQualifiedSymbolName(ref v) = sym_name;
let name = v.last().unwrap().name.clone();
let Symbol { local_name, spec, .. } = match symbol_table.lookup_by_fqsn(&sym_name) {
Some(s) => s,
//None => return Expr::ReductionError(format!("Symbol {:?} not found", sym_name)),
None => return Expr::Sym(name.clone())
};
match spec {
SymbolSpec::RecordConstructor { .. } => Expr::ReductionError(format!("AST reducer doesn't expect a RecordConstructor here")),
SymbolSpec::DataConstructor { index, type_args, type_name } => Expr::Constructor {
type_name: type_name.clone(),
name: name.clone(),
tag: index.clone(),
arity: type_args.len(),
},
SymbolSpec::Func(_) => Expr::Sym(local_name.clone()),
SymbolSpec::Binding => Expr::Sym(local_name.clone()), //TODO not sure if this is right, probably needs to eventually be fqsn
SymbolSpec::Type { .. } => Expr::ReductionError("AST reducer doesnt expect a type here".to_string())
}
}
fn reduce_lambda(&mut self, params: &Vec<FormalParam>, body: &Block) -> Expr {
Expr::Func(Func::UserDefined {
name: None,

View File

@@ -47,7 +47,7 @@ impl Schala {
symbol_table: symbols.clone(),
source_map: source_map.clone(),
resolver: crate::scope_resolution::ScopeResolver::new(symbols.clone()),
state: eval::State::new(symbols),
state: eval::State::new(),
type_context: typechecking::TypeContext::new(),
active_parser: parsing::Parser::new(source_map)
}

View File

@@ -13,6 +13,7 @@ pub struct ScopeResolver<'a> {
}
impl<'a> ASTVisitor for ScopeResolver<'a> {
//TODO need to un-insert these - maybe need to rethink visitor
fn import(&mut self, import_spec: &ImportSpecifier) {
let ref symbol_table = self.symbol_table_handle.borrow();
let ImportSpecifier { ref path_components, ref imported_names, .. } = &import_spec;

View File

@@ -21,7 +21,10 @@ pub enum TokenKind {
Operator(Rc<String>),
DigitGroup(Rc<String>), HexLiteral(Rc<String>), BinNumberSigil,
StrLiteral(Rc<String>),
StrLiteral {
s: Rc<String>,
prefix: Option<Rc<String>>
},
Identifier(Rc<String>),
Keyword(Kw),
@@ -37,7 +40,7 @@ impl fmt::Display for TokenKind {
&Operator(ref s) => write!(f, "Operator({})", **s),
&DigitGroup(ref s) => write!(f, "DigitGroup({})", s),
&HexLiteral(ref s) => write!(f, "HexLiteral({})", s),
&StrLiteral(ref s) => write!(f, "StrLiteral({})", s),
&StrLiteral {ref s, .. } => write!(f, "StrLiteral({})", s),
&Identifier(ref s) => write!(f, "Identifier({})", s),
&Error(ref s) => write!(f, "Error({})", s),
other => write!(f, "{:?}", other),
@@ -163,7 +166,7 @@ pub fn tokenize(input: &str) -> Vec<Token> {
'(' => LParen, ')' => RParen,
'{' => LCurlyBrace, '}' => RCurlyBrace,
'[' => LSquareBracket, ']' => RSquareBracket,
'"' => handle_quote(&mut input),
'"' => handle_quote(&mut input, None),
'\\' => Backslash,
c if c.is_digit(10) => handle_digit(c, &mut input),
c if c.is_alphabetic() || c == '_' => handle_alphabetic(c, &mut input),
@@ -191,7 +194,7 @@ fn handle_digit(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>) ->
}
}
fn handle_quote(input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenKind {
fn handle_quote(input: &mut Peekable<impl Iterator<Item=CharData>>, quote_prefix: Option<&str>) -> TokenKind {
let mut buf = String::new();
loop {
match input.next().map(|(_, _, c)| { c }) {
@@ -213,7 +216,7 @@ fn handle_quote(input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenKind
None => return TokenKind::Error(format!("Unclosed string")),
}
}
TokenKind::StrLiteral(Rc::new(buf))
TokenKind::StrLiteral { s: Rc::new(buf), prefix: quote_prefix.map(|s| Rc::new(s.to_string())) }
}
fn handle_alphabetic(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>) -> TokenKind {
@@ -225,6 +228,10 @@ fn handle_alphabetic(c: char, input: &mut Peekable<impl Iterator<Item=CharData>>
loop {
match input.peek().map(|&(_, _, c)| { c }) {
Some(c) if c == '"' => {
input.next();
return handle_quote(input, Some(&buf));
},
Some(c) if c.is_alphanumeric() || c == '_' => {
input.next();
buf.push(c);
@@ -325,4 +332,13 @@ mod schala_tokenizer_tests {
let token_kinds: Vec<TokenKind> = tokenize("1 `plus` 2").into_iter().map(move |t| t.kind).collect();
assert_eq!(token_kinds, vec![digit!("1"), op!("plus"), digit!("2")]);
}
#[test]
fn string_literals() {
let token_kinds: Vec<TokenKind> = tokenize(r#""some string""#).into_iter().map(move |t| t.kind).collect();
assert_eq!(token_kinds, vec![StrLiteral { s: Rc::new("some string".to_string()), prefix: None }]);
let token_kinds: Vec<TokenKind> = tokenize(r#"b"some bytestring""#).into_iter().map(move |t| t.kind).collect();
assert_eq!(token_kinds, vec![StrLiteral { s: Rc::new("some bytestring".to_string()), prefix: Some(Rc::new("b".to_string())) }]);
}
}