Properly print decimal numbers
This commit is contained in:
parent
04d2c4ab74
commit
44d257494a
@ -7,7 +7,7 @@ use num_rational::BigRational;
|
||||
use super::Builtin;
|
||||
use crate::color::Color;
|
||||
use crate::units::Unit;
|
||||
use crate::value::Value;
|
||||
use crate::value::{Number, Value};
|
||||
|
||||
pub(crate) fn register(f: &mut BTreeMap<String, Builtin>) {
|
||||
decl!(f "rgb", |args, _| {
|
||||
@ -23,31 +23,31 @@ pub(crate) fn register(f: &mut BTreeMap<String, Builtin>) {
|
||||
});
|
||||
decl!(f "red", |args, _| {
|
||||
match arg!(args, 0, "red") {
|
||||
Value::Color(c) => Some(Value::Dimension(BigRational::from_integer(BigInt::from(c.red())), Unit::None)),
|
||||
Value::Color(c) => Some(Value::Dimension(Number::from(BigInt::from(c.red())), Unit::None)),
|
||||
_ => todo!("non-color given to builtin function `red()`")
|
||||
}
|
||||
});
|
||||
decl!(f "green", |args, _| {
|
||||
match arg!(args, 0, "green") {
|
||||
Value::Color(c) => Some(Value::Dimension(BigRational::from_integer(BigInt::from(c.green())), Unit::None)),
|
||||
Value::Color(c) => Some(Value::Dimension(Number::from(BigInt::from(c.green())), Unit::None)),
|
||||
_ => todo!("non-color given to builtin function `green()`")
|
||||
}
|
||||
});
|
||||
decl!(f "blue", |args, _| {
|
||||
match arg!(args, 0, "blue") {
|
||||
Value::Color(c) => Some(Value::Dimension(BigRational::from_integer(BigInt::from(c.blue())), Unit::None)),
|
||||
Value::Color(c) => Some(Value::Dimension(Number::from(BigInt::from(c.blue())), Unit::None)),
|
||||
_ => todo!("non-color given to builtin function `blue()`")
|
||||
}
|
||||
});
|
||||
decl!(f "opacity", |args, _| {
|
||||
match arg!(args, 0, "color") {
|
||||
Value::Color(c) => Some(Value::Dimension(BigRational::from_integer(BigInt::from(c.alpha())), Unit::None)),
|
||||
Value::Color(c) => Some(Value::Dimension(Number::new(BigRational::new(BigInt::from(c.alpha()), BigInt::from(255))), Unit::None)),
|
||||
_ => todo!("non-color given to builtin function `opacity()`")
|
||||
}
|
||||
});
|
||||
decl!(f "alpha", |args, _| {
|
||||
match arg!(args, 0, "color") {
|
||||
Value::Color(c) => Some(Value::Dimension(BigRational::from_integer(BigInt::from(c.alpha())), Unit::None)),
|
||||
Value::Color(c) => Some(Value::Dimension(Number::new(BigRational::new(BigInt::from(c.alpha()), BigInt::from(255))), Unit::None)),
|
||||
_ => todo!("non-color given to builtin function `alpha()`")
|
||||
}
|
||||
});
|
||||
|
@ -360,7 +360,7 @@ impl Scope {
|
||||
pub fn get_var(&self, v: &str) -> Result<&Value, String> {
|
||||
match self.vars.get(&v.replace('_', "-")) {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(format!("Undefined variable `{}`.", v))
|
||||
None => Err(format!("Undefined variable `{}`.", v)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -375,7 +375,7 @@ impl Scope {
|
||||
pub fn get_mixin(&self, v: &str) -> Result<&Mixin, String> {
|
||||
match self.mixins.get(&v.replace('_', "-")) {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(format!("Undefined mixin `{}`.", v))
|
||||
None => Err(format!("Undefined mixin `{}`.", v)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -390,7 +390,7 @@ impl Scope {
|
||||
pub fn get_fn(&self, v: &str) -> Result<&Function, String> {
|
||||
match self.functions.get(&v.replace('_', "-")) {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(format!("Undefined function `{}`.", v))
|
||||
None => Err(format!("Undefined function `{}`.", v)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,9 +51,8 @@ pub(crate) fn parse_interpolation<I: Iterator<Item = Token>>(
|
||||
TokenKind::Symbol(Symbol::OpenCurlyBrace) => {
|
||||
todo!("invalid character in interpolation")
|
||||
}
|
||||
TokenKind::Variable(ref v) => val.extend(
|
||||
Lexer::new(&scope.get_var(v).unwrap().to_string()).collect::<Vec<Token>>(),
|
||||
),
|
||||
TokenKind::Variable(ref v) => val
|
||||
.extend(Lexer::new(&scope.get_var(v).unwrap().to_string()).collect::<Vec<Token>>()),
|
||||
TokenKind::Interpolation => val.extend(parse_interpolation(tokens, scope)),
|
||||
_ => val.push(tok),
|
||||
}
|
||||
|
@ -4,12 +4,13 @@ use std::fmt::{self, Display};
|
||||
use std::iter::Iterator;
|
||||
|
||||
use num_bigint::BigInt;
|
||||
use num_rational::BigRational;
|
||||
|
||||
use crate::color::Color;
|
||||
use crate::common::{ListSeparator, Op, QuoteKind};
|
||||
use crate::units::Unit;
|
||||
pub(crate) use number::Number;
|
||||
|
||||
mod number;
|
||||
mod ops;
|
||||
mod parse;
|
||||
|
||||
@ -19,7 +20,7 @@ pub(crate) enum Value {
|
||||
True,
|
||||
False,
|
||||
Null,
|
||||
Dimension(BigRational, Unit),
|
||||
Dimension(Number, Unit),
|
||||
List(Vec<Value>, ListSeparator),
|
||||
Color(Color),
|
||||
BinaryOp(Box<Value>, Op, Box<Value>),
|
||||
@ -58,7 +59,7 @@ impl TryInto<u16> for Value {
|
||||
Self::BinaryOp(..) => self.eval().try_into(),
|
||||
Self::Dimension(n, Unit::Percent) => todo!(),
|
||||
Self::Dimension(n, Unit::None) => {
|
||||
if n >= BigRational::from_integer(BigInt::from(255)) {
|
||||
if n >= Number::from(BigInt::from(255)) {
|
||||
Ok(255)
|
||||
} else {
|
||||
Ok(n.to_integer().to_str_radix(10).parse().unwrap())
|
||||
|
70
src/value/number.rs
Normal file
70
src/value/number.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use std::convert::From;
|
||||
use std::fmt::{self, Display, Write};
|
||||
use std::ops::{Add, Sub};
|
||||
|
||||
use num_bigint::BigInt;
|
||||
use num_rational::BigRational;
|
||||
|
||||
const PRECISION: usize = 10;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub(crate) struct Number {
|
||||
val: BigRational,
|
||||
}
|
||||
|
||||
impl Number {
|
||||
pub const fn new(val: BigRational) -> Number {
|
||||
Number { val }
|
||||
}
|
||||
|
||||
pub fn to_integer(&self) -> BigInt {
|
||||
self.val.to_integer()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BigInt> for Number {
|
||||
fn from(b: BigInt) -> Self {
|
||||
Number {
|
||||
val: BigRational::from_integer(b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Number {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.val.to_integer())?;
|
||||
let mut frac = self.val.fract();
|
||||
if frac != BigRational::from_integer(BigInt::from(0)) {
|
||||
f.write_char('.')?;
|
||||
for _ in 0..PRECISION {
|
||||
frac *= BigRational::from_integer(BigInt::from(10));
|
||||
write!(f, "{}", frac.to_integer())?;
|
||||
frac = frac.fract();
|
||||
if frac == BigRational::from_integer(BigInt::from(0)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Number {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: Self) -> Self {
|
||||
Number {
|
||||
val: self.val + other.val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Number {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, other: Self) -> Self {
|
||||
Number {
|
||||
val: self.val - other.val,
|
||||
}
|
||||
}
|
||||
}
|
@ -10,6 +10,8 @@ use crate::utils::{devour_whitespace_or_comment, parse_interpolation};
|
||||
use crate::value::Value;
|
||||
use crate::{Token, TokenKind};
|
||||
|
||||
use super::number::Number;
|
||||
|
||||
fn parse_hex(s: String) -> Value {
|
||||
match s.len() {
|
||||
3 => {
|
||||
@ -162,7 +164,7 @@ impl Value {
|
||||
Unit::None
|
||||
};
|
||||
Some(Value::Dimension(
|
||||
val.parse().expect("error parsing integer"),
|
||||
Number::new(val.parse().expect("error parsing integer")),
|
||||
unit,
|
||||
))
|
||||
}
|
||||
|
@ -46,3 +46,8 @@ test!(
|
||||
"a {\n color: rgb(255, 255, 255);\n}\n",
|
||||
"a {\n color: #ffffff;\n}\n"
|
||||
);
|
||||
test!(
|
||||
alpha_function_4_hex,
|
||||
"a {\n color: alpha(#0123);\n}\n",
|
||||
"a {\n color: .25;\n}\n"
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user