Handle variables as parameters to function calls

This commit is contained in:
ConnorSkees 2020-01-26 09:13:39 -05:00
parent 4cbbff259c
commit 709ed5c6b5
5 changed files with 102 additions and 25 deletions

View File

@ -1,8 +1,9 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::iter::Peekable; use std::iter::Peekable;
use crate::common::Symbol; use crate::common::{Scope, Symbol};
use crate::utils::devour_whitespace; use crate::utils::devour_whitespace;
use crate::value::Value;
use crate::{Token, TokenKind}; use crate::{Token, TokenKind};
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
@ -11,7 +12,7 @@ pub(crate) struct FuncArgs(pub Vec<FuncArg>);
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) struct FuncArg { pub(crate) struct FuncArg {
pub name: String, pub name: String,
pub default: Option<Vec<Token>>, pub default: Option<Value>,
} }
impl FuncArgs { impl FuncArgs {
@ -21,7 +22,7 @@ impl FuncArgs {
} }
#[derive(Debug, Clone, std::default::Default)] #[derive(Debug, Clone, std::default::Default)]
pub(crate) struct CallArgs(pub BTreeMap<String, Vec<Token>>); pub(crate) struct CallArgs(pub BTreeMap<String, Value>);
impl CallArgs { impl CallArgs {
pub fn new() -> Self { pub fn new() -> Self {
@ -32,12 +33,15 @@ impl CallArgs {
self.0.is_empty() self.0.is_empty()
} }
pub fn get(&self, val: &str) -> Option<&Vec<Token>> { pub fn get(&self, val: &str) -> Option<&Value> {
self.0.get(val) self.0.get(val)
} }
} }
pub(crate) fn eat_func_args<I: Iterator<Item = Token>>(toks: &mut Peekable<I>) -> FuncArgs { pub(crate) fn eat_func_args<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
scope: &Scope,
) -> FuncArgs {
let mut args: Vec<FuncArg> = Vec::new(); let mut args: Vec<FuncArg> = Vec::new();
devour_whitespace(toks); devour_whitespace(toks);
@ -62,14 +66,20 @@ pub(crate) fn eat_func_args<I: Iterator<Item = Token>>(toks: &mut Peekable<I>) -
toks.next(); toks.next();
args.push(FuncArg { args.push(FuncArg {
name, name,
default: Some(default), default: Some(
Value::from_tokens(&mut default.into_iter().peekable(), scope)
.unwrap(),
),
}); });
break; break;
} }
TokenKind::Symbol(Symbol::CloseParen) => { TokenKind::Symbol(Symbol::CloseParen) => {
args.push(FuncArg { args.push(FuncArg {
name, name,
default: Some(default), default: Some(
Value::from_tokens(&mut default.into_iter().peekable(), scope)
.unwrap(),
),
}); });
break; break;
} }
@ -87,7 +97,9 @@ pub(crate) fn eat_func_args<I: Iterator<Item = Token>>(toks: &mut Peekable<I>) -
default: if default.is_empty() { default: if default.is_empty() {
None None
} else { } else {
Some(default) Some(
Value::from_tokens(&mut default.into_iter().peekable(), scope).unwrap(),
)
}, },
}); });
break; break;
@ -112,21 +124,70 @@ pub(crate) fn eat_func_args<I: Iterator<Item = Token>>(toks: &mut Peekable<I>) -
FuncArgs(args) FuncArgs(args)
} }
pub(crate) fn eat_call_args<I: Iterator<Item = Token>>(toks: &mut Peekable<I>) -> CallArgs { pub(crate) fn eat_call_args<I: Iterator<Item = Token>>(
let mut args: BTreeMap<String, Vec<Token>> = BTreeMap::new(); toks: &mut Peekable<I>,
scope: &Scope,
) -> CallArgs {
let mut args: BTreeMap<String, Value> = BTreeMap::new();
devour_whitespace(toks); devour_whitespace(toks);
let mut name: Option<String> = None; let mut name: Option<String> = None;
let mut val = Vec::new(); let mut val = Vec::new();
while let Some(Token { kind, pos }) = toks.next() { while let Some(Token { kind, pos }) = toks.next() {
match kind { match kind {
TokenKind::Variable(v) => name = Some(v), TokenKind::Variable(v) => {
devour_whitespace(toks);
match toks.peek() {
Some(Token {
kind: TokenKind::Symbol(Symbol::Colon),
..
}) => name = Some(v),
Some(Token {
kind: TokenKind::Symbol(Symbol::Comma),
..
}) => {
toks.next();
match name {
Some(ref name) => {
args.insert(name.clone(), scope.vars.get(&v).unwrap().clone())
}
None => args.insert(
format!("{}", args.len()),
scope.vars.get(&v).unwrap().clone(),
),
};
if let Some(ref mut s) = name {
s.clear();
}
val.clear();
}
Some(Token {
kind: TokenKind::Symbol(Symbol::CloseParen),
..
}) => {
toks.next();
match name {
Some(name) => args.insert(name, scope.vars.get(&v).unwrap().clone()),
None => args.insert(
format!("{}", args.len()),
scope.vars.get(&v).unwrap().clone(),
),
};
break;
}
_ => todo!("unexpected token after variable in call args"),
}
}
TokenKind::Symbol(Symbol::Colon) => { TokenKind::Symbol(Symbol::Colon) => {
devour_whitespace(toks); devour_whitespace(toks);
while let Some(tok) = toks.peek() { while let Some(tok) = toks.peek() {
match &tok.kind { match &tok.kind {
TokenKind::Symbol(Symbol::Comma) => { TokenKind::Symbol(Symbol::Comma) => {
toks.next(); toks.next();
args.insert(name.clone().unwrap(), val.clone()); args.insert(
name.clone().unwrap(),
Value::from_tokens(&mut val.clone().into_iter().peekable(), scope)
.unwrap(),
);
if let Some(ref mut s) = name { if let Some(ref mut s) = name {
s.clear(); s.clear();
} }
@ -134,7 +195,11 @@ pub(crate) fn eat_call_args<I: Iterator<Item = Token>>(toks: &mut Peekable<I>) -
break; break;
} }
TokenKind::Symbol(Symbol::CloseParen) => { TokenKind::Symbol(Symbol::CloseParen) => {
args.insert(name.clone().unwrap(), val.clone()); args.insert(
name.clone().unwrap(),
Value::from_tokens(&mut val.clone().into_iter().peekable(), scope)
.unwrap(),
);
break; break;
} }
_ => val.push(toks.next().expect("we know this exists!")), _ => val.push(toks.next().expect("we know this exists!")),
@ -143,15 +208,27 @@ pub(crate) fn eat_call_args<I: Iterator<Item = Token>>(toks: &mut Peekable<I>) -
} }
TokenKind::Symbol(Symbol::CloseParen) => { TokenKind::Symbol(Symbol::CloseParen) => {
match name { match name {
Some(name) => args.insert(name, val), Some(name) => args.insert(
None => args.insert(format!("{}", args.len()), val), name,
Value::from_tokens(&mut val.into_iter().peekable(), scope).unwrap(),
),
None => args.insert(
format!("{}", args.len()),
Value::from_tokens(&mut val.into_iter().peekable(), scope).unwrap(),
),
}; };
break; break;
} }
TokenKind::Symbol(Symbol::Comma) => { TokenKind::Symbol(Symbol::Comma) => {
match name { match name {
Some(ref name) => args.insert(name.clone(), val.clone()), Some(ref name) => args.insert(
None => args.insert(format!("{}", args.len()), val.clone()), name.clone(),
Value::from_tokens(&mut val.clone().into_iter().peekable(), scope).unwrap(),
),
None => args.insert(
format!("{}", args.len()),
Value::from_tokens(&mut val.clone().into_iter().peekable(), scope).unwrap(),
),
}; };
if let Some(ref mut s) = name { if let Some(ref mut s) = name {
s.clear(); s.clear();

View File

@ -1,11 +1,10 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::default::Default;
use std::fmt::{self, Display}; use std::fmt::{self, Display};
use crate::function::Function; use crate::function::Function;
use crate::mixin::Mixin; use crate::mixin::Mixin;
use crate::Token; use crate::value::Value;
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Symbol { pub enum Symbol {
@ -337,9 +336,9 @@ impl Display for Pos {
} }
} }
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone)]
pub(crate) struct Scope { pub(crate) struct Scope {
pub vars: HashMap<String, Vec<Token>>, pub vars: HashMap<String, Value>,
pub mixins: HashMap<String, Mixin>, pub mixins: HashMap<String, Mixin>,
pub functions: HashMap<String, Function>, pub functions: HashMap<String, Function>,
} }

View File

@ -42,7 +42,7 @@ impl Function {
Some(Token { Some(Token {
kind: TokenKind::Symbol(Symbol::OpenParen), kind: TokenKind::Symbol(Symbol::OpenParen),
.. ..
}) => eat_func_args(toks), }) => eat_func_args(toks, scope),
_ => return Err((pos, String::from("expected `(` after function declaration"))), _ => return Err((pos, String::from("expected `(` after function declaration"))),
}; };

View File

@ -59,6 +59,7 @@ use crate::mixin::{eat_include, Mixin};
use crate::selector::{Attribute, Selector}; use crate::selector::{Attribute, Selector};
use crate::style::Style; use crate::style::Style;
use crate::utils::{devour_whitespace, eat_variable_value, IsComment, IsWhitespace}; use crate::utils::{devour_whitespace, eat_variable_value, IsComment, IsWhitespace};
use crate::value::Value;
mod args; mod args;
mod atrule; mod atrule;
@ -200,7 +201,7 @@ enum Expr {
/// A full selector `a > h1` /// A full selector `a > h1`
Selector(Selector), Selector(Selector),
/// A variable declaration `$var: 1px` /// A variable declaration `$var: 1px`
VariableDecl(String, Vec<Token>), VariableDecl(String, Value),
/// A mixin declaration `@mixin foo {}` /// A mixin declaration `@mixin foo {}`
MixinDecl(String, Mixin), MixinDecl(String, Mixin),
FunctionDecl(String, Function), FunctionDecl(String, Function),

View File

@ -42,7 +42,7 @@ impl Mixin {
Some(Token { Some(Token {
kind: TokenKind::Symbol(Symbol::OpenParen), kind: TokenKind::Symbol(Symbol::OpenParen),
.. ..
}) => eat_func_args(toks), }) => eat_func_args(toks, scope),
Some(Token { Some(Token {
kind: TokenKind::Symbol(Symbol::OpenCurlyBrace), kind: TokenKind::Symbol(Symbol::OpenCurlyBrace),
.. ..
@ -137,7 +137,7 @@ pub(crate) fn eat_include<I: Iterator<Item = Token>>(
let args = if let Some(tok) = toks.next() { let args = if let Some(tok) = toks.next() {
match tok.kind { match tok.kind {
TokenKind::Symbol(Symbol::SemiColon) => CallArgs::new(), TokenKind::Symbol(Symbol::SemiColon) => CallArgs::new(),
TokenKind::Symbol(Symbol::OpenParen) => eat_call_args(toks), TokenKind::Symbol(Symbol::OpenParen) => eat_call_args(toks, scope),
_ => return Err((pos, String::from("expected `(` or `;`"))), _ => return Err((pos, String::from("expected `(` or `;`"))),
} }
} else { } else {