Add NativeFunction macro and implement read_line
This commit is contained in:
parent
4d6412006a
commit
382d43ef77
@ -581,9 +581,13 @@ impl Instruction {
|
||||
let to_register = self.a();
|
||||
let native_function = NativeFunction::from(self.b());
|
||||
let argument_count = self.c();
|
||||
let native_function_name = native_function.as_str();
|
||||
let mut output = String::new();
|
||||
|
||||
let mut output = format!("R{to_register} = {native_function_name}(");
|
||||
if native_function.returns_value() {
|
||||
let native_function_name = native_function.as_str();
|
||||
|
||||
output.push_str(&format!("R{} = {}(", to_register, native_function_name));
|
||||
}
|
||||
|
||||
if argument_count != 0 {
|
||||
let first_argument = to_register.saturating_sub(argument_count);
|
||||
@ -597,7 +601,9 @@ impl Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
output.push(')');
|
||||
if native_function.returns_value() {
|
||||
output.push(')');
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
@ -563,15 +563,11 @@ impl<'src> Lexer<'src> {
|
||||
"loop" => Token::Loop,
|
||||
"map" => Token::Map,
|
||||
"mut" => Token::Mut,
|
||||
"panic" => Token::Panic,
|
||||
"return" => Token::Return,
|
||||
"str" => Token::Str,
|
||||
"struct" => Token::Struct,
|
||||
"to_string" => Token::ToString,
|
||||
"true" => Token::Boolean("true"),
|
||||
"while" => Token::While,
|
||||
"write" => Token::Write,
|
||||
"write_line" => Token::WriteLine,
|
||||
_ => Token::Identifier(string),
|
||||
};
|
||||
|
||||
@ -1258,7 +1254,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
lex(input),
|
||||
Ok(vec![
|
||||
(Token::WriteLine, Span(0, 10)),
|
||||
(Token::Identifier("write_line"), Span(0, 10)),
|
||||
(Token::LeftParenthesis, Span(10, 11)),
|
||||
(Token::String("Hello, world!"), Span(11, 26)),
|
||||
(Token::RightParenthesis, Span(26, 27)),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::{
|
||||
fmt::{self, Display, Formatter},
|
||||
io::{self, stdout, Write},
|
||||
io::{self, stdin, stdout, Write},
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -128,62 +128,101 @@ pub enum NativeFunction {
|
||||
Write = WRITE as isize,
|
||||
}
|
||||
|
||||
impl NativeFunction {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
NativeFunction::Panic => "panic",
|
||||
NativeFunction::Parse => "parse",
|
||||
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",
|
||||
}
|
||||
}
|
||||
macro_rules! impl_from_str_for_native_function {
|
||||
($(($name:ident, $str:expr, $returns_value:expr)),*) => {
|
||||
impl NativeFunction {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
$(
|
||||
NativeFunction::$name => $str,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[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(
|
||||
&self,
|
||||
instruction: Instruction,
|
||||
@ -279,7 +318,20 @@ impl NativeFunction {
|
||||
NativeFunction::TrimStart => todo!(),
|
||||
|
||||
// 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 => {
|
||||
let to_register = instruction.a();
|
||||
let mut stdout = stdout();
|
||||
|
@ -749,8 +749,6 @@ impl<'src> Parser<'src> {
|
||||
let token = self.current_token;
|
||||
let start_position = self.current_position;
|
||||
|
||||
self.advance()?;
|
||||
|
||||
let local_index = if let Token::Identifier(text) = token {
|
||||
if let Ok(local_index) = self.chunk.get_local_index(text, start_position) {
|
||||
local_index
|
||||
@ -771,17 +769,25 @@ impl<'src> Parser<'src> {
|
||||
self.current_is_expression = true;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
return if NativeFunction::from_str(text).is_some() {
|
||||
self.parse_native_call(allowed)
|
||||
} else {
|
||||
return Err(ParseError::UndeclaredVariable {
|
||||
Err(ParseError::UndeclaredVariable {
|
||||
identifier: Identifier::new(text),
|
||||
position: start_position,
|
||||
});
|
||||
}
|
||||
})
|
||||
};
|
||||
} 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 {
|
||||
Err(ParseError::UndeclaredVariable {
|
||||
identifier: Identifier::new(text),
|
||||
position: start_position,
|
||||
})
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return Err(ParseError::ExpectedToken {
|
||||
@ -790,6 +796,9 @@ impl<'src> Parser<'src> {
|
||||
position: start_position,
|
||||
});
|
||||
};
|
||||
|
||||
self.advance()?;
|
||||
|
||||
let is_mutable = self
|
||||
.chunk
|
||||
.get_local(local_index, start_position)?
|
||||
@ -1059,45 +1068,43 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
|
||||
fn parse_native_call(&mut self, _: Allowed) -> Result<(), ParseError> {
|
||||
let native_function = match self.current_token {
|
||||
Token::Panic => NativeFunction::Panic,
|
||||
Token::ToString => NativeFunction::ToString,
|
||||
Token::Write => NativeFunction::Write,
|
||||
Token::WriteLine => NativeFunction::WriteLine,
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
let native_function = if let Token::Identifier(text) = self.current_token {
|
||||
NativeFunction::from_str(text).unwrap()
|
||||
} else {
|
||||
return Err(ParseError::ExpectedToken {
|
||||
expected: TokenKind::Identifier,
|
||||
found: self.current_token.to_owned(),
|
||||
position: self.current_position,
|
||||
});
|
||||
};
|
||||
let is_expression = self.current_token.is_expression();
|
||||
|
||||
let start = self.current_position.0;
|
||||
let start_register = self.next_register();
|
||||
|
||||
self.advance()?;
|
||||
|
||||
if self.allow(Token::LeftParenthesis)? {
|
||||
while !self.allow(Token::RightParenthesis)? {
|
||||
let expected_register = self.next_register();
|
||||
self.expect(Token::LeftParenthesis)?;
|
||||
|
||||
self.parse_expression()?;
|
||||
while !self.allow(Token::RightParenthesis)? {
|
||||
let expected_register = self.next_register();
|
||||
|
||||
let actual_register = self.next_register() - 1;
|
||||
self.parse_expression()?;
|
||||
|
||||
if expected_register < actual_register {
|
||||
self.emit_instruction(
|
||||
Instruction::close(expected_register, actual_register),
|
||||
self.current_position,
|
||||
);
|
||||
}
|
||||
let actual_register = self.next_register() - 1;
|
||||
|
||||
self.allow(Token::Comma)?;
|
||||
if expected_register < actual_register {
|
||||
self.emit_instruction(
|
||||
Instruction::close(expected_register, actual_register),
|
||||
self.current_position,
|
||||
);
|
||||
}
|
||||
|
||||
self.allow(Token::Comma)?;
|
||||
}
|
||||
|
||||
let end = self.current_position.1;
|
||||
let to_register = self.next_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(
|
||||
Instruction::call_native(to_register, native_function, argument_count),
|
||||
@ -1738,11 +1745,6 @@ impl From<&Token<'_>> for ParseRule<'_> {
|
||||
infix: None,
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
Token::Panic => ParseRule {
|
||||
prefix: Some(Parser::parse_native_call),
|
||||
infix: None,
|
||||
precedence: Precedence::Call,
|
||||
},
|
||||
Token::Percent => ParseRule {
|
||||
prefix: None,
|
||||
infix: Some(Parser::parse_math_binary),
|
||||
@ -1819,26 +1821,11 @@ impl From<&Token<'_>> for ParseRule<'_> {
|
||||
precedence: Precedence::None,
|
||||
},
|
||||
Token::Struct => todo!(),
|
||||
Token::ToString => ParseRule {
|
||||
prefix: Some(Parser::parse_native_call),
|
||||
infix: None,
|
||||
precedence: Precedence::Call,
|
||||
},
|
||||
Token::While => ParseRule {
|
||||
prefix: Some(Parser::parse_while),
|
||||
infix: 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,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,15 +47,10 @@ define_tokens! {
|
||||
Loop,
|
||||
Map,
|
||||
Mut,
|
||||
Panic,
|
||||
Return,
|
||||
Str,
|
||||
Struct,
|
||||
ToString,
|
||||
While,
|
||||
Write,
|
||||
|
||||
WriteLine,
|
||||
|
||||
// Symbols
|
||||
ArrowThin,
|
||||
@ -119,10 +114,7 @@ impl<'src> Token<'src> {
|
||||
Token::Mut => 3,
|
||||
Token::Str => 3,
|
||||
Token::Struct => 6,
|
||||
Token::ToString => 8,
|
||||
Token::While => 5,
|
||||
Token::Write => 5,
|
||||
Token::WriteLine => 10,
|
||||
Token::BangEqual => 2,
|
||||
Token::Bang => 1,
|
||||
Token::Colon => 1,
|
||||
@ -142,7 +134,6 @@ impl<'src> Token<'src> {
|
||||
Token::LessEqual => 2,
|
||||
Token::Minus => 1,
|
||||
Token::MinusEqual => 2,
|
||||
Token::Panic => 5,
|
||||
Token::Percent => 1,
|
||||
Token::PercentEqual => 2,
|
||||
Token::Plus => 1,
|
||||
@ -184,7 +175,6 @@ impl<'src> Token<'src> {
|
||||
Token::Mut => "mut",
|
||||
Token::Str => "str",
|
||||
Token::Struct => "struct",
|
||||
Token::ToString => "to_string",
|
||||
Token::While => "while",
|
||||
Token::BangEqual => "!=",
|
||||
Token::Bang => "!",
|
||||
@ -205,7 +195,6 @@ impl<'src> Token<'src> {
|
||||
Token::LessEqual => "<=",
|
||||
Token::Minus => "-",
|
||||
Token::MinusEqual => "-=",
|
||||
Token::Panic => "panic",
|
||||
Token::Percent => "%",
|
||||
Token::PercentEqual => "%=",
|
||||
Token::Plus => "+",
|
||||
@ -219,8 +208,6 @@ impl<'src> Token<'src> {
|
||||
Token::SlashEqual => "/=",
|
||||
Token::Star => "*",
|
||||
Token::StarEqual => "*=",
|
||||
Token::Write => "write",
|
||||
Token::WriteLine => "write_line",
|
||||
}
|
||||
}
|
||||
|
||||
@ -265,7 +252,6 @@ impl<'src> Token<'src> {
|
||||
Token::Minus => TokenOwned::Minus,
|
||||
Token::MinusEqual => TokenOwned::MinusEqual,
|
||||
Token::Mut => TokenOwned::Mut,
|
||||
Token::Panic => TokenOwned::Panic,
|
||||
Token::Percent => TokenOwned::Percent,
|
||||
Token::PercentEqual => TokenOwned::PercentEqual,
|
||||
Token::Plus => TokenOwned::Plus,
|
||||
@ -282,10 +268,7 @@ impl<'src> Token<'src> {
|
||||
Token::String(text) => TokenOwned::String(text.to_string()),
|
||||
Token::Str => TokenOwned::Str,
|
||||
Token::Struct => TokenOwned::Struct,
|
||||
Token::ToString => TokenOwned::ToString,
|
||||
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::MinusEqual => TokenKind::MinusEqual,
|
||||
Token::Mut => TokenKind::Mut,
|
||||
Token::Panic => TokenKind::Panic,
|
||||
Token::Percent => TokenKind::Percent,
|
||||
Token::PercentEqual => TokenKind::PercentEqual,
|
||||
Token::Plus => TokenKind::Plus,
|
||||
@ -346,11 +328,8 @@ impl<'src> Token<'src> {
|
||||
Token::SlashEqual => TokenKind::SlashEqual,
|
||||
Token::Str => TokenKind::Str,
|
||||
Token::String(_) => TokenKind::String,
|
||||
Token::ToString => TokenKind::ToString,
|
||||
Token::Struct => TokenKind::Struct,
|
||||
Token::While => TokenKind::While,
|
||||
Token::Write => TokenKind::Write,
|
||||
Token::WriteLine => TokenKind::WriteLine,
|
||||
}
|
||||
}
|
||||
|
||||
@ -393,7 +372,6 @@ impl<'src> Token<'src> {
|
||||
| Token::SlashEqual
|
||||
| Token::Star
|
||||
| Token::StarEqual
|
||||
| Token::ToString
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -440,7 +418,6 @@ impl<'src> Display for Token<'src> {
|
||||
Token::Minus => write!(f, "-"),
|
||||
Token::MinusEqual => write!(f, "-="),
|
||||
Token::Mut => write!(f, "mut"),
|
||||
Token::Panic => write!(f, "panic"),
|
||||
Token::Percent => write!(f, "%"),
|
||||
Token::PercentEqual => write!(f, "%="),
|
||||
Token::Plus => write!(f, "+"),
|
||||
@ -456,11 +433,8 @@ impl<'src> Display for Token<'src> {
|
||||
Token::StarEqual => write!(f, "*="),
|
||||
Token::Str => write!(f, "str"),
|
||||
Token::String(value) => write!(f, "{value}"),
|
||||
Token::ToString => write!(f, "to_string"),
|
||||
Token::Struct => write!(f, "struct"),
|
||||
Token::While => write!(f, "while"),
|
||||
Token::Write => write!(f, "write"),
|
||||
Token::WriteLine => write!(f, "write_line"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -495,13 +469,9 @@ pub enum TokenOwned {
|
||||
Loop,
|
||||
Map,
|
||||
Mut,
|
||||
Panic,
|
||||
Return,
|
||||
Str,
|
||||
ToString,
|
||||
While,
|
||||
Write,
|
||||
WriteLine,
|
||||
|
||||
// Symbols
|
||||
ArrowThin,
|
||||
@ -581,7 +551,6 @@ impl Display for TokenOwned {
|
||||
TokenOwned::Minus => Token::Minus.fmt(f),
|
||||
TokenOwned::MinusEqual => Token::MinusEqual.fmt(f),
|
||||
TokenOwned::Mut => Token::Mut.fmt(f),
|
||||
TokenOwned::Panic => Token::Panic.fmt(f),
|
||||
TokenOwned::Percent => Token::Percent.fmt(f),
|
||||
TokenOwned::PercentEqual => Token::PercentEqual.fmt(f),
|
||||
TokenOwned::Plus => Token::Plus.fmt(f),
|
||||
@ -598,10 +567,7 @@ impl Display for TokenOwned {
|
||||
TokenOwned::Str => Token::Str.fmt(f),
|
||||
TokenOwned::String(string) => Token::String(string).fmt(f),
|
||||
TokenOwned::Struct => Token::Struct.fmt(f),
|
||||
TokenOwned::ToString => Token::ToString.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::MinusEqual => Token::MinusEqual.fmt(f),
|
||||
TokenKind::Mut => Token::Mut.fmt(f),
|
||||
TokenKind::Panic => Token::Panic.fmt(f),
|
||||
TokenKind::Percent => Token::Percent.fmt(f),
|
||||
TokenKind::PercentEqual => Token::PercentEqual.fmt(f),
|
||||
TokenKind::Plus => Token::Plus.fmt(f),
|
||||
@ -665,10 +630,7 @@ impl Display for TokenKind {
|
||||
TokenKind::SlashEqual => Token::SlashEqual.fmt(f),
|
||||
TokenKind::String => write!(f, "string value"),
|
||||
TokenKind::Struct => Token::Struct.fmt(f),
|
||||
TokenKind::ToString => Token::ToString.fmt(f),
|
||||
TokenKind::While => Token::While.fmt(f),
|
||||
TokenKind::Write => Token::Write.fmt(f),
|
||||
TokenKind::WriteLine => Token::WriteLine.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user