Skip to content
Snippets Groups Projects
Verified Commit ff05c138 authored by Louis's avatar Louis :fire:
Browse files

Replace peg parse_ fns with lalrpop parse_ fns

parent bb0c2dfa
No related branches found
No related tags found
No related merge requests found
Pipeline #504 failed with stages
in 1 day, 18 hours, 46 minutes, and 52 seconds
...@@ -130,4 +130,4 @@ target/ ...@@ -130,4 +130,4 @@ target/
# Generated Parser # Generated Parser
forge-script-lang/src/parser/forge_script.rs forge-script-lang/src/parser/forge_grammar.rs
\ No newline at end of file \ No newline at end of file
pub fn main() { pub fn main() {
lalrpop::Configuration::new() lalrpop::Configuration::new()
.process_file("src/parser/forge_script.lalrpop") .generate_in_source_tree()
.process_file("src/parser/forge_grammar.lalrpop")
.expect("Failed to parse language grammar"); .expect("Failed to parse language grammar");
} }
use crate::lexer::Span; use crate::lexer::{ScriptTokenType, Span};
use crate::parse::ScriptToken; use crate::parse::ScriptToken;
use peg::error::ExpectedSet; use crate::utilities::offset_to_line_column;
use lalrpop_util::ParseError as BaseLalrError;
use std::error::Error; use std::error::Error;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::process::{ExitCode, Termination};
pub type LalrError<'a> = BaseLalrError<usize, ScriptTokenType<'a>, TokenError<'a>>;
#[derive(Debug)] #[derive(Debug)]
pub enum TokenErrorKind<'a> { pub enum TokenErrorKind<'a> {
...@@ -61,13 +65,53 @@ pub enum ForgeErrorKind<'a> { ...@@ -61,13 +65,53 @@ pub enum ForgeErrorKind<'a> {
found: ScriptToken<'a>, found: ScriptToken<'a>,
expected: peg::error::ExpectedSet, expected: peg::error::ExpectedSet,
}, },
InvalidToken {
location: TokenIndex,
},
UnexpectedEof {
expected: Vec<String>,
},
UnrecognizedToken {
token: ScriptTokenType<'a>,
span: ErrorSpan,
expected: Vec<String>,
},
ExpectedEof {
token: ScriptTokenType<'a>,
span: ErrorSpan,
},
Custom(String),
} }
pub type TokenIndex = usize;
pub type ErrorSpan = (TokenIndex, TokenIndex);
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct ForgeError<'a> { pub struct ForgeError<'a> {
pub kind: ForgeErrorKind<'a>, pub kind: ForgeErrorKind<'a>,
} }
impl<'a> Termination for ForgeError<'a> {
fn report(self) -> ExitCode {
match self.kind {
ForgeErrorKind::IncompleteInput => ExitCode::from(101),
ForgeErrorKind::LexerError(_) => ExitCode::from(102),
ForgeErrorKind::UnexpectedToken { .. } => ExitCode::from(103),
ForgeErrorKind::InvalidToken { .. } => ExitCode::from(104),
ForgeErrorKind::UnexpectedEof { .. } => ExitCode::from(105),
ForgeErrorKind::UnrecognizedToken { .. } => ExitCode::from(106),
ForgeErrorKind::ExpectedEof { .. } => ExitCode::from(107),
ForgeErrorKind::Custom(_) => ExitCode::from(1),
}
}
}
impl<'a> From<ForgeErrorKind<'a>> for ForgeError<'a> {
fn from(value: ForgeErrorKind<'a>) -> Self {
Self { kind: value }
}
}
impl<'a> From<ParseError<'a>> for ForgeError<'a> { impl<'a> From<ParseError<'a>> for ForgeError<'a> {
fn from(value: ParseError<'a>) -> Self { fn from(value: ParseError<'a>) -> Self {
match value.kind { match value.kind {
...@@ -91,6 +135,31 @@ impl<'a> From<TokenError<'a>> for ForgeError<'a> { ...@@ -91,6 +135,31 @@ impl<'a> From<TokenError<'a>> for ForgeError<'a> {
} }
} }
impl<'a> From<LalrError<'a>> for ForgeError<'a> {
fn from(value: LalrError<'a>) -> Self {
match value {
LalrError::InvalidToken { location } => {
ForgeErrorKind::InvalidToken { location }.into()
}
LalrError::UnrecognizedEof { expected, .. } => {
ForgeErrorKind::UnexpectedEof { expected }.into()
}
LalrError::UnrecognizedToken { token, expected } => ForgeErrorKind::UnrecognizedToken {
expected,
token: token.1,
span: (token.0, token.2),
}
.into(),
LalrError::ExtraToken { token } => ForgeErrorKind::ExpectedEof {
token: token.1,
span: (token.0, token.2),
}
.into(),
LalrError::User { error } => ForgeErrorKind::Custom(format!("{}", error)).into(),
}
}
}
pub type ForgeResult<'a, T> = Result<T, ForgeError<'a>>; pub type ForgeResult<'a, T> = Result<T, ForgeError<'a>>;
pub fn print_unexpected_token<'a>( pub fn print_unexpected_token<'a>(
...@@ -134,79 +203,178 @@ pub fn print_unexpected_token<'a>( ...@@ -134,79 +203,178 @@ pub fn print_unexpected_token<'a>(
eprintln!("|\n| Failed To Parse: expected {}", expected); eprintln!("|\n| Failed To Parse: expected {}", expected);
} }
pub struct ErrorFormat<'a>(pub &'a str, pub &'a ScriptToken<'a>, pub &'a ExpectedSet); pub fn print_forge_error<'a>(source: &'a str, fe: &'a ForgeError) {
eprintln!("{}", format_forge_error(source, fe));
}
pub fn format_forge_error<'a>(source: &'a str, fe: &'a ForgeError) -> String {
match &fe.kind {
ForgeErrorKind::IncompleteInput => String::from("| Unexpected end of file"),
ForgeErrorKind::LexerError(err) => format!("| {}", err),
ForgeErrorKind::UnexpectedToken { found, expected } => {
format!(
"{}",
SourcePrinter(
source,
format!("{}", found.token_type),
HighlightConfig {
line: found.position.location_line() as usize,
column: found.position.get_column(),
highlight_len: found.token_type.len(),
},
Some(format!(
"| Found {}, expected one of {}",
found.token_type,
expected
.tokens()
.collect::<Vec<&str>>()
.as_slice()
.join(", ")
))
),
)
}
ForgeErrorKind::InvalidToken { location } => {
let (line, column) = offset_to_line_column(source, *location);
format!(
"{}",
SourcePrinter(
source,
String::from("invalid token"),
HighlightConfig {
line,
column,
highlight_len: 1,
},
None,
)
)
}
ForgeErrorKind::UnexpectedEof { expected } => {
format!(
"| Unexpected end of file, expected one of {}",
expected.as_slice().join(", ")
)
}
ForgeErrorKind::UnrecognizedToken {
token,
span,
expected,
} => {
let (line, column) = offset_to_line_column(source, span.0);
format!(
"{}",
SourcePrinter(
source,
format!("{}", token),
HighlightConfig {
line,
column,
highlight_len: token.len(),
},
Some(format!(
"| Found {}, expected one of {}",
token,
expected.as_slice().join(", ")
))
),
)
}
ForgeErrorKind::ExpectedEof { token, span } => {
let (line, column) = offset_to_line_column(source, span.0);
format!(
"{}",
SourcePrinter(
source,
format!("{}", token),
HighlightConfig {
line,
column,
highlight_len: token.len(),
},
Some(format!("| Expected EOF, found {}", token))
),
)
}
ForgeErrorKind::Custom(msg) => format!("| {}", msg),
}
}
#[derive(Clone, Copy)]
pub struct HighlightConfig {
line: usize,
column: usize,
highlight_len: usize,
}
impl<'a> Display for ErrorFormat<'a> { struct SourcePrinter<'a>(&'a str, String, HighlightConfig, Option<String>);
impl<'a> Display for SourcePrinter<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let ErrorFormat(source, token, expected) = self; highlight_source_snippet(self.0, &self.1, self.2, self.3.clone(), f)
}
let line = token.position.location_line() as usize; }
let column = token.position.get_column();
pub fn highlight_source_snippet(
let previous_line = if line > 1 { source: &str,
source.lines().nth(line - 2) name: impl Display,
} else { highlight: HighlightConfig,
None additional: Option<String>,
}; f: &mut Formatter<'_>,
let source_line = source.lines().nth(line - 1).expect("Missing line"); ) -> std::fmt::Result {
let next_line = source.lines().nth(line); let HighlightConfig {
line,
let largest_line_num = line.max(line.saturating_sub(1)).max(line.saturating_add(1)); column,
let number_length = format!("{}", largest_line_num).len(); highlight_len,
} = highlight;
writeln!(f, "| Script error on line {} at \"{}\"\n|", line, token)?;
if let Some(prev) = previous_line { let previous_line = if line > 1 {
writeln!( source.lines().nth(line - 2)
f, } else {
"| [{:>width$}] {}", None
line - 1, };
prev, let source_line = source.lines().nth(line - 1).expect("Missing line");
width = number_length let next_line = source.lines().nth(line);
)?;
} let largest_line_num = line.max(line.saturating_sub(1)).max(line.saturating_add(1));
let number_length = format!("{}", largest_line_num).len();
writeln!(f, "| Script error on line {} at \"{}\"\n|", line, name)?;
if let Some(prev) = previous_line {
writeln!( writeln!(
f, f,
"| [{:>width$}] {}", "| [{:>width$}] {}",
line, line - 1,
source_line, prev,
width = number_length width = number_length
)?; )?;
}
writeln!(
f,
"| [{:>width$}] {}",
line,
source_line,
width = number_length
)?;
writeln!(
f,
"| {} {}{}",
vec![" "; number_length + 2].join(""),
vec![" "; column - 1].join(""),
vec!["^"; highlight_len].join(""),
)?;
if let Some(next) = next_line {
writeln!( writeln!(
f, f,
"| {} {}{}", "| [{:>width$}] {}",
vec![" "; number_length + 2].join(""), line + 1,
vec![" "; column - 1].join(""), next,
vec!["^"; token.token_type.len()].join(""), width = number_length
)?; )?;
if let Some(next) = next_line {
writeln!(
f,
"| [{:>width$}] {}",
line + 1,
next,
width = number_length
)?;
}
writeln!(f, "|\n| Failed To Parse: expected {}", expected)
}
}
pub fn print_forge_error<'a>(source: &'a str, fe: &'a ForgeError) {
match &fe.kind {
ForgeErrorKind::IncompleteInput => eprintln!("| Unexpected end of file"),
ForgeErrorKind::LexerError(err) => eprintln!("| {}", err),
ForgeErrorKind::UnexpectedToken { found, expected } => {
print_unexpected_token(source, found, expected)
}
} }
}
pub fn format_forge_error<'a>(source: &'a str, fe: &'a ForgeError) -> String { if let Some(extra) = additional {
match &fe.kind { writeln!(f, "|\n| {}", extra)
ForgeErrorKind::IncompleteInput => String::from("| Unexpected end of file"), } else {
ForgeErrorKind::LexerError(err) => format!("| {}", err), writeln!(f, "|")
ForgeErrorKind::UnexpectedToken { found, expected } => {
format!("{}", ErrorFormat(source, found, expected))
}
} }
} }
...@@ -66,7 +66,7 @@ pub fn token_if(input: Span) -> IResult<Span, ScriptToken> { ...@@ -66,7 +66,7 @@ pub fn token_if(input: Span) -> IResult<Span, ScriptToken> {
pub fn token_null(input: Span) -> IResult<Span, ScriptToken> { pub fn token_null(input: Span) -> IResult<Span, ScriptToken> {
let (input, pos) = position(input)?; let (input, pos) = position(input)?;
let (input, _) = tag_ws("null", input)?; let (input, _) = tag("null")(input)?;
Ok(( Ok((
input, input,
ScriptToken { ScriptToken {
......
...@@ -108,6 +108,24 @@ mod _lex { ...@@ -108,6 +108,24 @@ mod _lex {
Ok(tokens) Ok(tokens)
} }
#[cfg(test)]
mod lex_test {
use super::*;
use test_case::test_case;
#[test_case("123" => matches Ok(ScriptTokenType::Integer(123)))]
#[test_case("0.123" => matches Ok(ScriptTokenType::Float(_)))]
#[test_case("null" => matches Ok(ScriptTokenType::Null))]
#[test_case("foobar" => matches Ok(ScriptTokenType::Identifier(_)))]
#[test_case("true" => matches Ok(ScriptTokenType::Boolean(true)))]
#[test_case("false" => matches Ok(ScriptTokenType::Boolean(false)))]
fn correct_lexing(inp: &str) -> Result<ScriptTokenType, ()> {
any_token(Span::new(inp))
.map_err(|_| ())
.map(|tok| tok.1.token_type)
}
}
} }
use crate::parser::TokenSlice; use crate::parser::TokenSlice;
......
...@@ -6,13 +6,13 @@ use std::ops::Deref; ...@@ -6,13 +6,13 @@ use std::ops::Deref;
pub trait AstNode {} pub trait AstNode {}
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Program(pub ExpressionList); pub struct Program(pub ExpressionList);
impl AstNode for Program {} impl AstNode for Program {}
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ExpressionList { pub struct ExpressionList {
pub expressions: Vec<Expression>, pub expressions: Vec<Expression>,
...@@ -51,7 +51,7 @@ impl Deref for ExpressionList { ...@@ -51,7 +51,7 @@ impl Deref for ExpressionList {
} }
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr( #[cfg_attr(
feature = "serde", feature = "serde",
derive(serde::Serialize, serde::Deserialize), derive(serde::Serialize, serde::Deserialize),
...@@ -144,7 +144,7 @@ impl Display for BinaryOp { ...@@ -144,7 +144,7 @@ impl Display for BinaryOp {
} }
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr( #[cfg_attr(
feature = "serde", feature = "serde",
derive(serde::Serialize, serde::Deserialize), derive(serde::Serialize, serde::Deserialize),
...@@ -158,14 +158,14 @@ pub enum VoidExpression { ...@@ -158,14 +158,14 @@ pub enum VoidExpression {
} }
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct GroupedExpression { pub struct GroupedExpression {
pub inner: Box<ValueExpression>, pub inner: Box<ValueExpression>,
} }
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr( #[cfg_attr(
feature = "serde", feature = "serde",
derive(serde::Serialize, serde::Deserialize), derive(serde::Serialize, serde::Deserialize),
...@@ -194,12 +194,12 @@ pub enum ValueExpression { ...@@ -194,12 +194,12 @@ pub enum ValueExpression {
} }
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TypeofValue(pub Box<ValueExpression>); pub struct TypeofValue(pub Box<ValueExpression>);
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ConditionalLoop { pub struct ConditionalLoop {
pub block: GuardedBlock, pub block: GuardedBlock,
...@@ -222,7 +222,7 @@ impl ConditionalLoop { ...@@ -222,7 +222,7 @@ impl ConditionalLoop {
} }
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Conditional { pub struct Conditional {
pub blocks: Vec<GuardedBlock>, pub blocks: Vec<GuardedBlock>,
...@@ -230,7 +230,7 @@ pub struct Conditional { ...@@ -230,7 +230,7 @@ pub struct Conditional {
} }
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct GuardedBlock { pub struct GuardedBlock {
pub guard: Box<ValueExpression>, pub guard: Box<ValueExpression>,
...@@ -238,7 +238,7 @@ pub struct GuardedBlock { ...@@ -238,7 +238,7 @@ pub struct GuardedBlock {
} }
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Import { pub struct Import {
pub source: String, pub source: String,
...@@ -246,14 +246,14 @@ pub struct Import { ...@@ -246,14 +246,14 @@ pub struct Import {
} }
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Export { pub struct Export {
pub items: IdentifierList, pub items: IdentifierList,
} }
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Print { pub struct Print {
pub expr: Box<ValueExpression>, pub expr: Box<ValueExpression>,
...@@ -270,7 +270,7 @@ pub type IdentifierList = Vec<IdentifierNode>; ...@@ -270,7 +270,7 @@ pub type IdentifierList = Vec<IdentifierNode>;
pub type ParameterList = Vec<ValueExpression>; pub type ParameterList = Vec<ValueExpression>;
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Identifier(pub String); pub struct Identifier(pub String);
impl Display for Identifier { impl Display for Identifier {
...@@ -282,7 +282,7 @@ impl Display for Identifier { ...@@ -282,7 +282,7 @@ impl Display for Identifier {
/// Alias an identifier, to create a new way of referring to it /// Alias an identifier, to create a new way of referring to it
/// IdentifierAlias(original, alias) => identifier "as" alias /// IdentifierAlias(original, alias) => identifier "as" alias
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct IdentifierAlias(pub String, pub String); pub struct IdentifierAlias(pub String, pub String);
impl Display for IdentifierAlias { impl Display for IdentifierAlias {
...@@ -292,7 +292,7 @@ impl Display for IdentifierAlias { ...@@ -292,7 +292,7 @@ impl Display for IdentifierAlias {
} }
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr( #[cfg_attr(
feature = "serde", feature = "serde",
derive(serde::Serialize, serde::Deserialize), derive(serde::Serialize, serde::Deserialize),
...@@ -328,7 +328,7 @@ impl IdentifierNode { ...@@ -328,7 +328,7 @@ impl IdentifierNode {
} }
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr( #[cfg_attr(
feature = "serde", feature = "serde",
derive(serde::Serialize, serde::Deserialize), derive(serde::Serialize, serde::Deserialize),
...@@ -353,7 +353,7 @@ impl Display for LiteralNode { ...@@ -353,7 +353,7 @@ impl Display for LiteralNode {
} }
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Assignment { pub struct Assignment {
pub ident: Identifier, pub ident: Identifier,
...@@ -361,7 +361,7 @@ pub struct Assignment { ...@@ -361,7 +361,7 @@ pub struct Assignment {
} }
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr( #[cfg_attr(
feature = "serde", feature = "serde",
derive(serde::Serialize, serde::Deserialize), derive(serde::Serialize, serde::Deserialize),
...@@ -373,7 +373,7 @@ pub enum DeclareIdent { ...@@ -373,7 +373,7 @@ pub enum DeclareIdent {
} }
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DeclareFunction { pub struct DeclareFunction {
pub ident: Identifier, pub ident: Identifier,
...@@ -382,14 +382,25 @@ pub struct DeclareFunction { ...@@ -382,14 +382,25 @@ pub struct DeclareFunction {
} }
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "debug-ast", derive(Debug))] #[cfg_attr(any(feature = "debug-ast", test), derive(Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FunctionCall { pub struct FunctionCall {
pub name: Identifier, pub name: Identifier,
pub params: ParameterList, pub params: ParameterList,
} }
macro_rules! impl_into_ast { macro_rules! impl_into_void_expr {
($($type: ty => $variant: expr),+) => {
$(
impl From<$type> for VoidExpression {
fn from(other: $type) -> Self {
$variant(other)
}
}
)+
};
}
macro_rules! impl_into_value_expr {
($($type: ty => $variant: expr),+) => { ($($type: ty => $variant: expr),+) => {
$( $(
impl From<$type> for ValueExpression { impl From<$type> for ValueExpression {
...@@ -401,7 +412,7 @@ macro_rules! impl_into_ast { ...@@ -401,7 +412,7 @@ macro_rules! impl_into_ast {
}; };
} }
impl_into_ast!( impl_into_value_expr!(
GroupedExpression => ValueExpression::Grouped, GroupedExpression => ValueExpression::Grouped,
ExpressionList => ValueExpression::Block, ExpressionList => ValueExpression::Block,
LiteralNode => ValueExpression::Literal, LiteralNode => ValueExpression::Literal,
...@@ -413,3 +424,10 @@ impl_into_ast!( ...@@ -413,3 +424,10 @@ impl_into_ast!(
DeclareFunction => ValueExpression::DeclareFunction, DeclareFunction => ValueExpression::DeclareFunction,
TypeofValue => ValueExpression::Typeof TypeofValue => ValueExpression::Typeof
); );
impl_into_void_expr!(
Print => VoidExpression::Print,
ConditionalLoop => VoidExpression::ConditionLoop,
Import => VoidExpression::Import,
Export => VoidExpression::Export
);
...@@ -4,17 +4,78 @@ use crate::runtime::numbers::Number; ...@@ -4,17 +4,78 @@ use crate::runtime::numbers::Number;
grammar<'a>; grammar<'a>;
pub Program: Program = {
ExpressionList => Program(<>)
};
Block: Option<ExpressionList> = {
"{" <ExpressionList?> "}" => <>,
};
ExpressionList: ExpressionList = {
<mut v:(<Expression> ";")+> <e:Expression?> => {
match e {
Some(val) => {
v.push(val);
ExpressionList {
expressions: v,
is_void: false,
}
},
None => {
ExpressionList {
expressions: v,
is_void: true,
}
},
}
}
};
pub Expression: Expression = { pub Expression: Expression = {
ValueExpression => Expression::Value(<>), ValueExpression => Expression::Value(<>),
} VoidExpression => Expression::Void(<>),
};
VoidExpression: VoidExpression = {
"print" <expr:ValueExpression> => Print { expr: Box::new(expr) }.into(),
"import" "{" <items:IdentifierList> "}" "from" <source:StringValue> => Import { source, items }.into(),
"export" "{" <items:IdentifierList> "}" => Export { items }.into(),
};
ValueExpression: ValueExpression = { ValueExpression: ValueExpression = {
#[precedence(level="1")] #[precedence(level="1")]
Literal => ValueExpression::Literal(<>), Literal => ValueExpression::Literal(<>),
#[precedence(level="1")] #[precedence(level="1")]
"(" <expr:ValueExpression> ")" => GroupedExpression { inner: Box::new(expr) }.into(),
#[precedence(level="2")] #[assoc(side="right")]
"typeof" <expr:ValueExpression> => TypeofValue(Box::new(expr)).into(), "typeof" <expr:ValueExpression> => TypeofValue(Box::new(expr)).into(),
#[precedence(level="2")] #[assoc(side="right")]
"-" <expr:ValueExpression> => ValueExpression::Unary { operand: Box::new(expr), operator: UnaryOp::Negate },
#[precedence(level="2")] #[assoc(side="right")]
"!" <expr:ValueExpression> => ValueExpression::Unary { operand: Box::new(expr), operator: UnaryOp::Not },
#[precedence(level = "3")]
IfStatement => <>.into(),
#[precedence(level="4")] #[assoc(side="left")]
<lhs:ValueExpression> "*" <rhs:ValueExpression> => {
ValueExpression::Binary {
lhs: Box::new(lhs),
rhs: Box::new(rhs),
operator: BinaryOp::Multiply,
}
},
#[precedence(level="4")] #[assoc(side="left")] #[precedence(level="4")] #[assoc(side="left")]
<lhs:ValueExpression> "/" <rhs:ValueExpression> => {
ValueExpression::Binary {
lhs: Box::new(lhs),
rhs: Box::new(rhs),
operator: BinaryOp::Divide,
}
},
#[precedence(level="6")] #[assoc(side="left")]
<lhs:ValueExpression> "+" <rhs:ValueExpression> => { <lhs:ValueExpression> "+" <rhs:ValueExpression> => {
ValueExpression::Binary { ValueExpression::Binary {
lhs: Box::new(lhs), lhs: Box::new(lhs),
...@@ -22,7 +83,7 @@ ValueExpression: ValueExpression = { ...@@ -22,7 +83,7 @@ ValueExpression: ValueExpression = {
operator: BinaryOp::Add, operator: BinaryOp::Add,
} }
}, },
#[precedence(level="4")] #[assoc(side="left")] #[precedence(level="6")] #[assoc(side="left")]
<lhs:ValueExpression> "-" <rhs:ValueExpression> => { <lhs:ValueExpression> "-" <rhs:ValueExpression> => {
ValueExpression::Binary { ValueExpression::Binary {
lhs: Box::new(lhs), lhs: Box::new(lhs),
...@@ -30,7 +91,49 @@ ValueExpression: ValueExpression = { ...@@ -30,7 +91,49 @@ ValueExpression: ValueExpression = {
operator: BinaryOp::Subtract, operator: BinaryOp::Subtract,
} }
}, },
}
#[precedence(level="1")]
Identifier => <>.into()
};
IfStatement: Conditional = {
BareIfStatement => Conditional { blocks: vec![<>], fallback: None },
<fi: BareIfStatement> "else" <bl:Block> => Conditional { blocks: vec![fi], fallback: bl },
<fi:BareIfStatement> "else" <ls:IfStatement> => {
let mut ls = ls;
let mut new = vec![fi];
new.append(&mut ls.blocks);
Conditional {
blocks: new,
fallback: ls.fallback,
}
}
};
BareIfStatement: GuardedBlock = {
"if" <guard:ValueExpression> <bl:Block> => {
GuardedBlock {
block: bl.unwrap_or_default(),
guard: Box::new(guard),
}
}
};
ParameterList = ListOf<ValueExpression>;
IdentifierList = ListOf<IdentifierNode>;
IdentifierNode: IdentifierNode = {
IdentifierAlias => IdentifierNode::Alias(<>),
Identifier => IdentifierNode::Direct(<>),
};
IdentifierAlias: IdentifierAlias = {
<base:Identifier> "as" <alias:Identifier> => IdentifierAlias(base.0, alias.0),
};
Identifier: Identifier = {
"identifier" => Identifier(String::from(<>))
};
Literal: LiteralNode = { Literal: LiteralNode = {
"boolean" => LiteralNode::Boolean(<>), "boolean" => LiteralNode::Boolean(<>),
...@@ -39,12 +142,22 @@ Literal: LiteralNode = { ...@@ -39,12 +142,22 @@ Literal: LiteralNode = {
"integer" => LiteralNode::Number(Number::Integer(<>)), "integer" => LiteralNode::Number(Number::Integer(<>)),
"string" => LiteralNode::String(String::from(<>)), "string" => LiteralNode::String(String::from(<>)),
"owned_string" => LiteralNode::String(<>), "owned_string" => LiteralNode::String(<>),
} };
StringValue: String = { StringValue: String = {
"string" => String::from(<>), "string" => String::from(<>),
"owned_string" => <>, "owned_string" => <>,
} };
ListOf<T>: Vec<T> = {
<mut v:(<T> ",")+> <e:T?> => match e {
None => v,
Some(e) => {
v.push(e);
v
}
}
};
extern { extern {
type Location = usize; type Location = usize;
......
// use lalrpop_util::lalrpop_mod;
// lalrpop_mod!(pub forge_script);
use crate::error::ForgeResult;
use crate::lexer::{script_to_tokens, ScriptTokenType};
use crate::parser::ast::{Expression, Program};
use crate::TokenError;
use peg::Parse;
pub type InputSpan<'a, Loc, Tok> = Result<(Loc, Tok, Loc), TokenError<'a>>;
type ExprSpan<'a> = InputSpan<'a, usize, ScriptTokenType<'a>>;
macro_rules! export_grammar_fn {
($name:ident = $output:ty => $part: tt) => {
pub fn $name(source: &str) -> ForgeResult<$output> {
let tokens = script_to_tokens(source)?
.iter()
.map(|tok| {
Ok((
tok.position.start(),
tok.token_type.clone(),
tok.position.start() + tok.position.len(),
))
})
.collect::<Vec<ExprSpan>>();
let value =
super::forge_grammar::$part::new().parse::<ExprSpan, Vec<ExprSpan>>(tokens)?;
Ok(value)
}
};
}
export_grammar_fn!(parse_program = Program => ProgramParser);
export_grammar_fn!(parse_expression = Expression => ExpressionParser);
// pub fn parse_expression(source: &str) -> ForgeResult<Expression> {
// let tokens = script_to_tokens(source)?
// .iter()
// .map(|tok| {
// Ok((
// tok.position.start(),
// tok.token_type.clone(),
// tok.position.start() + tok.position.len(),
// ))
// })
// .collect::<Vec<ExprSpan>>();
//
// let value =
// super::forge_grammar::ExpressionParser::new().parse::<ExprSpan, Vec<ExprSpan>>(tokens)?;
//
// Ok(value)
// }
#[cfg(test)]
mod grammar_test {
use super::parse_expression;
use crate::error::ForgeResult;
use crate::parse::ast::Expression;
use test_case::test_case;
#[test_case("123" => matches Ok(_) ; "Parse literal number")]
#[test_case(r#""Basic String""# => matches Ok(_) ; "Parse literal string")]
#[test_case("false" => matches Ok(_) ; "Parse literal false")]
#[test_case("true" => matches Ok(_) ; "Parse literal true")]
#[test_case("null" => matches Ok(_) ; "Parse literal null")]
fn expression_parsing<'a>(prog: &'a str) -> ForgeResult<'a, Expression> {
parse_expression(prog)
}
}
// use lalrpop_util::lalrpop_mod;
// lalrpop_mod!(pub forge_script);
use crate::lexer::{ScriptToken, ScriptTokenType};
use crate::TokenError;
use peg::Parse;
use std::slice::Iter;
pub struct TokenInput<'a>(std::slice::Iter<'a, ScriptToken<'a>>);
impl<'a> From<Iter<'a, ScriptToken<'a>>> for TokenInput<'a> {
fn from(value: Iter<'a, ScriptToken<'a>>) -> Self {
TokenInput(value)
}
}
pub type InputSpan<'a, Loc, Tok> = Result<(Loc, Tok, Loc), TokenError<'a>>;
impl<'a> Iterator for TokenInput<'a> {
type Item = InputSpan<'a, usize, ScriptTokenType<'a>>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|tok| {
Ok((
tok.position.start(),
tok.token_type.clone(),
tok.position.start() + tok.position.len(),
))
})
}
}
#[cfg(test)]
mod grammar_test {
use crate::lexer::{script_to_tokens, ScriptTokenType};
use crate::parser::lr_grammar::{InputSpan, TokenInput};
#[test]
fn basic_parsing() {
let code = script_to_tokens("4 + 4").expect("Failed to parse");
crate::parser::forge_script::ExpressionParser::new()
.parse::<InputSpan<usize, ScriptTokenType>, TokenInput>(code.iter().into())
.expect("Failed to boop");
}
}
pub mod ast; pub mod ast;
mod atoms; mod atoms;
mod forge_script;
mod grammar; mod grammar;
mod lr_grammar;
#[cfg(test)] #[cfg(test)]
mod test_suite; mod test_suite;
mod forge_script; pub(crate) mod forge_grammar;
use crate::error::{ForgeError, ForgeErrorKind, ForgeResult}; use crate::error::{ForgeError, ForgeErrorKind, ForgeResult};
use crate::print_forge_error; use crate::print_forge_error;
...@@ -15,44 +15,47 @@ pub fn slice<'a>(toks: &'a [crate::lexer::ScriptToken]) -> TokenSlice<'a> { ...@@ -15,44 +15,47 @@ pub fn slice<'a>(toks: &'a [crate::lexer::ScriptToken]) -> TokenSlice<'a> {
TokenSlice(toks) TokenSlice(toks)
} }
pub fn parse_expression(expr: &str) -> ForgeResult<ast::Expression> { pub use forge_script::parse_expression;
let tokens = crate::lexer::script_to_tokens(expr)?; pub use forge_script::parse_program;
let result = match grammar::expression(&TokenSlice(tokens.as_slice())) {
Ok(expr) => Ok(expr),
Err(parse_error) => {
let bad_token = &tokens[parse_error.location];
Err(ForgeError {
kind: ForgeErrorKind::UnexpectedToken {
found: bad_token.clone(),
expected: parse_error.expected,
},
})
}
};
result.map_err(|e| { // pub fn parse_expression(expr: &str) -> ForgeResult<ast::Expression> {
print_forge_error(expr, &e); // let tokens = crate::lexer::script_to_tokens(expr)?;
e // let result = match grammar::expression(&TokenSlice(tokens.as_slice())) {
}) // Ok(expr) => Ok(expr),
} // Err(parse_error) => {
// let bad_token = &tokens[parse_error.location];
// Err(ForgeError {
// kind: ForgeErrorKind::UnexpectedToken {
// found: bad_token.clone(),
// expected: parse_error.expected,
// },
// })
// }
// };
//
// result.map_err(|e| {
// print_forge_error(expr, &e);
// e
// })
// }
pub fn parse_program(prog: &str) -> ForgeResult<ast::Program> { // pub fn parse_program(prog: &str) -> ForgeResult<ast::Program> {
let tokens = crate::lexer::script_to_tokens(prog)?; // let tokens = crate::lexer::script_to_tokens(prog)?;
let result = match grammar::program(&TokenSlice(tokens.as_slice())) { // let result = match grammar::program(&TokenSlice(tokens.as_slice())) {
Ok(prog) => Ok(prog.clone()), // Ok(prog) => Ok(prog.clone()),
Err(parse_error) => { // Err(parse_error) => {
let bad_token = &tokens[parse_error.location]; // let bad_token = &tokens[parse_error.location];
Err(ForgeError { // Err(ForgeError {
kind: ForgeErrorKind::UnexpectedToken { // kind: ForgeErrorKind::UnexpectedToken {
found: bad_token.clone(), // found: bad_token.clone(),
expected: parse_error.expected, // expected: parse_error.expected,
}, // },
}) // })
} // }
}; // };
//
result.map_err(|e| { // result.map_err(|e| {
print_forge_error(prog, &e); // print_forge_error(prog, &e);
e // e
}) // })
} // }
...@@ -42,3 +42,23 @@ macro_rules! deref_as { ...@@ -42,3 +42,23 @@ macro_rules! deref_as {
pub trait Clown<Target> { pub trait Clown<Target> {
fn clown(&self) -> Target; fn clown(&self) -> Target;
} }
type LineNum = usize;
type ColumnNum = usize;
pub fn offset_to_line_column(source: &str, offset: usize) -> (LineNum, ColumnNum) {
let mut remaining = offset;
let mut current_line = 0;
for line in source.lines() {
current_line += 1;
if remaining < line.len() {
return (current_line, remaining + 1);
}
remaining -= line.len();
}
(
current_line,
source.lines().last().map(|l| l.len()).unwrap_or(0),
)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment