support hexadecimals
This commit is contained in:
parent
b95097834f
commit
42c431d5c7
5 changed files with 110 additions and 32 deletions
|
|
@ -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);
|
||||||
|
|
@ -50,14 +53,12 @@ impl AstCompiler {
|
||||||
self.current = 0;
|
self.current = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_tokens(&mut self) -> Result<Vec<Statement>,CompilerErrorAtLine> {
|
fn compile_tokens(&mut self) -> Result<Vec<Statement>, CompilerErrorAtLine> {
|
||||||
self.collect_functions()?;
|
self.collect_functions()?;
|
||||||
self.reset();
|
self.reset();
|
||||||
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);
|
||||||
|
|
@ -236,8 +235,10 @@ 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 {
|
||||||
|
|
|
||||||
|
|
@ -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)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)]
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
@ -32,7 +32,7 @@ impl Scanner {
|
||||||
Ok(self.tokens)
|
Ok(self.tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scan_token(&mut self) -> Result<(),CompilerErrorAtLine> {
|
fn scan_token(&mut self) -> Result<(), CompilerErrorAtLine> {
|
||||||
let c = self.advance();
|
let c = self.advance();
|
||||||
if self.new_line && (c == ' ' || c == '\t') {
|
if self.new_line && (c == ' ' || c == '\t') {
|
||||||
self.add_token(TokenType::Indent);
|
self.add_token(TokenType::Indent);
|
||||||
|
|
@ -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,8 +160,25 @@ 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();
|
||||||
}
|
}
|
||||||
let mut has_dot = false;
|
let mut has_dot = false;
|
||||||
|
|
@ -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();
|
||||||
|
|
@ -200,7 +219,7 @@ impl Scanner {
|
||||||
CompilerErrorAtLine::raise(error, self.line)
|
CompilerErrorAtLine::raise(error, self.line)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn string(&mut self) -> Result<(),CompilerErrorAtLine> {
|
fn string(&mut self) -> Result<(), CompilerErrorAtLine> {
|
||||||
while self.peek() != '"' && !self.is_at_end() {
|
while self.peek() != '"' && !self.is_at_end() {
|
||||||
if self.peek() == '\n' {
|
if self.peek() == '\n' {
|
||||||
self.line += 1;
|
self.line += 1;
|
||||||
|
|
@ -276,7 +295,7 @@ fn is_digit(c: char) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_digit_or_scientific(c: char) -> bool {
|
fn is_digit_or_scientific(c: char) -> bool {
|
||||||
is_digit(c) || c=='e' || c=='E'
|
is_digit(c) || c == 'e' || c == 'E'
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_alphanumeric(c: char) -> bool {
|
fn is_alphanumeric(c: char) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -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"),
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue