1
0

Add NativeFunction macro and implement read_line

This commit is contained in:
Jeff 2024-10-30 09:11:28 -04:00
parent 4d6412006a
commit 382d43ef77
5 changed files with 158 additions and 155 deletions

View File

@ -581,9 +581,13 @@ impl Instruction {
let to_register = self.a(); let to_register = self.a();
let native_function = NativeFunction::from(self.b()); let native_function = NativeFunction::from(self.b());
let argument_count = self.c(); let argument_count = self.c();
let mut output = String::new();
if native_function.returns_value() {
let native_function_name = native_function.as_str(); let native_function_name = native_function.as_str();
let mut output = format!("R{to_register} = {native_function_name}("); output.push_str(&format!("R{} = {}(", to_register, native_function_name));
}
if argument_count != 0 { if argument_count != 0 {
let first_argument = to_register.saturating_sub(argument_count); let first_argument = to_register.saturating_sub(argument_count);
@ -597,7 +601,9 @@ impl Instruction {
} }
} }
if native_function.returns_value() {
output.push(')'); output.push(')');
}
output output
} }

View File

@ -563,15 +563,11 @@ impl<'src> Lexer<'src> {
"loop" => Token::Loop, "loop" => Token::Loop,
"map" => Token::Map, "map" => Token::Map,
"mut" => Token::Mut, "mut" => Token::Mut,
"panic" => Token::Panic,
"return" => Token::Return, "return" => Token::Return,
"str" => Token::Str, "str" => Token::Str,
"struct" => Token::Struct, "struct" => Token::Struct,
"to_string" => Token::ToString,
"true" => Token::Boolean("true"), "true" => Token::Boolean("true"),
"while" => Token::While, "while" => Token::While,
"write" => Token::Write,
"write_line" => Token::WriteLine,
_ => Token::Identifier(string), _ => Token::Identifier(string),
}; };
@ -1258,7 +1254,7 @@ mod tests {
assert_eq!( assert_eq!(
lex(input), lex(input),
Ok(vec![ Ok(vec![
(Token::WriteLine, Span(0, 10)), (Token::Identifier("write_line"), Span(0, 10)),
(Token::LeftParenthesis, Span(10, 11)), (Token::LeftParenthesis, Span(10, 11)),
(Token::String("Hello, world!"), Span(11, 26)), (Token::String("Hello, world!"), Span(11, 26)),
(Token::RightParenthesis, Span(26, 27)), (Token::RightParenthesis, Span(26, 27)),

View File

@ -1,6 +1,6 @@
use std::{ use std::{
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
io::{self, stdout, Write}, io::{self, stdin, stdout, Write},
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -128,62 +128,101 @@ pub enum NativeFunction {
Write = WRITE as isize, Write = WRITE as isize,
} }
impl NativeFunction { macro_rules! impl_from_str_for_native_function {
($(($name:ident, $str:expr, $returns_value:expr)),*) => {
impl NativeFunction {
pub fn as_str(&self) -> &'static str { pub fn as_str(&self) -> &'static str {
match self { match self {
NativeFunction::Panic => "panic", $(
NativeFunction::Parse => "parse", NativeFunction::$name => $str,
NativeFunction::ToByte => "to_byte", )*
NativeFunction::ToFloat => "to_float",
NativeFunction::ToInteger => "to_integer",
NativeFunction::ToString => "to_string",
NativeFunction::All => "all",
NativeFunction::Any => "any",
NativeFunction::Append => "append",
NativeFunction::Contains => "contains",
NativeFunction::Find => "find",
NativeFunction::Flatten => "flatten",
NativeFunction::Get => "get",
NativeFunction::IndexOf => "index_of",
NativeFunction::Join => "join",
NativeFunction::Length => "length",
NativeFunction::Map => "map",
NativeFunction::Prepend => "prepend",
NativeFunction::Reduce => "reduce",
NativeFunction::Remove => "remove",
NativeFunction::Reverse => "reverse",
NativeFunction::Set => "set",
NativeFunction::Slice => "slice",
NativeFunction::Sort => "sort",
NativeFunction::Split => "split",
NativeFunction::Unzip => "unzip",
NativeFunction::Zip => "zip",
NativeFunction::CharAt => "char_at",
NativeFunction::CharCodeAt => "char_code_at",
NativeFunction::Chars => "chars",
NativeFunction::EndsWith => "ends_with",
NativeFunction::Format => "format",
NativeFunction::Includes => "includes",
NativeFunction::Match => "match",
NativeFunction::PadEnd => "pad_end",
NativeFunction::PadStart => "pad_start",
NativeFunction::Repeat => "repeat",
NativeFunction::Replace => "replace",
NativeFunction::SplitAt => "split_at",
NativeFunction::SplitLines => "split_lines",
NativeFunction::SplitWhitespace => "split_whitespace",
NativeFunction::StartsWith => "starts_with",
NativeFunction::ToLowerCase => "to_lower_case",
NativeFunction::ToUpperCase => "to_upper_case",
NativeFunction::Trim => "trim",
NativeFunction::TrimEnd => "trim_end",
NativeFunction::TrimStart => "trim_start",
NativeFunction::ReadLine => "read_line",
NativeFunction::WriteLine => "write_line",
NativeFunction::Write => "write",
} }
} }
#[allow(clippy::should_implement_trait)]
pub fn from_str(string: &str) -> Option<Self> {
match string {
$(
$str => Some(NativeFunction::$name),
)*
_ => None,
}
}
pub fn returns_value(&self) -> bool {
match self {
$(
NativeFunction::$name => $returns_value,
)*
}
}
}
};
}
// Use the macro to implement From<&str> for NativeFunction
impl_from_str_for_native_function! {
(Panic, "panic", false),
// Type conversion
(Parse, "parse", true),
(ToByte, "to_byte", true),
(ToFloat, "to_float", true),
(ToInteger, "to_integer", true),
(ToString, "to_string", true),
// List
(All, "all", true),
(Any, "any", true),
(Append, "append", true),
(Contains, "contains", true),
(Find, "find", true),
(Flatten, "flatten", true),
(Get, "get", true),
(IndexOf, "index_of", true),
(Join, "join", true),
(Length, "length", true),
(Map, "map", true),
(Prepend, "prepend", true),
(Reduce, "reduce", true),
(Remove, "remove", true),
(Reverse, "reverse", true),
(Set, "set", true),
(Slice, "slice", true),
(Sort, "sort", true),
(Split, "split", true),
(Unzip, "unzip", true),
(Zip, "zip", true),
// String
(CharAt, "char_at", true),
(CharCodeAt, "char_code_at", true),
(Chars, "chars", true),
(EndsWith, "ends_with", true),
(Format, "format", true),
(Includes, "includes", true),
(Match, "match", true),
(PadEnd, "pad_end", true),
(PadStart, "pad_start", true),
(Repeat, "repeat", true),
(Replace, "replace", true),
(SplitAt, "split_at", true),
(SplitLines, "split_lines", true),
(SplitWhitespace, "split_whitespace", true),
(StartsWith, "starts_with", true),
(ToLowerCase, "to_lower_case", true),
(ToUpperCase, "to_upper_case", true),
(Trim, "trim", true),
(TrimEnd, "trim_end", true),
(TrimStart, "trim_start", true),
// I/O
(ReadLine, "read_line", true),
(WriteLine, "write_line", false),
(Write, "write", false)
}
impl NativeFunction {
pub fn call( pub fn call(
&self, &self,
instruction: Instruction, instruction: Instruction,
@ -279,7 +318,20 @@ impl NativeFunction {
NativeFunction::TrimStart => todo!(), NativeFunction::TrimStart => todo!(),
// I/O // I/O
NativeFunction::ReadLine => todo!(), NativeFunction::ReadLine => {
let mut buffer = String::new();
stdin()
.read_line(&mut buffer)
.map_err(|io_error| VmError::Io {
error: io_error.kind(),
position,
})?;
buffer = buffer.trim_end_matches('\n').to_string();
Some(Value::Primitive(Primitive::String(buffer)))
}
NativeFunction::Write => { NativeFunction::Write => {
let to_register = instruction.a(); let to_register = instruction.a();
let mut stdout = stdout(); let mut stdout = stdout();

View File

@ -749,8 +749,6 @@ impl<'src> Parser<'src> {
let token = self.current_token; let token = self.current_token;
let start_position = self.current_position; let start_position = self.current_position;
self.advance()?;
let local_index = if let Token::Identifier(text) = token { let local_index = if let Token::Identifier(text) = token {
if let Ok(local_index) = self.chunk.get_local_index(text, start_position) { if let Ok(local_index) = self.chunk.get_local_index(text, start_position) {
local_index local_index
@ -771,17 +769,25 @@ impl<'src> Parser<'src> {
self.current_is_expression = true; self.current_is_expression = true;
return Ok(()); return Ok(());
} else {
return Err(ParseError::UndeclaredVariable {
identifier: Identifier::new(text),
position: start_position,
});
} }
return if NativeFunction::from_str(text).is_some() {
self.parse_native_call(allowed)
} else { } else {
return Err(ParseError::UndeclaredVariable { Err(ParseError::UndeclaredVariable {
identifier: Identifier::new(text), identifier: Identifier::new(text),
position: start_position, position: start_position,
}); })
};
} else {
return if NativeFunction::from_str(text).is_some() {
self.parse_native_call(allowed)
} else {
Err(ParseError::UndeclaredVariable {
identifier: Identifier::new(text),
position: start_position,
})
};
} }
} else { } else {
return Err(ParseError::ExpectedToken { return Err(ParseError::ExpectedToken {
@ -790,6 +796,9 @@ impl<'src> Parser<'src> {
position: start_position, position: start_position,
}); });
}; };
self.advance()?;
let is_mutable = self let is_mutable = self
.chunk .chunk
.get_local(local_index, start_position)? .get_local(local_index, start_position)?
@ -1059,23 +1068,22 @@ impl<'src> Parser<'src> {
} }
fn parse_native_call(&mut self, _: Allowed) -> Result<(), ParseError> { fn parse_native_call(&mut self, _: Allowed) -> Result<(), ParseError> {
let native_function = match self.current_token { let native_function = if let Token::Identifier(text) = self.current_token {
Token::Panic => NativeFunction::Panic, NativeFunction::from_str(text).unwrap()
Token::ToString => NativeFunction::ToString, } else {
Token::Write => NativeFunction::Write, return Err(ParseError::ExpectedToken {
Token::WriteLine => NativeFunction::WriteLine, expected: TokenKind::Identifier,
_ => { found: self.current_token.to_owned(),
unreachable!() position: self.current_position,
} });
}; };
let is_expression = self.current_token.is_expression();
let start = self.current_position.0; let start = self.current_position.0;
let start_register = self.next_register(); let start_register = self.next_register();
self.advance()?; self.advance()?;
if self.allow(Token::LeftParenthesis)? { self.expect(Token::LeftParenthesis)?;
while !self.allow(Token::RightParenthesis)? { while !self.allow(Token::RightParenthesis)? {
let expected_register = self.next_register(); let expected_register = self.next_register();
@ -1092,12 +1100,11 @@ impl<'src> Parser<'src> {
self.allow(Token::Comma)?; self.allow(Token::Comma)?;
} }
}
let end = self.current_position.1; let end = self.current_position.1;
let to_register = self.next_register(); let to_register = self.next_register();
let argument_count = to_register - start_register; let argument_count = to_register - start_register;
self.current_is_expression = is_expression; self.current_is_expression = native_function.returns_value();
self.emit_instruction( self.emit_instruction(
Instruction::call_native(to_register, native_function, argument_count), Instruction::call_native(to_register, native_function, argument_count),
@ -1738,11 +1745,6 @@ impl From<&Token<'_>> for ParseRule<'_> {
infix: None, infix: None,
precedence: Precedence::None, precedence: Precedence::None,
}, },
Token::Panic => ParseRule {
prefix: Some(Parser::parse_native_call),
infix: None,
precedence: Precedence::Call,
},
Token::Percent => ParseRule { Token::Percent => ParseRule {
prefix: None, prefix: None,
infix: Some(Parser::parse_math_binary), infix: Some(Parser::parse_math_binary),
@ -1819,26 +1821,11 @@ impl From<&Token<'_>> for ParseRule<'_> {
precedence: Precedence::None, precedence: Precedence::None,
}, },
Token::Struct => todo!(), Token::Struct => todo!(),
Token::ToString => ParseRule {
prefix: Some(Parser::parse_native_call),
infix: None,
precedence: Precedence::Call,
},
Token::While => ParseRule { Token::While => ParseRule {
prefix: Some(Parser::parse_while), prefix: Some(Parser::parse_while),
infix: None, infix: None,
precedence: Precedence::None, precedence: Precedence::None,
}, },
Token::Write => ParseRule {
prefix: Some(Parser::parse_native_call),
infix: None,
precedence: Precedence::Call,
},
Token::WriteLine => ParseRule {
prefix: Some(Parser::parse_native_call),
infix: None,
precedence: Precedence::Call,
},
} }
} }
} }

View File

@ -47,15 +47,10 @@ define_tokens! {
Loop, Loop,
Map, Map,
Mut, Mut,
Panic,
Return, Return,
Str, Str,
Struct, Struct,
ToString,
While, While,
Write,
WriteLine,
// Symbols // Symbols
ArrowThin, ArrowThin,
@ -119,10 +114,7 @@ impl<'src> Token<'src> {
Token::Mut => 3, Token::Mut => 3,
Token::Str => 3, Token::Str => 3,
Token::Struct => 6, Token::Struct => 6,
Token::ToString => 8,
Token::While => 5, Token::While => 5,
Token::Write => 5,
Token::WriteLine => 10,
Token::BangEqual => 2, Token::BangEqual => 2,
Token::Bang => 1, Token::Bang => 1,
Token::Colon => 1, Token::Colon => 1,
@ -142,7 +134,6 @@ impl<'src> Token<'src> {
Token::LessEqual => 2, Token::LessEqual => 2,
Token::Minus => 1, Token::Minus => 1,
Token::MinusEqual => 2, Token::MinusEqual => 2,
Token::Panic => 5,
Token::Percent => 1, Token::Percent => 1,
Token::PercentEqual => 2, Token::PercentEqual => 2,
Token::Plus => 1, Token::Plus => 1,
@ -184,7 +175,6 @@ impl<'src> Token<'src> {
Token::Mut => "mut", Token::Mut => "mut",
Token::Str => "str", Token::Str => "str",
Token::Struct => "struct", Token::Struct => "struct",
Token::ToString => "to_string",
Token::While => "while", Token::While => "while",
Token::BangEqual => "!=", Token::BangEqual => "!=",
Token::Bang => "!", Token::Bang => "!",
@ -205,7 +195,6 @@ impl<'src> Token<'src> {
Token::LessEqual => "<=", Token::LessEqual => "<=",
Token::Minus => "-", Token::Minus => "-",
Token::MinusEqual => "-=", Token::MinusEqual => "-=",
Token::Panic => "panic",
Token::Percent => "%", Token::Percent => "%",
Token::PercentEqual => "%=", Token::PercentEqual => "%=",
Token::Plus => "+", Token::Plus => "+",
@ -219,8 +208,6 @@ impl<'src> Token<'src> {
Token::SlashEqual => "/=", Token::SlashEqual => "/=",
Token::Star => "*", Token::Star => "*",
Token::StarEqual => "*=", Token::StarEqual => "*=",
Token::Write => "write",
Token::WriteLine => "write_line",
} }
} }
@ -265,7 +252,6 @@ impl<'src> Token<'src> {
Token::Minus => TokenOwned::Minus, Token::Minus => TokenOwned::Minus,
Token::MinusEqual => TokenOwned::MinusEqual, Token::MinusEqual => TokenOwned::MinusEqual,
Token::Mut => TokenOwned::Mut, Token::Mut => TokenOwned::Mut,
Token::Panic => TokenOwned::Panic,
Token::Percent => TokenOwned::Percent, Token::Percent => TokenOwned::Percent,
Token::PercentEqual => TokenOwned::PercentEqual, Token::PercentEqual => TokenOwned::PercentEqual,
Token::Plus => TokenOwned::Plus, Token::Plus => TokenOwned::Plus,
@ -282,10 +268,7 @@ impl<'src> Token<'src> {
Token::String(text) => TokenOwned::String(text.to_string()), Token::String(text) => TokenOwned::String(text.to_string()),
Token::Str => TokenOwned::Str, Token::Str => TokenOwned::Str,
Token::Struct => TokenOwned::Struct, Token::Struct => TokenOwned::Struct,
Token::ToString => TokenOwned::ToString,
Token::While => TokenOwned::While, Token::While => TokenOwned::While,
Token::Write => TokenOwned::Write,
Token::WriteLine => TokenOwned::WriteLine,
} }
} }
@ -330,7 +313,6 @@ impl<'src> Token<'src> {
Token::Minus => TokenKind::Minus, Token::Minus => TokenKind::Minus,
Token::MinusEqual => TokenKind::MinusEqual, Token::MinusEqual => TokenKind::MinusEqual,
Token::Mut => TokenKind::Mut, Token::Mut => TokenKind::Mut,
Token::Panic => TokenKind::Panic,
Token::Percent => TokenKind::Percent, Token::Percent => TokenKind::Percent,
Token::PercentEqual => TokenKind::PercentEqual, Token::PercentEqual => TokenKind::PercentEqual,
Token::Plus => TokenKind::Plus, Token::Plus => TokenKind::Plus,
@ -346,11 +328,8 @@ impl<'src> Token<'src> {
Token::SlashEqual => TokenKind::SlashEqual, Token::SlashEqual => TokenKind::SlashEqual,
Token::Str => TokenKind::Str, Token::Str => TokenKind::Str,
Token::String(_) => TokenKind::String, Token::String(_) => TokenKind::String,
Token::ToString => TokenKind::ToString,
Token::Struct => TokenKind::Struct, Token::Struct => TokenKind::Struct,
Token::While => TokenKind::While, Token::While => TokenKind::While,
Token::Write => TokenKind::Write,
Token::WriteLine => TokenKind::WriteLine,
} }
} }
@ -393,7 +372,6 @@ impl<'src> Token<'src> {
| Token::SlashEqual | Token::SlashEqual
| Token::Star | Token::Star
| Token::StarEqual | Token::StarEqual
| Token::ToString
) )
} }
} }
@ -440,7 +418,6 @@ impl<'src> Display for Token<'src> {
Token::Minus => write!(f, "-"), Token::Minus => write!(f, "-"),
Token::MinusEqual => write!(f, "-="), Token::MinusEqual => write!(f, "-="),
Token::Mut => write!(f, "mut"), Token::Mut => write!(f, "mut"),
Token::Panic => write!(f, "panic"),
Token::Percent => write!(f, "%"), Token::Percent => write!(f, "%"),
Token::PercentEqual => write!(f, "%="), Token::PercentEqual => write!(f, "%="),
Token::Plus => write!(f, "+"), Token::Plus => write!(f, "+"),
@ -456,11 +433,8 @@ impl<'src> Display for Token<'src> {
Token::StarEqual => write!(f, "*="), Token::StarEqual => write!(f, "*="),
Token::Str => write!(f, "str"), Token::Str => write!(f, "str"),
Token::String(value) => write!(f, "{value}"), Token::String(value) => write!(f, "{value}"),
Token::ToString => write!(f, "to_string"),
Token::Struct => write!(f, "struct"), Token::Struct => write!(f, "struct"),
Token::While => write!(f, "while"), Token::While => write!(f, "while"),
Token::Write => write!(f, "write"),
Token::WriteLine => write!(f, "write_line"),
} }
} }
} }
@ -495,13 +469,9 @@ pub enum TokenOwned {
Loop, Loop,
Map, Map,
Mut, Mut,
Panic,
Return, Return,
Str, Str,
ToString,
While, While,
Write,
WriteLine,
// Symbols // Symbols
ArrowThin, ArrowThin,
@ -581,7 +551,6 @@ impl Display for TokenOwned {
TokenOwned::Minus => Token::Minus.fmt(f), TokenOwned::Minus => Token::Minus.fmt(f),
TokenOwned::MinusEqual => Token::MinusEqual.fmt(f), TokenOwned::MinusEqual => Token::MinusEqual.fmt(f),
TokenOwned::Mut => Token::Mut.fmt(f), TokenOwned::Mut => Token::Mut.fmt(f),
TokenOwned::Panic => Token::Panic.fmt(f),
TokenOwned::Percent => Token::Percent.fmt(f), TokenOwned::Percent => Token::Percent.fmt(f),
TokenOwned::PercentEqual => Token::PercentEqual.fmt(f), TokenOwned::PercentEqual => Token::PercentEqual.fmt(f),
TokenOwned::Plus => Token::Plus.fmt(f), TokenOwned::Plus => Token::Plus.fmt(f),
@ -598,10 +567,7 @@ impl Display for TokenOwned {
TokenOwned::Str => Token::Str.fmt(f), TokenOwned::Str => Token::Str.fmt(f),
TokenOwned::String(string) => Token::String(string).fmt(f), TokenOwned::String(string) => Token::String(string).fmt(f),
TokenOwned::Struct => Token::Struct.fmt(f), TokenOwned::Struct => Token::Struct.fmt(f),
TokenOwned::ToString => Token::ToString.fmt(f),
TokenOwned::While => Token::While.fmt(f), TokenOwned::While => Token::While.fmt(f),
TokenOwned::Write => Token::Write.fmt(f),
TokenOwned::WriteLine => Token::WriteLine.fmt(f),
} }
} }
} }
@ -648,7 +614,6 @@ impl Display for TokenKind {
TokenKind::Minus => Token::Minus.fmt(f), TokenKind::Minus => Token::Minus.fmt(f),
TokenKind::MinusEqual => Token::MinusEqual.fmt(f), TokenKind::MinusEqual => Token::MinusEqual.fmt(f),
TokenKind::Mut => Token::Mut.fmt(f), TokenKind::Mut => Token::Mut.fmt(f),
TokenKind::Panic => Token::Panic.fmt(f),
TokenKind::Percent => Token::Percent.fmt(f), TokenKind::Percent => Token::Percent.fmt(f),
TokenKind::PercentEqual => Token::PercentEqual.fmt(f), TokenKind::PercentEqual => Token::PercentEqual.fmt(f),
TokenKind::Plus => Token::Plus.fmt(f), TokenKind::Plus => Token::Plus.fmt(f),
@ -665,10 +630,7 @@ impl Display for TokenKind {
TokenKind::SlashEqual => Token::SlashEqual.fmt(f), TokenKind::SlashEqual => Token::SlashEqual.fmt(f),
TokenKind::String => write!(f, "string value"), TokenKind::String => write!(f, "string value"),
TokenKind::Struct => Token::Struct.fmt(f), TokenKind::Struct => Token::Struct.fmt(f),
TokenKind::ToString => Token::ToString.fmt(f),
TokenKind::While => Token::While.fmt(f), TokenKind::While => Token::While.fmt(f),
TokenKind::Write => Token::Write.fmt(f),
TokenKind::WriteLine => Token::WriteLine.fmt(f),
} }
} }
} }