ops, negative number parsing
This commit is contained in:
parent
2372f0d254
commit
8e9ac2262a
8 changed files with 451 additions and 30 deletions
|
|
@ -39,3 +39,11 @@ pub fn define_expr(name: &str, expr: Expr) -> Definition {
|
||||||
pub fn func(args: Vec<Arg>, body: Expr) -> Expr {
|
pub fn func(args: Vec<Arg>, body: Expr) -> Expr {
|
||||||
Expr::Func(Box::new(Fn { args, body }))
|
Expr::Func(Box::new(Fn { args, body }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn op(op: Op, a: Expr, b: Expr) -> Expr {
|
||||||
|
Expr::Op {
|
||||||
|
op,
|
||||||
|
lhs: Box::new(a),
|
||||||
|
rhs: Box::new(b),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use std::collections::BTreeMap;
|
||||||
/// An expression.
|
/// An expression.
|
||||||
#[derive(PartialEq, PartialOrd, Debug, Clone)]
|
#[derive(PartialEq, PartialOrd, Debug, Clone)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
|
Not(Box<Expr>),
|
||||||
Op {
|
Op {
|
||||||
lhs: Box<Expr>,
|
lhs: Box<Expr>,
|
||||||
op: Op,
|
op: Op,
|
||||||
|
|
@ -41,7 +42,36 @@ pub enum Expr {
|
||||||
#[derive(PartialEq, PartialOrd, Debug, Clone)]
|
#[derive(PartialEq, PartialOrd, Debug, Clone)]
|
||||||
pub enum Op {
|
pub enum Op {
|
||||||
Assign,
|
Assign,
|
||||||
// Equals,
|
Compare(Cmp),
|
||||||
|
Calc(Calc),
|
||||||
|
Bool(BoolOp),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, PartialOrd, Debug, Clone)]
|
||||||
|
pub enum BoolOp {
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, PartialOrd, Debug, Clone)]
|
||||||
|
pub enum Cmp {
|
||||||
|
Eq,
|
||||||
|
NotEq,
|
||||||
|
Gt,
|
||||||
|
Lt,
|
||||||
|
Gte,
|
||||||
|
Lte,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, PartialOrd, Debug, Clone)]
|
||||||
|
pub enum Calc {
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Div,
|
||||||
|
Mul,
|
||||||
|
Mod,
|
||||||
|
BinAnd,
|
||||||
|
BinOr,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A statement.
|
/// A statement.
|
||||||
|
|
|
||||||
|
|
@ -172,6 +172,10 @@ fn eval_expr(expr_env: &Env, state: &mut State, expr: &ast::Expr) -> Result<ast:
|
||||||
Backtrace::capture(),
|
Backtrace::capture(),
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
|
ast::Expr::Not(e) => match eval_expr(&expr_env, state, e)? {
|
||||||
|
ast::Value::Boolean(b) => Ok(ast::Value::Boolean(!b)),
|
||||||
|
v => Err(Error::NotABoolean(v, Backtrace::capture())),
|
||||||
|
},
|
||||||
ast::Expr::Value(v) => match v {
|
ast::Expr::Value(v) => match v {
|
||||||
ast::Value::Closure { expr, env } => {
|
ast::Value::Closure { expr, env } => {
|
||||||
let closure_env: Env = state.envs.get(env)?.clone();
|
let closure_env: Env = state.envs.get(env)?.clone();
|
||||||
|
|
@ -189,13 +193,124 @@ fn eval_expr(expr_env: &Env, state: &mut State, expr: &ast::Expr) -> Result<ast:
|
||||||
}
|
}
|
||||||
v => Err(Error::NotAReference(v.clone(), Backtrace::capture())),
|
v => Err(Error::NotAReference(v.clone(), Backtrace::capture())),
|
||||||
},
|
},
|
||||||
/*
|
ast::Op::Calc(cop) => {
|
||||||
_ => {
|
let lhs = eval_expr(expr_env, state, lhs)?;
|
||||||
let _lhs = eval_expr(expr_env, state, lhs)?;
|
let rhs = eval_expr(expr_env, state, rhs)?;
|
||||||
let _rhs = eval_expr(expr_env, state, rhs)?;
|
|
||||||
todo!()
|
match (lhs, rhs) {
|
||||||
|
(ast::Value::Int(a), ast::Value::Int(b)) => {
|
||||||
|
let cop = match cop {
|
||||||
|
ast::Calc::Add => |a, b| a + b,
|
||||||
|
ast::Calc::Sub => |a, b| a - b,
|
||||||
|
ast::Calc::Mul => |a, b| a * b,
|
||||||
|
ast::Calc::Div => |a, b| a / b,
|
||||||
|
ast::Calc::Mod => |a, b| a % b,
|
||||||
|
ast::Calc::BinAnd => |a, b| a & b,
|
||||||
|
ast::Calc::BinOr => |a, b| a | b,
|
||||||
|
};
|
||||||
|
Ok(ast::Value::Int(cop(a, b)))
|
||||||
|
}
|
||||||
|
(ast::Value::Float(a), ast::Value::Int(b)) => {
|
||||||
|
let cop = match cop {
|
||||||
|
ast::Calc::Add => |a, b| a + b,
|
||||||
|
ast::Calc::Sub => |a, b| a - b,
|
||||||
|
ast::Calc::Mul => |a, b| a * b,
|
||||||
|
ast::Calc::Div => |a, b| a / b,
|
||||||
|
ast::Calc::Mod => |a, b| a % b,
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
|
Ok(ast::Value::Float(cop(a, b as f32)))
|
||||||
|
}
|
||||||
|
(ast::Value::Int(a), ast::Value::Float(b)) => {
|
||||||
|
let cop = match cop {
|
||||||
|
ast::Calc::Add => |a, b| a + b,
|
||||||
|
ast::Calc::Sub => |a, b| a - b,
|
||||||
|
ast::Calc::Mul => |a, b| a * b,
|
||||||
|
ast::Calc::Div => |a, b| a / b,
|
||||||
|
ast::Calc::Mod => |a, b| a % b,
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
|
Ok(ast::Value::Float(cop(a as f32, b)))
|
||||||
|
}
|
||||||
|
(ast::Value::Float(a), ast::Value::Float(b)) => {
|
||||||
|
let cop = match cop {
|
||||||
|
ast::Calc::Add => |a, b| a + b,
|
||||||
|
ast::Calc::Sub => |a, b| a - b,
|
||||||
|
ast::Calc::Mul => |a, b| a * b,
|
||||||
|
ast::Calc::Div => |a, b| a / b,
|
||||||
|
ast::Calc::Mod => |a, b| a % b,
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
|
Ok(ast::Value::Float(cop(a, b)))
|
||||||
|
}
|
||||||
|
(lhs, rhs) => Err(Error::OpError(lhs, op.clone(), rhs, Backtrace::capture())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast::Op::Compare(cop) => {
|
||||||
|
let lhs = eval_expr(expr_env, state, lhs)?;
|
||||||
|
let rhs = eval_expr(expr_env, state, rhs)?;
|
||||||
|
match (lhs, rhs) {
|
||||||
|
(ast::Value::Int(a), ast::Value::Int(b)) => {
|
||||||
|
let cop = match cop {
|
||||||
|
ast::Cmp::Eq => |a, b| a == b,
|
||||||
|
ast::Cmp::NotEq => |a, b| a != b,
|
||||||
|
ast::Cmp::Gt => |a, b| a > b,
|
||||||
|
ast::Cmp::Gte => |a, b| a >= b,
|
||||||
|
ast::Cmp::Lt => |a, b| a < b,
|
||||||
|
ast::Cmp::Lte => |a, b| a <= b,
|
||||||
|
};
|
||||||
|
Ok(ast::Value::Boolean(cop(a, b)))
|
||||||
|
}
|
||||||
|
(ast::Value::Float(a), ast::Value::Float(b)) => {
|
||||||
|
let cop = match cop {
|
||||||
|
ast::Cmp::Eq => |a, b| a == b,
|
||||||
|
ast::Cmp::NotEq => |a, b| a != b,
|
||||||
|
ast::Cmp::Gt => |a, b| a > b,
|
||||||
|
ast::Cmp::Gte => |a, b| a >= b,
|
||||||
|
ast::Cmp::Lt => |a, b| a < b,
|
||||||
|
ast::Cmp::Lte => |a, b| a <= b,
|
||||||
|
};
|
||||||
|
Ok(ast::Value::Boolean(cop(a, b)))
|
||||||
|
}
|
||||||
|
(ast::Value::Boolean(a), ast::Value::Boolean(b)) => {
|
||||||
|
let cop = match cop {
|
||||||
|
ast::Cmp::Eq => |a, b| a == b,
|
||||||
|
ast::Cmp::NotEq => |a, b| a != b,
|
||||||
|
ast::Cmp::Gt => |a, b| a > b,
|
||||||
|
ast::Cmp::Gte => |a, b| a >= b,
|
||||||
|
ast::Cmp::Lt => |a, b| a < b,
|
||||||
|
ast::Cmp::Lte => |a, b| a <= b,
|
||||||
|
};
|
||||||
|
Ok(ast::Value::Boolean(cop(a, b)))
|
||||||
|
}
|
||||||
|
(ast::Value::String(a), ast::Value::String(b)) => {
|
||||||
|
let cop = match cop {
|
||||||
|
ast::Cmp::Eq => |a, b| a == b,
|
||||||
|
ast::Cmp::NotEq => |a, b| a != b,
|
||||||
|
ast::Cmp::Gt => |a, b| a > b,
|
||||||
|
ast::Cmp::Gte => |a, b| a >= b,
|
||||||
|
ast::Cmp::Lt => |a, b| a < b,
|
||||||
|
ast::Cmp::Lte => |a, b| a <= b,
|
||||||
|
};
|
||||||
|
Ok(ast::Value::Boolean(cop(a, b)))
|
||||||
|
}
|
||||||
|
(lhs, rhs) => Err(Error::OpError(lhs, op.clone(), rhs, Backtrace::capture())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast::Op::Bool(bop) => {
|
||||||
|
let lhs = eval_expr(expr_env, state, lhs)?;
|
||||||
|
let rhs = eval_expr(expr_env, state, rhs)?;
|
||||||
|
match (lhs, rhs) {
|
||||||
|
(ast::Value::Boolean(a), ast::Value::Boolean(b)) => {
|
||||||
|
let bop = match bop {
|
||||||
|
ast::BoolOp::And => |a, b| a && b,
|
||||||
|
ast::BoolOp::Or => |a, b| a || b,
|
||||||
|
};
|
||||||
|
Ok(ast::Value::Boolean(bop(a, b)))
|
||||||
|
}
|
||||||
|
(lhs, rhs) => Err(Error::OpError(lhs, op.clone(), rhs, Backtrace::capture())),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
},
|
},
|
||||||
ast::Expr::Record(record) => {
|
ast::Expr::Record(record) => {
|
||||||
let mut map = BTreeMap::new();
|
let mut map = BTreeMap::new();
|
||||||
|
|
@ -316,6 +431,55 @@ mod tests {
|
||||||
assert_eq!(result, Ok(0.into()));
|
assert_eq!(result, Ok(0.into()));
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
|
fn main_not() {
|
||||||
|
let program = vec![helpers::define_expr(
|
||||||
|
"main",
|
||||||
|
helpers::func(vec![], ast::Expr::Not(Box::new(false.into()))),
|
||||||
|
)]
|
||||||
|
.into();
|
||||||
|
let result = run(program, "main".into(), vec![]);
|
||||||
|
assert_eq!(result, Ok(true.into()));
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn main_or() {
|
||||||
|
let program = vec![helpers::define_expr(
|
||||||
|
"main",
|
||||||
|
helpers::func(
|
||||||
|
vec![],
|
||||||
|
helpers::op(ast::Op::Bool(ast::BoolOp::Or), false.into(), true.into()),
|
||||||
|
),
|
||||||
|
)]
|
||||||
|
.into();
|
||||||
|
let result = run(program, "main".into(), vec![]);
|
||||||
|
assert_eq!(result, Ok(true.into()));
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn main_and() {
|
||||||
|
let program = vec![helpers::define_expr(
|
||||||
|
"main",
|
||||||
|
helpers::func(
|
||||||
|
vec![],
|
||||||
|
helpers::op(ast::Op::Bool(ast::BoolOp::And), false.into(), true.into()),
|
||||||
|
),
|
||||||
|
)]
|
||||||
|
.into();
|
||||||
|
let result = run(program, "main".into(), vec![]);
|
||||||
|
assert_eq!(result, Ok(false.into()));
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn main_add() {
|
||||||
|
let program = vec![helpers::define_expr(
|
||||||
|
"main",
|
||||||
|
helpers::func(
|
||||||
|
vec![],
|
||||||
|
helpers::op(ast::Op::Calc(ast::Calc::Add), 7.into(), 8.into()),
|
||||||
|
),
|
||||||
|
)]
|
||||||
|
.into();
|
||||||
|
let result = run(program, "main".into(), vec![]);
|
||||||
|
assert_eq!(result, Ok(15.into()));
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
fn var_lookup() {
|
fn var_lookup() {
|
||||||
let program = vec![
|
let program = vec![
|
||||||
helpers::define_expr("main", helpers::func(vec![], "lit".into())),
|
helpers::define_expr("main", helpers::func(vec![], "lit".into())),
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ pub enum Error {
|
||||||
NotARecord(ast::Value, Backtrace),
|
NotARecord(ast::Value, Backtrace),
|
||||||
NotAReference(ast::Value, Backtrace),
|
NotAReference(ast::Value, Backtrace),
|
||||||
NotABoolean(ast::Value, Backtrace),
|
NotABoolean(ast::Value, Backtrace),
|
||||||
|
OpError(ast::Value, ast::Op, ast::Value, Backtrace),
|
||||||
ArgumentsMismatch(Backtrace),
|
ArgumentsMismatch(Backtrace),
|
||||||
}
|
}
|
||||||
impl PartialEq for Error {
|
impl PartialEq for Error {
|
||||||
|
|
@ -60,6 +61,7 @@ impl std::fmt::Display for Error {
|
||||||
Error::NotAReference(a, b) => write!(f, "Not a reference {a:?}\n{b}"),
|
Error::NotAReference(a, b) => write!(f, "Not a reference {a:?}\n{b}"),
|
||||||
Error::NotABoolean(a, b) => write!(f, "Not a boolean {a:?}\n{b}"),
|
Error::NotABoolean(a, b) => write!(f, "Not a boolean {a:?}\n{b}"),
|
||||||
Error::ArgumentsMismatch(a) => write!(f, "Arguments mismatch\n{a}"),
|
Error::ArgumentsMismatch(a) => write!(f, "Arguments mismatch\n{a}"),
|
||||||
|
Error::OpError(a, b, c, d) => write!(f, "Op Error {a:?} {b:?} {c:?}\n{d}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,25 @@ fn parse_definitions(tokens: &mut Tokens) -> ParseResult<Vec<ast::Definition>> {
|
||||||
tokens.many(parse_definition)
|
tokens.many(parse_definition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_op(tokens: &mut Tokens) -> ParseResult<(ast::Expr, Op)> {
|
||||||
|
if let Some(LocatedToken {
|
||||||
|
token: Token::Op(op),
|
||||||
|
..
|
||||||
|
}) = tokens.next_if(&Token::Op(Op::Add))
|
||||||
|
{
|
||||||
|
if let Some(expr) = parse_expr(tokens)? {
|
||||||
|
Ok(Some((expr, op)))
|
||||||
|
} else {
|
||||||
|
Err(Error::UnexpectedTokenForParser(
|
||||||
|
"parse_op".into(),
|
||||||
|
tokens.next(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_set(tokens: &mut Tokens) -> ParseResult<ast::Expr> {
|
fn parse_set(tokens: &mut Tokens) -> ParseResult<ast::Expr> {
|
||||||
if let Some(_eq) = tokens.next_if(&Token::Equals) {
|
if let Some(_eq) = tokens.next_if(&Token::Equals) {
|
||||||
if let Some(expr) = parse_expr(tokens)? {
|
if let Some(expr) = parse_expr(tokens)? {
|
||||||
|
|
@ -97,6 +116,29 @@ fn parse_expr(tokens: &mut Tokens) -> ParseResult<ast::Expr> {
|
||||||
func: Box::new(expr1),
|
func: Box::new(expr1),
|
||||||
args,
|
args,
|
||||||
}))
|
}))
|
||||||
|
} else if let Some((expr2, op)) = parse_op(tokens)? {
|
||||||
|
let op = match op {
|
||||||
|
Op::Eq => ast::Op::Compare(ast::Cmp::Eq),
|
||||||
|
Op::NotEq => ast::Op::Compare(ast::Cmp::NotEq),
|
||||||
|
Op::Gt => ast::Op::Compare(ast::Cmp::Gt),
|
||||||
|
Op::Gte => ast::Op::Compare(ast::Cmp::Gte),
|
||||||
|
Op::Lt => ast::Op::Compare(ast::Cmp::Lt),
|
||||||
|
Op::Lte => ast::Op::Compare(ast::Cmp::Lte),
|
||||||
|
Op::Add => ast::Op::Calc(ast::Calc::Add),
|
||||||
|
Op::Sub => ast::Op::Calc(ast::Calc::Sub),
|
||||||
|
Op::Mul => ast::Op::Calc(ast::Calc::Mul),
|
||||||
|
Op::Div => ast::Op::Calc(ast::Calc::Div),
|
||||||
|
Op::Mod => ast::Op::Calc(ast::Calc::Mod),
|
||||||
|
Op::BinAnd => ast::Op::Calc(ast::Calc::BinAnd),
|
||||||
|
Op::BinOr => ast::Op::Calc(ast::Calc::BinOr),
|
||||||
|
Op::And => ast::Op::Bool(ast::BoolOp::And),
|
||||||
|
Op::Or => ast::Op::Bool(ast::BoolOp::Or),
|
||||||
|
};
|
||||||
|
Ok(Some(ast::Expr::Op {
|
||||||
|
op,
|
||||||
|
lhs: Box::new(expr1.clone()),
|
||||||
|
rhs: Box::new(expr2),
|
||||||
|
}))
|
||||||
} else {
|
} else {
|
||||||
Ok(parse_set(tokens)?
|
Ok(parse_set(tokens)?
|
||||||
.map(|expr2| ast::Expr::Op {
|
.map(|expr2| ast::Expr::Op {
|
||||||
|
|
@ -113,6 +155,13 @@ fn parse_expr(tokens: &mut Tokens) -> ParseResult<ast::Expr> {
|
||||||
|
|
||||||
fn parse_simple_expr(tokens: &mut Tokens) -> ParseResult<ast::Expr> {
|
fn parse_simple_expr(tokens: &mut Tokens) -> ParseResult<ast::Expr> {
|
||||||
tokens.one_of(vec![
|
tokens.one_of(vec![
|
||||||
|
|t| {
|
||||||
|
if let Some(_) = t.next_if(&Token::Not) {
|
||||||
|
Ok(parse_expr(t)?.map(|expr| ast::Expr::Not(Box::new(expr))))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
},
|
||||||
parse_number,
|
parse_number,
|
||||||
parse_identifier,
|
parse_identifier,
|
||||||
parse_string,
|
parse_string,
|
||||||
|
|
@ -334,6 +383,12 @@ mod tests {
|
||||||
insta::assert_debug_snapshot!(result);
|
insta::assert_debug_snapshot!(result);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
|
fn negative_number() {
|
||||||
|
let program = "-108".to_string();
|
||||||
|
let result = parse(&mut scan(program), parse_expr);
|
||||||
|
insta::assert_debug_snapshot!(result);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
fn paren_identifier() {
|
fn paren_identifier() {
|
||||||
let program = "(hello)".to_string();
|
let program = "(hello)".to_string();
|
||||||
let result = parse(&mut scan(program), parse_expr);
|
let result = parse(&mut scan(program), parse_expr);
|
||||||
|
|
|
||||||
|
|
@ -93,11 +93,135 @@ pub fn scan(source: String) -> Tokens {
|
||||||
end: scanner.cursor(),
|
end: scanner.cursor(),
|
||||||
token: Token::CloseBracket,
|
token: Token::CloseBracket,
|
||||||
}),
|
}),
|
||||||
'=' => tokens.push(LocatedToken {
|
'+' => tokens.push(LocatedToken {
|
||||||
start,
|
start,
|
||||||
end: scanner.cursor(),
|
end: scanner.cursor(),
|
||||||
token: Token::Equals,
|
token: Token::Op(Op::Add),
|
||||||
}),
|
}),
|
||||||
|
'*' => tokens.push(LocatedToken {
|
||||||
|
start,
|
||||||
|
end: scanner.cursor(),
|
||||||
|
token: Token::Op(Op::Mul),
|
||||||
|
}),
|
||||||
|
'/' => tokens.push(LocatedToken {
|
||||||
|
start,
|
||||||
|
end: scanner.cursor(),
|
||||||
|
token: Token::Op(Op::Div),
|
||||||
|
}),
|
||||||
|
'%' => tokens.push(LocatedToken {
|
||||||
|
start,
|
||||||
|
end: scanner.cursor(),
|
||||||
|
token: Token::Op(Op::Mod),
|
||||||
|
}),
|
||||||
|
'-' => {
|
||||||
|
let c = c.clone();
|
||||||
|
let is_number = scanner.peek().map(|ch| ch.is_numeric()) == Some(true);
|
||||||
|
if is_number {
|
||||||
|
tokens.push(lex_number(&mut scanner, start, &c));
|
||||||
|
} else {
|
||||||
|
tokens.push(LocatedToken {
|
||||||
|
start,
|
||||||
|
end: scanner.cursor(),
|
||||||
|
token: Token::Op(Op::Sub),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'<' => {
|
||||||
|
let token = if let Some(ch) = scanner.peek()
|
||||||
|
&& *ch == '='
|
||||||
|
{
|
||||||
|
scanner.pop();
|
||||||
|
Token::Op(Op::Lte)
|
||||||
|
} else {
|
||||||
|
Token::Op(Op::Lt)
|
||||||
|
};
|
||||||
|
|
||||||
|
tokens.push(LocatedToken {
|
||||||
|
start,
|
||||||
|
end: scanner.cursor(),
|
||||||
|
token,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
'>' => {
|
||||||
|
let token = if let Some(ch) = scanner.peek()
|
||||||
|
&& *ch == '='
|
||||||
|
{
|
||||||
|
scanner.pop();
|
||||||
|
Token::Op(Op::Gte)
|
||||||
|
} else {
|
||||||
|
Token::Op(Op::Gt)
|
||||||
|
};
|
||||||
|
|
||||||
|
tokens.push(LocatedToken {
|
||||||
|
start,
|
||||||
|
end: scanner.cursor(),
|
||||||
|
token,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
'=' => {
|
||||||
|
let token = if let Some(ch) = scanner.peek()
|
||||||
|
&& *ch == '='
|
||||||
|
{
|
||||||
|
scanner.pop();
|
||||||
|
Token::Op(Op::Eq)
|
||||||
|
} else {
|
||||||
|
Token::Equals
|
||||||
|
};
|
||||||
|
|
||||||
|
tokens.push(LocatedToken {
|
||||||
|
start,
|
||||||
|
end: scanner.cursor(),
|
||||||
|
token,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
'|' => {
|
||||||
|
let token = if let Some(ch) = scanner.peek()
|
||||||
|
&& *ch == '|'
|
||||||
|
{
|
||||||
|
scanner.pop();
|
||||||
|
Token::Op(Op::Or)
|
||||||
|
} else {
|
||||||
|
Token::Op(Op::BinOr)
|
||||||
|
};
|
||||||
|
|
||||||
|
tokens.push(LocatedToken {
|
||||||
|
start,
|
||||||
|
end: scanner.cursor(),
|
||||||
|
token,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
'&' => {
|
||||||
|
let token = if let Some(ch) = scanner.peek()
|
||||||
|
&& *ch == '&'
|
||||||
|
{
|
||||||
|
scanner.pop();
|
||||||
|
Token::Op(Op::And)
|
||||||
|
} else {
|
||||||
|
Token::Op(Op::BinAnd)
|
||||||
|
};
|
||||||
|
|
||||||
|
tokens.push(LocatedToken {
|
||||||
|
start,
|
||||||
|
end: scanner.cursor(),
|
||||||
|
token,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
'!' => {
|
||||||
|
let token = if let Some(ch) = scanner.peek()
|
||||||
|
&& *ch == '='
|
||||||
|
{
|
||||||
|
scanner.pop();
|
||||||
|
Token::Op(Op::NotEq)
|
||||||
|
} else {
|
||||||
|
Token::Not
|
||||||
|
};
|
||||||
|
|
||||||
|
tokens.push(LocatedToken {
|
||||||
|
start,
|
||||||
|
end: scanner.cursor(),
|
||||||
|
token,
|
||||||
|
})
|
||||||
|
}
|
||||||
// labels
|
// labels
|
||||||
'.' => {
|
'.' => {
|
||||||
let mut str = "".to_string();
|
let mut str = "".to_string();
|
||||||
|
|
@ -152,25 +276,8 @@ pub fn scan(source: String) -> Tokens {
|
||||||
_ if c.is_whitespace() => {}
|
_ if c.is_whitespace() => {}
|
||||||
// numbers
|
// numbers
|
||||||
_ if c.is_numeric() => {
|
_ if c.is_numeric() => {
|
||||||
let mut str = c.to_string();
|
let c = c.clone();
|
||||||
loop {
|
tokens.push(lex_number(&mut scanner, start, &c));
|
||||||
if let Some(ch) = scanner.peek()
|
|
||||||
&& !ch.is_numeric()
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if let Some(ch) = scanner.pop() {
|
|
||||||
str.push(*ch);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let i = str.parse::<u16>().unwrap();
|
|
||||||
tokens.push(LocatedToken {
|
|
||||||
start,
|
|
||||||
end: scanner.cursor(),
|
|
||||||
token: Token::Number(i),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
// identifiers and keywords
|
// identifiers and keywords
|
||||||
_ if c.is_alphabetic() || *c == '_' => {
|
_ if c.is_alphabetic() || *c == '_' => {
|
||||||
|
|
@ -216,6 +323,28 @@ pub fn scan(source: String) -> Tokens {
|
||||||
tokens.into()
|
tokens.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lex_number(scanner: &mut Scanner, start: Pos, c: &char) -> LocatedToken {
|
||||||
|
let mut str = c.to_string();
|
||||||
|
loop {
|
||||||
|
if let Some(ch) = scanner.peek()
|
||||||
|
&& !ch.is_numeric()
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if let Some(ch) = scanner.pop() {
|
||||||
|
str.push(*ch);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let i = str.parse::<i32>().unwrap();
|
||||||
|
LocatedToken {
|
||||||
|
start,
|
||||||
|
end: scanner.cursor(),
|
||||||
|
token: Token::Number(i),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
---
|
||||||
|
source: src/parser/parser.rs
|
||||||
|
expression: result
|
||||||
|
---
|
||||||
|
Ok(
|
||||||
|
Value(
|
||||||
|
Int(
|
||||||
|
-108,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
@ -22,7 +22,6 @@ pub enum Token {
|
||||||
Fn,
|
Fn,
|
||||||
Mut,
|
Mut,
|
||||||
Return,
|
Return,
|
||||||
Equals,
|
|
||||||
OpenParen,
|
OpenParen,
|
||||||
CloseParen,
|
CloseParen,
|
||||||
OpenBracket,
|
OpenBracket,
|
||||||
|
|
@ -34,12 +33,34 @@ pub enum Token {
|
||||||
Colon,
|
Colon,
|
||||||
True,
|
True,
|
||||||
False,
|
False,
|
||||||
Number(u16),
|
Equals,
|
||||||
|
Not,
|
||||||
|
Op(Op),
|
||||||
|
Number(i32),
|
||||||
String(String),
|
String(String),
|
||||||
Identifier(String),
|
Identifier(String),
|
||||||
Label(String),
|
Label(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum Op {
|
||||||
|
Eq,
|
||||||
|
NotEq,
|
||||||
|
Gt,
|
||||||
|
Lt,
|
||||||
|
Gte,
|
||||||
|
Lte,
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Div,
|
||||||
|
Mul,
|
||||||
|
Mod,
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
|
BinAnd,
|
||||||
|
BinOr,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Tokens(pub Vec<LocatedToken>);
|
pub struct Tokens(pub Vec<LocatedToken>);
|
||||||
|
|
||||||
|
|
@ -65,6 +86,7 @@ impl Tokens {
|
||||||
(Token::Number(_), Token::Number(_)) => true,
|
(Token::Number(_), Token::Number(_)) => true,
|
||||||
(Token::String(_), Token::String(_)) => true,
|
(Token::String(_), Token::String(_)) => true,
|
||||||
(Token::Label(_), Token::Label(_)) => true,
|
(Token::Label(_), Token::Label(_)) => true,
|
||||||
|
(Token::Op(_), Token::Op(_)) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue