use crate::{
compilation::{
Lexer, Parser,
error::CompilationError,
token::{Token, TokenKind},
},
execution::{Chunk, op_code::OpCode},
scripting_value::ScriptingValue,
};
use super::PrefixParselet;
#[derive(Clone)]
pub struct ValueParselet;
impl PrefixParselet for ValueParselet {
fn parse(
&self,
lexer: &mut Lexer,
parser: &mut Parser,
chunk: &mut Chunk,
token: Token,
) -> Result<(), CompilationError> {
match token.kind {
TokenKind::Enum => {
let Some(value) = lexer.enums().get(&token.origin) else {
return Err(CompilationError::EnumValNotFound {
value: token.origin.into(),
pos: token.line,
});
};
let offset = chunk.add_constant(ScriptingValue::Int64(i64::from(*value)))?;
parser.emit_bytes(OpCode::Constant as u8, offset, chunk);
Ok(())
}
TokenKind::FloatNumber => {
let double: f64 = match token.origin.parse() {
Ok(n) => n,
Err(_) => {
return Err(CompilationError::ParseNumber {
token: token.origin.into(),
pos: token.line,
});
}
};
let offset = chunk.add_constant(ScriptingValue::Float64(double))?;
parser.emit_bytes(OpCode::Constant as u8, offset, chunk);
Ok(())
}
TokenKind::HexNumber => {
let literal = token.origin.trim_start_matches("0x");
let Ok(value) = i64::from_str_radix(literal, 16) else {
return Err(CompilationError::ParseHex {
token: literal.into(),
pos: token.line,
});
};
let offset = chunk.add_constant(ScriptingValue::Int64(value))?;
parser.emit_bytes(OpCode::Constant as u8, offset, chunk);
Ok(())
}
TokenKind::IntNumber => {
let Ok(value) = token.origin.parse::<i64>() else {
return Err(CompilationError::ParseInt {
token: token.origin.into(),
pos: token.line,
});
};
let offset = chunk.add_constant(ScriptingValue::Int64(value))?;
parser.emit_bytes(OpCode::Constant as u8, offset, chunk);
Ok(())
}
TokenKind::String => {
let offset = chunk.add_constant(ScriptingValue::String(token.origin))?;
parser.emit_bytes(OpCode::Constant as u8, offset, chunk);
Ok(())
}
_ => Err(CompilationError::Unreachable {
file: file!().into(),
line: line!(),
}),
}
}
}
#[cfg(test)]
mod tests {
use alloc::{
collections::btree_map::BTreeMap,
string::{String, ToString},
};
use super::*;
const fn is_normal<T: Sized + Send + Sync>() {}
fn token(kind: TokenKind, origin: &str) -> Token {
Token {
origin: origin.to_string(),
offset: 0,
line: 1,
kind,
}
}
#[test]
const fn normal_types() {
is_normal::<&ValueParselet>();
is_normal::<ValueParselet>();
}
#[test]
fn parse_enum_missing_value_errors() {
let enums = BTreeMap::<String, i8>::new();
let mut lexer = Lexer::new(&enums, "");
let mut parser = Parser::new();
let mut chunk = Chunk::default();
let result = ValueParselet.parse(&mut lexer, &mut parser, &mut chunk, token(TokenKind::Enum, "MISSING"));
assert!(matches!(result, Err(CompilationError::EnumValNotFound { .. })));
}
#[test]
fn parse_float_unparseable_origin_errors() {
let enums = BTreeMap::<String, i8>::new();
let mut lexer = Lexer::new(&enums, "");
let mut parser = Parser::new();
let mut chunk = Chunk::default();
let result = ValueParselet.parse(&mut lexer, &mut parser, &mut chunk, token(TokenKind::FloatNumber, "not-a-float"));
assert!(matches!(result, Err(CompilationError::ParseNumber { .. })));
}
#[test]
fn parse_rejects_non_value_token() {
let enums = BTreeMap::<String, i8>::new();
let mut lexer = Lexer::new(&enums, "");
let mut parser = Parser::new();
let mut chunk = Chunk::default();
let result = ValueParselet.parse(&mut lexer, &mut parser, &mut chunk, Token::none());
assert!(matches!(result, Err(CompilationError::Unreachable { .. })));
}
}