Skip to content

Commit 44f8eb5

Browse files
authored
Merge pull request #4 from testingrequired/numbers
Add support for numbers
2 parents 8b601f9 + 9fc939e commit 44f8eb5

9 files changed

Lines changed: 68 additions & 6 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ The syntax is s-expression like. There are only [builtin functions](#builtin-fun
4848
| `@key` | Reference to the client context value `key` |
4949
| `(id :a)` | Call to builtin `id` with arguments: `:a` |
5050
| `` `foo` `` | String literal |
51+
| `12345`, `456.789` | Number literal |
5152
| `true` | Literal boolean value `true` |
5253
| `false` | Literal boolean value `false` |
5354
| `String` | Literal type `String` |

src/ast.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub enum Expr {
88
Identifier(Box<ExprIdentifier>),
99
Call(Box<ExprCall>),
1010
String(Box<ExprString>),
11+
Number(Box<ExprNumber>),
1112
Error,
1213
}
1314

@@ -60,6 +61,7 @@ impl Expr {
6061
.clone(),
6162
Expr::Call(_) => Type::Unknown,
6263
Expr::String(_) => Type::String,
64+
Expr::Number(_) => Type::Number,
6365
Expr::Error => Type::Unknown,
6466
}
6567
}
@@ -157,6 +159,15 @@ impl ExprString {
157159
}
158160
}
159161

162+
#[derive(Debug, PartialEq)]
163+
pub struct ExprNumber(pub f64);
164+
165+
impl ExprNumber {
166+
pub fn new(value: f64) -> Self {
167+
Self(value)
168+
}
169+
}
170+
160171
#[derive(Debug, PartialEq)]
161172
pub struct ExprCall {
162173
pub callee: Box<ExprS>,

src/compiler.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,23 @@ fn compile_expr(
289289
codes.push(index as u8);
290290
}
291291
}
292+
Expr::Number(number) => {
293+
if let Some(index) = constants.iter().position(|x| {
294+
if let Value::Number(value) = x {
295+
value == &number.0
296+
} else {
297+
false
298+
}
299+
}) {
300+
codes.push(CONSTANT);
301+
codes.push(index as u8);
302+
} else {
303+
constants.push(Value::Number(number.0.clone()));
304+
let index = constants.len() - 1;
305+
codes.push(CONSTANT);
306+
codes.push(index as u8);
307+
}
308+
}
292309
Expr::Identifier(identifier) => {
293310
let identifier_lookup_name = identifier.lookup_name();
294311
let identifier_name = identifier.full_name().to_string();

src/errors.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! Errors
22
3+
use std::num::ParseFloatError;
4+
35
use lalrpop_util::ParseError;
46
use thiserror::Error;
57

@@ -40,13 +42,15 @@ pub enum LexicalError {
4042
#[default]
4143
#[error("Invalid token")]
4244
InvalidToken,
45+
#[error("Invalid number $0")]
46+
InvalidNumber(ParseFloatError),
4347
}
4448

4549
impl diagnostics::AsDiagnostic for LexicalError {
4650
fn as_diagnostic(&self, source: &str, span: &Span) -> ExprDiagnostic {
4751
let error_code = "lexical".to_string();
4852
match self {
49-
LexicalError::InvalidToken => ExprDiagnostic {
53+
_ => ExprDiagnostic {
5054
code: error_code,
5155
range: get_range(source, span),
5256
severity: Some(ExprDiagnosisSeverity::ERROR),
@@ -56,6 +60,12 @@ impl diagnostics::AsDiagnostic for LexicalError {
5660
}
5761
}
5862

63+
impl From<ParseFloatError> for LexicalError {
64+
fn from(value: ParseFloatError) -> Self {
65+
LexicalError::InvalidNumber(value)
66+
}
67+
}
68+
5969
#[derive(Debug, Clone, Error, PartialEq)]
6070
pub enum SyntaxError {
6171
#[error("extraneous input: {token:?}")]

src/grammar.lalrpop

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub Expr: ast::Expr = {
1111
ExprIdentifier,
1212
ExprCall,
1313
ExprString,
14+
ExprNumber,
1415
ExprBool,
1516
<e:!> => {
1617
errors.push(SyntaxError::from_parser_error(e.error, source));
@@ -25,6 +26,12 @@ ExprBool: ast::Expr = {
2526
"false" => ast::Expr::Bool(ast::ExprBool(false).into()),
2627
};
2728

29+
// Number Expressions
30+
31+
ExprNumber: ast::Expr = {
32+
number => ast::Expr::Number(ast::ExprNumber(<>).into())
33+
};
34+
2835
// String Expressions
2936

3037
ExprString: ast::Expr = {
@@ -109,6 +116,7 @@ extern {
109116
"true" => Token::True,
110117
"false" => Token::False,
111118
string => Token::String(<String>),
119+
number => Token::Number(<f64>),
112120
identifier => Token::Identifier(<String>),
113121
ty => Token::Type(<String>)
114122
}

src/lexer.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::ops::Range;
55

66
use crate::{
77
errors::{ExprErrorS, LexicalError},
8-
span::Spanned,
8+
span::{Span, Spanned},
99
};
1010

1111
/// Parse source code in to a list of [`Token`].
@@ -85,6 +85,9 @@ pub enum Token {
8585
#[regex(r#"`[^`]*`"#, lex_string)]
8686
String(String),
8787

88+
#[regex(r#"[0-9]+(\.[0-9]+)?"#, lex_number)]
89+
Number(f64),
90+
8891
#[regex("[!?:@]?[a-z_][a-zA-Z0-9_]*", lex_identifier)]
8992
Identifier(String),
9093

@@ -108,6 +111,13 @@ fn lex_string(lexer: &mut logos::Lexer<Token>) -> String {
108111
slice[1..slice.len() - 1].to_string()
109112
}
110113

114+
fn lex_number(lexer: &mut logos::Lexer<Token>) -> Result<f64, (LexicalError, Span)> {
115+
let slice = lexer.slice();
116+
slice
117+
.parse::<f64>()
118+
.map_err(|err| (err.into(), lexer.span()))
119+
}
120+
111121
impl Token {
112122
pub fn identifier(identifier: &str) -> Self {
113123
Token::Identifier(identifier.to_string())

src/types.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::{prelude::BuiltinFn, value::Value};
88
pub enum Type {
99
Value,
1010
String,
11+
Number,
1112
Fn {
1213
args: Vec<Type>,
1314
variadic_arg: Option<Box<Type>>,
@@ -71,6 +72,7 @@ impl Type {
7172
match self {
7273
Type::Value => "Value".to_string(),
7374
Type::String => "String".to_string(),
75+
Type::Number => "Number".to_string(),
7476
Type::Fn {
7577
args,
7678
variadic_arg,
@@ -118,6 +120,7 @@ impl From<Value> for Type {
118120
fn from(value: Value) -> Self {
119121
match value {
120122
Value::String(_) => Type::String,
123+
Value::Number(_) => Type::Number,
121124
Value::Fn(builtin_fn) => {
122125
let mut args: Vec<Type> =
123126
builtin_fn.args.iter().map(|arg| arg.ty.clone()).collect();

src/value.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::{
1111
#[derive(Debug, Clone, PartialEq)]
1212
pub enum Value {
1313
String(String),
14+
Number(f64),
1415
Fn(Box<BuiltinFn<'static>>),
1516
Bool(bool),
1617
Type(Box<Type>),
@@ -72,6 +73,7 @@ impl Display for Value {
7273
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
7374
match self {
7475
Value::String(string) => write!(f, "`{}`", string),
76+
Value::Number(value) => write!(f, "{}", value),
7577
Value::Fn(builtin) => write!(f, "{builtin:?}"),
7678
Value::Bool(value) => write!(f, "{}", value),
7779
Value::Type(ty) => write!(f, "Type<{}>", ty),

tests/integration_tests.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2404,7 +2404,7 @@ mod invalid {
24042404
ast should be: Err(vec![(
24052405
SyntaxError::UnrecognizedToken {
24062406
token: String::from(")"),
2407-
expected: vec![r#""(""#.to_string(), r#""Fn""#.to_string(), r#""true""#.to_string(), r#""false""#.to_string(), "string".to_string(), "identifier".to_string(), "ty".to_string()]
2407+
expected: vec![r#""(""#.to_string(), r#""Fn""#.to_string(), r#""true""#.to_string(), r#""false""#.to_string(), "string".to_string(), "number".to_string(), "identifier".to_string(), "ty".to_string()]
24082408
}.into(),
24092409
1..2
24102410
)]);
@@ -2416,7 +2416,7 @@ mod invalid {
24162416
compiles to: Err(vec![(
24172417
SyntaxError::UnrecognizedToken {
24182418
token: String::from(")"),
2419-
expected: vec![r#""(""#.to_string(), r#""Fn""#.to_string(), r#""true""#.to_string(), r#""false""#.to_string(), "string".to_string(), "identifier".to_string(), "ty".to_string()]
2419+
expected: vec![r#""(""#.to_string(), r#""Fn""#.to_string(), r#""true""#.to_string(), r#""false""#.to_string(), "string".to_string(), "number".to_string(), "identifier".to_string(), "ty".to_string()]
24202420
}.into(),
24212421
1..2
24222422
)]);
@@ -2430,7 +2430,7 @@ mod invalid {
24302430
interpets to: Err(vec![(
24312431
SyntaxError::UnrecognizedToken {
24322432
token: String::from(")"),
2433-
expected: vec![r#""(""#.to_string(), r#""Fn""#.to_string(), r#""true""#.to_string(), r#""false""#.to_string(), "string".to_string(), "identifier".to_string(), "ty".to_string()]
2433+
expected: vec![r#""(""#.to_string(), r#""Fn""#.to_string(), r#""true""#.to_string(), r#""false""#.to_string(), "string".to_string(), "number".to_string(), "identifier".to_string(), "ty".to_string()]
24342434
}.into(),
24352435
1..2
24362436
)]);
@@ -2693,7 +2693,7 @@ mod invalid {
26932693

26942694
interpets to: Err(vec![(
26952695
SyntaxError::UnrecognizedEOF {
2696-
expected: vec![r#""(""#.to_string(), r#"")""#.to_string(), r#""Fn""#.to_string(), r#""true""#.to_string(), r#""false""#.to_string(), "string".to_string(), "identifier".to_string(), "ty".to_string()]
2696+
expected: vec![r#""(""#.to_string(), r#"")""#.to_string(), r#""Fn""#.to_string(), r#""true""#.to_string(), r#""false""#.to_string(), "string".to_string(), "number".to_string(), "identifier".to_string(), "ty".to_string()]
26972697
}.into(),
26982698
19..19
26992699
)]);

0 commit comments

Comments
 (0)