support hexadecimals

This commit is contained in:
Shautvast 2025-11-03 19:47:38 +01:00
parent b95097834f
commit 42c431d5c7
5 changed files with 110 additions and 32 deletions

View file

@ -1,16 +1,19 @@
use crate::errors::CompilerError::{self, Expected, IncompatibleTypes, ParseError, TooManyParameters, TypeError, UnexpectedIndent, UninitializedVariable}; use crate::errors::CompilerError::{
self, Expected, IncompatibleTypes, ParseError, TooManyParameters, TypeError, UnexpectedIndent,
UninitializedVariable,
};
use crate::errors::CompilerErrorAtLine;
use crate::tokens::TokenType::{ use crate::tokens::TokenType::{
Bang, Bool, Char, Colon, Date, Eof, Eol, Equal, False, FloatingPoint, Fn, Greater, GreaterEqual, GreaterGreater, Bang, Bool, Char, Colon, Date, Eof, Eol, Equal, F32, F64, False, FloatingPoint, Fn, Greater,
Identifier, Indent, Integer, LeftBrace, LeftBracket, LeftParen, Less, LessEqual, LessLess, GreaterEqual, GreaterGreater, I32, I64, Identifier, Indent, Integer, LeftBrace, LeftBracket,
Let, ListType, MapType, Minus, Object, Plus, Print, RightBrace, RightBracket, RightParen, SignedInteger, LeftParen, Less, LessEqual, LessLess, Let, ListType, MapType, Minus, Object, Plus, Print,
SingleRightArrow, Slash, Star, StringType, True, UnsignedInteger, F32, F64, RightBrace, RightBracket, RightParen, SignedInteger, SingleRightArrow, Slash, Star, StringType,
I32, I64, U32, U64, True, U32, U64, UnsignedInteger,
}; };
use crate::tokens::{Token, TokenType}; use crate::tokens::{Token, TokenType};
use crate::value::Value; use crate::value::Value;
use log::debug; use log::debug;
use std::collections::HashMap; use std::collections::HashMap;
use crate::errors::CompilerErrorAtLine;
pub fn compile(tokens: Vec<Token>) -> Result<Vec<Statement>, CompilerErrorAtLine> { pub fn compile(tokens: Vec<Token>) -> Result<Vec<Statement>, CompilerErrorAtLine> {
let mut compiler = AstCompiler::new(tokens); let mut compiler = AstCompiler::new(tokens);
@ -56,8 +59,6 @@ impl AstCompiler {
self.compile() self.compile()
} }
fn compile(&mut self) -> Result<Vec<Statement>, CompilerErrorAtLine> { fn compile(&mut self) -> Result<Vec<Statement>, CompilerErrorAtLine> {
self.current_line(); self.current_line();
if !self.had_error { if !self.had_error {
@ -146,9 +147,7 @@ impl AstCompiler {
indent_on_line += 1; indent_on_line += 1;
} }
if indent_on_line > expected_indent { if indent_on_line > expected_indent {
Err(self.raise(UnexpectedIndent( Err(self.raise(UnexpectedIndent(indent_on_line, expected_indent)))
indent_on_line, expected_indent
)))
} else if indent_on_line < expected_indent { } else if indent_on_line < expected_indent {
self.indent.pop(); self.indent.pop();
return Ok(None); return Ok(None);
@ -237,7 +236,9 @@ impl AstCompiler {
fn let_declaration(&mut self) -> Result<Statement, CompilerErrorAtLine> { fn let_declaration(&mut self) -> Result<Statement, CompilerErrorAtLine> {
if self.peek().token_type.is_type() { if self.peek().token_type.is_type() {
return Err(self.raise(CompilerError::KeywordNotAllowedAsIdentifier(self.peek().token_type))) return Err(self.raise(CompilerError::KeywordNotAllowedAsIdentifier(
self.peek().token_type,
)));
} }
let name_token = self.consume(Identifier, Expected("variable name."))?; let name_token = self.consume(Identifier, Expected("variable name."))?;
@ -403,13 +404,41 @@ impl AstCompiler {
Expression::Literal { Expression::Literal {
line: self.peek().line, line: self.peek().line,
literaltype: Integer, literaltype: Integer,
value: Value::I64(self.previous().lexeme.parse().map_err(|e|self.raise(ParseError(format!("{:?}",e))))?), value: Value::I64(
self.previous()
.lexeme
.parse()
.map_err(|e| self.raise(ParseError(format!("{:?}", e))))?,
),
}
} else if self.match_token(vec![U32]) {
Expression::Literal {
line: self.peek().line,
literaltype: Integer,
value: Value::U32(
u32::from_str_radix(&self.previous().lexeme.trim_start_matches("0x"), 16)
.map_err(|e| self.raise(ParseError(format!("{:?}", e))))?,
),
}
} else if self.match_token(vec![U64]) {
Expression::Literal {
line: self.peek().line,
literaltype: Integer,
value: Value::U64(
u64::from_str_radix(&self.previous().lexeme.trim_start_matches("0x"), 16)
.map_err(|e| self.raise(ParseError(format!("{:?}", e))))?,
),
} }
} else if self.match_token(vec![FloatingPoint]) { } else if self.match_token(vec![FloatingPoint]) {
Expression::Literal { Expression::Literal {
line: self.peek().line, line: self.peek().line,
literaltype: FloatingPoint, literaltype: FloatingPoint,
value: Value::F64(self.previous().lexeme.parse().map_err(|e|self.raise(ParseError(format!("{:?}",e))))?), value: Value::F64(
self.previous()
.lexeme
.parse()
.map_err(|e| self.raise(ParseError(format!("{:?}", e))))?,
),
} }
} else if self.match_token(vec![StringType]) { } else if self.match_token(vec![StringType]) {
Expression::Literal { Expression::Literal {
@ -514,7 +543,7 @@ impl AstCompiler {
if arg_type != function.parameters[arguments.len()].var_type { if arg_type != function.parameters[arguments.len()].var_type {
return Err(self.raise(IncompatibleTypes( return Err(self.raise(IncompatibleTypes(
function.parameters[arguments.len()].var_type, function.parameters[arguments.len()].var_type,
arg_type arg_type,
))); )));
} }
arguments.push(arg); arguments.push(arg);
@ -534,7 +563,11 @@ impl AstCompiler {
}) })
} }
fn consume(&mut self, token_type: TokenType, message: CompilerError) -> Result<Token, CompilerErrorAtLine> { fn consume(
&mut self,
token_type: TokenType,
message: CompilerError,
) -> Result<Token, CompilerErrorAtLine> {
if self.check(token_type) { if self.check(token_type) {
self.advance(); self.advance();
} else { } else {
@ -610,10 +643,7 @@ fn calculate_type(
(U64, I32) => U64, (U64, I32) => U64,
(StringType, _) => StringType, // meh, this all needs rigorous testing. Update: this is in progress (StringType, _) => StringType, // meh, this all needs rigorous testing. Update: this is in progress
_ => { _ => {
return Err(IncompatibleTypes( return Err(IncompatibleTypes(declared_type, inferred_type));
declared_type,
inferred_type
));
} }
} }
} else { } else {

View file

@ -156,4 +156,29 @@ m"#);
assert!(result.is_err()); assert!(result.is_err());
assert_eq!("Compilation failed: error at line 1, 'map' is a keyword. You cannot use it as an identifier",result.unwrap_err().to_string()); assert_eq!("Compilation failed: error at line 1, 'map' is a keyword. You cannot use it as an identifier",result.unwrap_err().to_string());
} }
#[test]
fn add_strings(){
assert_eq!(run(r#""a"+"b""#), Ok(Value::String("ab".into())));
}
#[test]
fn add_string_and_int(){
assert_eq!(run(r#""a"+42"#), Ok(Value::String("a42".into())));
}
#[test]
fn add_string_and_bool(){
assert_eq!(run(r#""a"+false"#), Ok(Value::String("afalse".into())));
}
#[test]
fn add_string_and_scientific_float(){
assert_eq!(run(r#""a"+4.2e10"#), Ok(Value::String("a42000000000".into())));
}
#[test]
fn add_hex_ints(){
assert_eq!(run(r#"0x10 + 0x20"#), Ok(Value::U32(48)));
}
} }

View file

@ -61,6 +61,8 @@ pub enum CompilerError {
UnexpectedType(TokenType), UnexpectedType(TokenType),
#[error("'{0}' is a keyword. You cannot use it as an identifier")] #[error("'{0}' is a keyword. You cannot use it as an identifier")]
KeywordNotAllowedAsIdentifier(TokenType), KeywordNotAllowedAsIdentifier(TokenType),
#[error("Crud does not support numbers above 2^64")]
Overflow,
} }
#[derive(Error, Debug, PartialEq)] #[derive(Error, Debug, PartialEq)]

View file

@ -1,4 +1,6 @@
use crate::tokens::TokenType::{BitXor, FloatingPoint, Integer}; use crate::errors::CompilerError::{IllegalCharLength, UnexpectedIdentifier, Unterminated};
use crate::errors::{CompilerError, CompilerErrorAtLine};
use crate::tokens::TokenType::{BitXor, FloatingPoint, Integer, U32, U64};
use crate::{ use crate::{
keywords, keywords,
tokens::{ tokens::{
@ -6,8 +8,6 @@ use crate::{
TokenType::{self}, TokenType::{self},
}, },
}; };
use crate::errors::{CompilerError, CompilerErrorAtLine};
use crate::errors::CompilerError::{IllegalCharLength, UnexpectedIdentifier, Unterminated};
pub fn scan(source: &str) -> Result<Vec<Token>, CompilerErrorAtLine> { pub fn scan(source: &str) -> Result<Vec<Token>, CompilerErrorAtLine> {
let scanner = Scanner { let scanner = Scanner {
@ -135,7 +135,9 @@ impl Scanner {
} }
'^' => self.add_token(BitXor), '^' => self.add_token(BitXor),
_ => { _ => {
if is_digit(c) { if c == '0' && self.peek() == 'x' {
self.hex_number()?;
} else if is_digit(c) {
self.number(); self.number();
} else if is_alpha(c) { } else if is_alpha(c) {
self.identifier(); self.identifier();
@ -158,6 +160,23 @@ impl Scanner {
self.add_token_with_value(tokentype, value); self.add_token_with_value(tokentype, value);
} }
fn hex_number(&mut self) -> Result<(), CompilerErrorAtLine> {
self.advance();
self.advance();
while is_digit(self.peek()) || is_alpha(self.peek()) {
self.advance();
}
let value: String = self.chars[self.start..self.current].iter().collect();
if value.len() < 5 {
self.add_token_with_value(U32, value);
} else if value.len() < 9 {
self.add_token_with_value(U64, value);
} else {
return Err(self.raise(CompilerError::Overflow));
}
Ok(())
}
fn number(&mut self) { fn number(&mut self) {
while is_digit(self.peek()) { while is_digit(self.peek()) {
self.advance(); self.advance();
@ -181,7 +200,7 @@ impl Scanner {
} }
if self.is_at_end() { if self.is_at_end() {
return Err(CompilerErrorAtLine::raise(Unterminated("char"), self.line)) return Err(CompilerErrorAtLine::raise(Unterminated("char"), self.line));
} }
self.advance(); self.advance();

View file

@ -45,6 +45,7 @@ pub enum TokenType {
GreaterEqual, GreaterEqual,
GreaterGreater, GreaterGreater,
Hash, Hash,
Hex,
I32, I32,
I64, I64,
Identifier, Identifier,
@ -123,6 +124,7 @@ impl fmt::Display for TokenType {
TokenType::GreaterEqual => write!(f, ">="), TokenType::GreaterEqual => write!(f, ">="),
TokenType::GreaterGreater => write!(f, ">>"), TokenType::GreaterGreater => write!(f, ">>"),
TokenType::Hash => write!(f, "#"), TokenType::Hash => write!(f, "#"),
TokenType::Hex => write!(f, "0x"),
TokenType::If => write!(f, "if"), TokenType::If => write!(f, "if"),
TokenType::Identifier => write!(f, "identifier"), TokenType::Identifier => write!(f, "identifier"),
TokenType::Indent => write!(f, "indent"), TokenType::Indent => write!(f, "indent"),