3 Commits
0.3.2 ... 0.3.3

9 changed files with 222 additions and 98 deletions

View File

@@ -1,5 +1,10 @@
# Changelog # Changelog
[0.3.3]
- Fixed bug where negative temperature literals were converted to Kelvin
first before applying the negative
[0.3.2] [0.3.2]
- Fixed stack overflow due to incorrect optimization of 'leaf' functions - Fixed stack overflow due to incorrect optimization of 'leaf' functions

View File

@@ -2,7 +2,7 @@
<ModMetadata xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <ModMetadata xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Name>Slang</Name> <Name>Slang</Name>
<Author>JoeDiertay</Author> <Author>JoeDiertay</Author>
<Version>0.3.2</Version> <Version>0.3.3</Version>
<Description> <Description>
[h1]Slang: High-Level Programming for Stationeers[/h1] [h1]Slang: High-Level Programming for Stationeers[/h1]

View File

@@ -41,7 +41,7 @@ namespace Slang
{ {
public const string PluginGuid = "com.biddydev.slang"; public const string PluginGuid = "com.biddydev.slang";
public const string PluginName = "Slang"; public const string PluginName = "Slang";
public const string PluginVersion = "0.3.2"; public const string PluginVersion = "0.3.3";
public static Mod MOD = new Mod(PluginName, PluginVersion); public static Mod MOD = new Mod(PluginName, PluginVersion);

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "slang" name = "slang"
version = "0.3.2" version = "0.3.3"
edition = "2021" edition = "2021"
[workspace] [workspace]

View File

@@ -175,3 +175,52 @@ fn test_ternary_expression_assignment() -> Result<()> {
Ok(()) Ok(())
} }
#[test]
fn test_negative_literals() -> Result<()> {
let compiled = compile!(
debug
r#"
let item = -10c - 20c;
"#
);
assert_eq!(
compiled,
indoc! {
"
j main
main:
move r8 243.15
"
}
);
Ok(())
}
#[test]
fn test_mismatched_temperature_literals() -> Result<()> {
let compiled = compile!(
debug
r#"
let item = -10c - 100k;
let item2 = item + 500c;
"#
);
assert_eq!(
compiled,
indoc! {
"
j main
main:
move r8 163.15
add r1 r8 773.15
move r9 r1
"
}
);
Ok(())
}

View File

@@ -15,7 +15,7 @@ use parser::{
use rust_decimal::Decimal; use rust_decimal::Decimal;
use std::{borrow::Cow, collections::HashMap}; use std::{borrow::Cow, collections::HashMap};
use thiserror::Error; use thiserror::Error;
use tokenizer::token::Number; use tokenizer::token::{Number, Unit};
fn extract_literal<'a>( fn extract_literal<'a>(
literal: Literal<'a>, literal: Literal<'a>,
@@ -811,7 +811,7 @@ impl<'a> Compiler<'a> {
.. ..
})), })),
.. ..
}) => Literal::Number(Number::Integer(crc_hash_signed(&str_to_hash))), }) => Literal::Number(Number::Integer(crc_hash_signed(&str_to_hash), Unit::None)),
LiteralOr::Or(Spanned { span, .. }) => { LiteralOr::Or(Spanned { span, .. }) => {
return Err(Error::Unknown( return Err(Error::Unknown(
"hash only supports string literals in this context.".into(), "hash only supports string literals in this context.".into(),
@@ -2022,6 +2022,7 @@ impl<'a> Compiler<'a> {
let loc = VariableLocation::Constant(Literal::Number(Number::Integer( let loc = VariableLocation::Constant(Literal::Number(Number::Integer(
crc_hash_signed(&str_lit), crc_hash_signed(&str_lit),
Unit::None,
))); )));
Ok(Some(CompileLocation { Ok(Some(CompileLocation {

View File

@@ -1,6 +1,6 @@
use tokenizer::Tokenizer;
use crate::Parser; use crate::Parser;
use pretty_assertions::assert_eq;
use tokenizer::Tokenizer;
#[test] #[test]
fn test_block() -> anyhow::Result<()> { fn test_block() -> anyhow::Result<()> {

View File

@@ -54,10 +54,7 @@ fn test_const_declaration() -> Result<()> {
let tokenizer = Tokenizer::from(input); let tokenizer = Tokenizer::from(input);
let mut parser = Parser::new(tokenizer); let mut parser = Parser::new(tokenizer);
assert_eq!( assert_eq!("(const item = 20c)", parser.parse()?.unwrap().to_string());
"(const item = 293.15)",
parser.parse()?.unwrap().to_string()
);
assert_eq!( assert_eq!(
"(const decimal = 200.15)", "(const decimal = 200.15)",

View File

@@ -102,48 +102,6 @@ impl<'a> Token<'a> {
} }
} }
#[derive(Debug, PartialEq, Hash, Eq, Clone)]
pub enum Temperature {
Celsius(Number),
Fahrenheit(Number),
Kelvin(Number),
}
impl std::fmt::Display for Temperature {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Temperature::Celsius(n) => write!(f, "{}°C", n),
Temperature::Fahrenheit(n) => write!(f, "{}°F", n),
Temperature::Kelvin(n) => write!(f, "{}°K", n),
}
}
}
impl Temperature {
pub fn to_kelvin(self) -> Number {
match self {
Temperature::Celsius(n) => {
let n = match n {
Number::Integer(i) => Decimal::new(i as i64, 0),
Number::Decimal(d) => d,
};
Number::Decimal(n + Decimal::new(27315, 2))
}
Temperature::Fahrenheit(n) => {
let n = match n {
Number::Integer(i) => Decimal::new(i as i64, 0),
Number::Decimal(d) => d,
};
let a = n - Decimal::new(32, 0);
let b = Decimal::new(5, 0) / Decimal::new(9, 0);
Number::Decimal(a * b + Decimal::new(27315, 2))
}
Temperature::Kelvin(n) => n,
}
}
}
macro_rules! symbol { macro_rules! symbol {
($var:ident) => { ($var:ident) => {
|_| Symbol::$var |_| Symbol::$var
@@ -280,30 +238,27 @@ fn parse_number<'a>(lexer: &mut Lexer<'a, TokenType<'a>>) -> Result<Number, LexE
span.end -= lexer.extras.line_start_index; span.end -= lexer.extras.line_start_index;
span.start -= lexer.extras.line_start_index; span.start -= lexer.extras.line_start_index;
let num = if clean_str.contains('.') { let unit = match suffix {
Number::Decimal( Some('c') => Unit::Celsius,
Some('f') => Unit::Fahrenheit,
Some('k') => Unit::Kelvin,
_ => Unit::None,
};
if clean_str.contains('.') {
Ok(Number::Decimal(
clean_str clean_str
.parse::<Decimal>() .parse::<Decimal>()
.map_err(|_| LexError::NumberParse(line, span, slice.to_string()))?, .map_err(|_| LexError::NumberParse(line, span, slice.to_string()))?,
) unit,
))
} else { } else {
Number::Integer( Ok(Number::Integer(
clean_str clean_str
.parse::<i128>() .parse::<i128>()
.map_err(|_| LexError::NumberParse(line, span, slice.to_string()))?, .map_err(|_| LexError::NumberParse(line, span, slice.to_string()))?,
) unit,
}; ))
if let Some(suffix) = suffix {
Ok(match suffix {
'c' => Temperature::Celsius(num),
'f' => Temperature::Fahrenheit(num),
'k' => Temperature::Kelvin(num),
_ => unreachable!(),
}
.to_kelvin())
} else {
Ok(num)
} }
} }
@@ -395,25 +350,55 @@ impl<'a> std::fmt::Display for TokenType<'a> {
} }
} }
#[derive(Debug, PartialEq, Hash, Eq, Clone, Copy)]
pub enum Unit {
None,
Celsius,
Fahrenheit,
Kelvin,
}
#[derive(Debug, PartialEq, Hash, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Hash, Eq, Clone, Copy)]
pub enum Number { pub enum Number {
/// Represents an integer number /// Represents an integer number
Integer(i128), Integer(i128, Unit),
/// Represents a decimal type number with a precision of 64 bits /// Represents a decimal type number with a precision of 64 bits
Decimal(Decimal), Decimal(Decimal, Unit),
}
impl Number {
pub fn unit(&self) -> Unit {
match self {
Number::Integer(_, u) => *u,
Number::Decimal(_, u) => *u,
}
}
pub fn has_unit(&self) -> bool {
self.unit() != Unit::None
}
} }
impl From<bool> for Number { impl From<bool> for Number {
fn from(value: bool) -> Self { fn from(value: bool) -> Self {
Self::Integer(if value { 1 } else { 0 }) Self::Integer(if value { 1 } else { 0 }, Unit::None)
} }
} }
impl From<Number> for Decimal { impl From<Number> for Decimal {
fn from(value: Number) -> Self { fn from(value: Number) -> Self {
match value { let (val, unit) = match value {
Number::Decimal(d) => d, Number::Decimal(d, u) => (d, u),
Number::Integer(i) => Decimal::from(i), Number::Integer(i, u) => (Decimal::from(i), u),
};
match unit {
Unit::None | Unit::Kelvin => val,
Unit::Celsius => val + Decimal::new(27315, 2),
Unit::Fahrenheit => {
(val - Decimal::new(32, 0)) * Decimal::new(5, 0) / Decimal::new(9, 0)
+ Decimal::new(27315, 2)
}
} }
} }
} }
@@ -423,22 +408,48 @@ impl std::ops::Neg for Number {
fn neg(self) -> Self::Output { fn neg(self) -> Self::Output {
match self { match self {
Self::Integer(i) => Self::Integer(-i), Self::Integer(i, u) => Self::Integer(-i, u),
Self::Decimal(d) => Self::Decimal(-d), Self::Decimal(d, u) => Self::Decimal(-d, u),
} }
} }
} }
fn determine_target_unit(lhs_unit: Unit, rhs_unit: Unit) -> Option<Unit> {
if lhs_unit == rhs_unit {
return Some(lhs_unit);
}
if lhs_unit != Unit::None && rhs_unit == Unit::None {
return Some(lhs_unit);
}
if lhs_unit == Unit::None && rhs_unit != Unit::None {
return Some(rhs_unit);
}
// Mismatched units (C + F) -> Fallback to Kelvin/None
None
}
impl std::ops::Add for Number { impl std::ops::Add for Number {
type Output = Number; type Output = Number;
fn add(self, rhs: Self) -> Self::Output { fn add(self, rhs: Self) -> Self::Output {
match (self, rhs) { // If we can determine a common target unit (e.g. C + C = C, or C + Scalar = C),
(Self::Integer(l), Self::Integer(r)) => Number::Integer(l + r), // we preserve that unit. Otherwise, we convert to Kelvin (Decimal) and return Unit::None.
(Self::Decimal(l), Self::Decimal(r)) => Number::Decimal(l + r), if let Some(target_unit) = determine_target_unit(self.unit(), rhs.unit()) {
(Self::Integer(l), Self::Decimal(r)) => Number::Decimal(Decimal::from(l) + r), return match (self, rhs) {
(Self::Decimal(l), Self::Integer(r)) => Number::Decimal(l + Decimal::from(r)), (Self::Integer(l, _), Self::Integer(r, _)) => Number::Integer(l + r, target_unit),
(Self::Decimal(l, _), Self::Decimal(r, _)) => Number::Decimal(l + r, target_unit),
(Self::Integer(l, _), Self::Decimal(r, _)) => {
Number::Decimal(Decimal::from(l) + r, target_unit)
} }
(Self::Decimal(l, _), Self::Integer(r, _)) => {
Number::Decimal(l + Decimal::from(r), target_unit)
}
};
}
let l: Decimal = self.into();
let r: Decimal = rhs.into();
Number::Decimal(l + r, Unit::None)
} }
} }
@@ -446,12 +457,22 @@ impl std::ops::Sub for Number {
type Output = Number; type Output = Number;
fn sub(self, rhs: Self) -> Self::Output { fn sub(self, rhs: Self) -> Self::Output {
match (self, rhs) { if let Some(target_unit) = determine_target_unit(self.unit(), rhs.unit()) {
(Self::Integer(l), Self::Integer(r)) => Self::Integer(l - r), return match (self, rhs) {
(Self::Decimal(l), Self::Integer(r)) => Self::Decimal(l - Decimal::from(r)), (Self::Integer(l, _), Self::Integer(r, _)) => Number::Integer(l - r, target_unit),
(Self::Integer(l), Self::Decimal(r)) => Self::Decimal(Decimal::from(l) - r), (Self::Decimal(l, _), Self::Decimal(r, _)) => Number::Decimal(l - r, target_unit),
(Self::Decimal(l), Self::Decimal(r)) => Self::Decimal(l - r), (Self::Integer(l, _), Self::Decimal(r, _)) => {
Number::Decimal(Decimal::from(l) - r, target_unit)
} }
(Self::Decimal(l, _), Self::Integer(r, _)) => {
Number::Decimal(l - Decimal::from(r), target_unit)
}
};
}
let l: Decimal = self.into();
let r: Decimal = rhs.into();
Number::Decimal(l - r, Unit::None)
} }
} }
@@ -459,12 +480,26 @@ impl std::ops::Mul for Number {
type Output = Number; type Output = Number;
fn mul(self, rhs: Self) -> Self::Output { fn mul(self, rhs: Self) -> Self::Output {
match (self, rhs) { if let Some(target_unit) = determine_target_unit(self.unit(), rhs.unit()) {
(Number::Integer(l), Number::Integer(r)) => Number::Integer(l * r), return match (self, rhs) {
(Number::Integer(l), Number::Decimal(r)) => Number::Decimal(Decimal::from(l) * r), (Number::Integer(l, _), Number::Integer(r, _)) => {
(Number::Decimal(l), Number::Integer(r)) => Number::Decimal(l * Decimal::from(r)), Number::Integer(l * r, target_unit)
(Number::Decimal(l), Number::Decimal(r)) => Number::Decimal(l * r),
} }
(Number::Integer(l, _), Number::Decimal(r, _)) => {
Number::Decimal(Decimal::from(l) * r, target_unit)
}
(Number::Decimal(l, _), Number::Integer(r, _)) => {
Number::Decimal(l * Decimal::from(r), target_unit)
}
(Number::Decimal(l, _), Number::Decimal(r, _)) => {
Number::Decimal(l * r, target_unit)
}
};
}
let l: Decimal = self.into();
let r: Decimal = rhs.into();
Number::Decimal(l * r, Unit::None)
} }
} }
@@ -472,7 +507,22 @@ impl std::ops::Div for Number {
type Output = Number; type Output = Number;
fn div(self, rhs: Self) -> Self::Output { fn div(self, rhs: Self) -> Self::Output {
Number::Decimal(Decimal::from(self) / Decimal::from(rhs)) if let Some(target_unit) = determine_target_unit(self.unit(), rhs.unit()) {
// Division always promotes to Decimal
let l_val = match self {
Self::Integer(i, _) => Decimal::from(i),
Self::Decimal(d, _) => d,
};
let r_val = match rhs {
Self::Integer(i, _) => Decimal::from(i),
Self::Decimal(d, _) => d,
};
return Number::Decimal(l_val / r_val, target_unit);
}
let l: Decimal = self.into();
let r: Decimal = rhs.into();
Number::Decimal(l / r, Unit::None)
} }
} }
@@ -480,15 +530,36 @@ impl std::ops::Rem for Number {
type Output = Number; type Output = Number;
fn rem(self, rhs: Self) -> Self::Output { fn rem(self, rhs: Self) -> Self::Output {
Number::Decimal(Decimal::from(self) % Decimal::from(rhs)) if let Some(target_unit) = determine_target_unit(self.unit(), rhs.unit()) {
let l_val = match self {
Self::Integer(i, _) => Decimal::from(i),
Self::Decimal(d, _) => d,
};
let r_val = match rhs {
Self::Integer(i, _) => Decimal::from(i),
Self::Decimal(d, _) => d,
};
return Number::Decimal(l_val % r_val, target_unit);
}
let l: Decimal = self.into();
let r: Decimal = rhs.into();
Number::Decimal(l % r, Unit::None)
} }
} }
impl std::fmt::Display for Number { impl std::fmt::Display for Number {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { let (val, unit) = match self {
Number::Integer(i) => write!(f, "{}", i), Number::Integer(i, u) => (i.to_string(), u),
Number::Decimal(d) => write!(f, "{}", d), Number::Decimal(d, u) => (d.to_string(), u),
};
match unit {
Unit::None => write!(f, "{}", val),
Unit::Celsius => write!(f, "{}c", val),
Unit::Fahrenheit => write!(f, "{}f", val),
Unit::Kelvin => write!(f, "{}k", val),
} }
} }
} }
@@ -771,3 +842,4 @@ documented! {
While, While,
} }
} }