Compare commits
11 Commits
fqsn_fix_2
...
complex_vi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ec1d78f1b | ||
|
|
afcb10bb72 | ||
|
|
8de625e540 | ||
|
|
a2bd9a3985 | ||
|
|
e4a1a23f4d | ||
|
|
2cd325ba12 | ||
|
|
8218007f1c | ||
|
|
040ab11873 | ||
|
|
b967fa1911 | ||
|
|
4c718ed977 | ||
|
|
d20acf7166 |
14
TODO.md
14
TODO.md
@@ -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
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::rc::Rc;
|
||||
use crate::ast::*;
|
||||
use crate::util::deref_optional_box;
|
||||
|
||||
//TODO maybe these functions should take closures that return a KeepRecursing | StopHere type,
|
||||
//or a tuple of (T, <that type>)
|
||||
@@ -39,3 +40,126 @@ pub trait ASTVisitor: Sized {
|
||||
fn prefix_exp(&mut self, _op: &PrefixOp, _arg: &Expression) {}
|
||||
fn pattern(&mut self, _pat: &Pattern) {}
|
||||
}
|
||||
|
||||
pub trait ASTVisitorNew: Sized {
|
||||
fn expression(&mut self, _expression: &Expression) -> VisitResult { VisitResult::default() }
|
||||
fn expression_kind(&mut self, _kind: &ExpressionKind) -> VisitResult { VisitResult::default() }
|
||||
|
||||
fn type_annotation(&mut self, _id: ItemId, _type_anno: Option<&TypeIdentifier>) -> VisitResult { VisitResult::default() }
|
||||
|
||||
fn named_struct(&mut self, _name: &QualifiedName, _fields: &Vec<(Rc<String>, Expression)>) -> VisitResult { VisitResult::default() }
|
||||
|
||||
fn nat_literal(&mut self, _id: ItemId, _n: u64)-> VisitResult { VisitResult::default() }
|
||||
fn float_literal(&mut self, _id: ItemId, _f: f64) -> VisitResult { VisitResult::default() }
|
||||
fn string_literal(&mut self, _id: ItemId, _s: &Rc<String>) -> VisitResult { VisitResult::default() }
|
||||
fn bool_literal(&mut self, _id: ItemId, _b: bool) -> VisitResult { VisitResult::default() }
|
||||
|
||||
fn qualified_name(&mut self, _id: ItemId, _name: &QualifiedName) {}
|
||||
}
|
||||
|
||||
enum VisitResult {
|
||||
KeepRecursing,
|
||||
Stop
|
||||
}
|
||||
|
||||
impl std::default::Default for VisitResult {
|
||||
fn default() -> VisitResult { VisitResult::KeepRecursing }
|
||||
}
|
||||
|
||||
pub fn traverse<V: ASTVisitorNew>(visitor: &mut V, expression: &Expression) {
|
||||
expresion(visitor, expresion)
|
||||
}
|
||||
|
||||
fn expression<V: ASTVisitorNew>(visitor: &mut V, expression: &Expression) {
|
||||
use ExpressionKind::*;
|
||||
|
||||
let Expression { id, kind, type_anno } = expression;
|
||||
|
||||
let output = match kind {
|
||||
NatLiteral(n) => v.nat_literal(id, *n),
|
||||
FloatLiteral(f) => v.float_literal(id, *f),
|
||||
StringLiteral(s) => v.string_literal(id, s),
|
||||
BoolLiteral(b) => v.bool_literal(d, *b),
|
||||
BinExp(op, lhs, rhs) => {
|
||||
expression(v, lhs);
|
||||
expression(v, rhs);
|
||||
},
|
||||
PrefixExp(op, arg) => {
|
||||
expression(v, arg);
|
||||
}
|
||||
TupleLiteral(exprs) => {
|
||||
for expr in exprs {
|
||||
expression(v, expr);
|
||||
}
|
||||
},
|
||||
Value(name) => v.qualified_name(id, name),
|
||||
NamedStruct { name, fields } => {
|
||||
for (_, expr) in fields.iter() {
|
||||
v.expression(expr);
|
||||
}
|
||||
}
|
||||
Call { f, arguments } => {
|
||||
expression(v, f);
|
||||
for arg in args.iter() {
|
||||
match arg {
|
||||
InvocationArgument::Positional(expr) => {
|
||||
expression(v, expr);
|
||||
},
|
||||
InvocationArgument::Keyword { expr, .. } => {
|
||||
expression(v, expr);
|
||||
},
|
||||
Ignored => (),
|
||||
}
|
||||
}
|
||||
},
|
||||
Index { indexee, indexers } => {
|
||||
expression(v, indexee);
|
||||
for i in indexers.iter() {
|
||||
expression(v, i);
|
||||
}
|
||||
},
|
||||
IfExpression { discriminator, body } => {
|
||||
discriminator.as_ref().map(|d| expression(v, d));
|
||||
match body {
|
||||
IfExpressionBody::SimpleConditional { then_case, else_case } => {
|
||||
walk_block(v, then_case);
|
||||
else_case.as_ref().map(|block| walk_block(v, block));
|
||||
},
|
||||
IfExpressionBody::SimplePatternMatch { pattern, then_case, else_case } => {
|
||||
v.pattern(pattern);
|
||||
walk_pattern(v, pattern);
|
||||
walk_block(v, then_case);
|
||||
else_case.as_ref().map(|block| walk_block(v, block));
|
||||
},
|
||||
IfExpressionBody::CondList(arms) => {
|
||||
for arm in arms {
|
||||
v.condition_arm(arm);
|
||||
condition_arm(v, arm);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
WhileExpression { condition, body } => (),
|
||||
ForExpression { enumerators, body } => (),
|
||||
Lambda { params , type_anno, body } => {
|
||||
for param in params {
|
||||
formal_param(v, param);
|
||||
}
|
||||
v.type_annotation(type_anno);
|
||||
},
|
||||
ListLiteral(exprs) => {
|
||||
for expr in exprs {
|
||||
expression(v, expr);
|
||||
}
|
||||
},
|
||||
};
|
||||
output
|
||||
}
|
||||
|
||||
fn formal_param<V: ASTVisitorNew>(v: &mut V, param: &FormalParam) {
|
||||
param.default.as_ref().map(|p| {
|
||||
expression(v, p);
|
||||
});
|
||||
v.type_annotation(param.anno.as_ref());
|
||||
}
|
||||
|
||||
|
||||
@@ -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)),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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())))
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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())) }]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user