klang 0.1.0

The Klang programming language
Documentation
use super::errors::ParseError;
use super::ir::{
    line::LineKind, text_part::PartKind, Command, Function, FunctionArg, FunctionCall, Line,
    Program, TextPart, TextWithArgs,
};
use super::structs::Rule;
use crate::parser::passes::ir_to_ast;
use crate::parser::KlangProgram;
use pest::iterators::Pair;

pub fn parse_program(pair: pest::iterators::Pair<Rule>) -> Result<KlangProgram, ParseError> {
    let mut all_lines = Vec::new();

    for line_pair in pair.into_inner() {
        match line_pair.as_rule() {
            Rule::line => match parse_line(line_pair) {
                Ok(lines) => all_lines.extend(lines),
                Err(e) => return Err(e),
            },
            Rule::EOI => break,
            _ => {
                return Err(ParseError::from_pair(
                    format!("Unknown rule: {:?}", line_pair.as_rule()),
                    line_pair,
                ));
            }
        }
    }

    let ir_program = Program { lines: all_lines };
    let ast_program = ir_to_ast(&ir_program)?;

    Ok(KlangProgram::from_ast(&ast_program))
}

fn parse_line(line: Pair<Rule>) -> Result<Vec<Line>, ParseError> {
    line.into_inner()
        .filter_map(|line_pair| match line_pair.as_rule() {
            Rule::function_def => match parse_function_def(line_pair) {
                Ok(func) => Some(Ok(Line {
                    line_kind: Some(LineKind::Function(func)),
                })),
                Err(e) => Some(Err(e)),
            },
            Rule::function_call => match parse_function_call(line_pair) {
                Ok(call) => Some(Ok(Line {
                    line_kind: Some(LineKind::FunctionCall(call)),
                })),
                Err(e) => Some(Err(e)),
            },
            Rule::command => match parse_command(line_pair) {
                Ok(cmd) => Some(Ok(Line {
                    line_kind: Some(LineKind::Command(cmd)),
                })),
                Err(e) => Some(Err(e)),
            },
            Rule::empty_line => None,
            _ => Some(Err(ParseError::from_pair(
                format!("Unknown rule: {:?}", line_pair.as_rule()),
                line_pair,
            ))),
        })
        .collect()
}

fn parse_function_def(pair: Pair<Rule>) -> Result<Function, ParseError> {
    let text_with_args = parse_text_with_args(pair.clone().into_inner().next().unwrap())?;
    let children: Result<Vec<Line>, ParseError> = pair
        .into_inner()
        .skip(1)
        .map(parse_line)
        .try_fold(Vec::new(), |mut acc, result| {
            acc.extend(result?);
            Ok(acc)
        });

    Ok(Function {
        name: Some(text_with_args),
        lines: children?,
    })
}

fn parse_function_call(pair: Pair<Rule>) -> Result<FunctionCall, ParseError> {
    Ok(FunctionCall {
        name: Some(parse_text_with_args(pair.into_inner().next().unwrap())?),
    })
}

fn parse_command(pair: Pair<Rule>) -> Result<Command, ParseError> {
    Ok(Command {
        text: Some(parse_text_with_args(pair.into_inner().next().unwrap())?),
    })
}

fn parse_text_with_args(pair: Pair<Rule>) -> Result<TextWithArgs, ParseError> {
    match pair.as_rule() {
        Rule::text_with_function_args | Rule::text_with_function_params => {
            let parts = pair
                .into_inner()
                .map(parse_text_part)
                .collect::<Result<Vec<TextPart>, ParseError>>()?;
            Ok(TextWithArgs { parts })
        }
        _ => Err(ParseError::from_pair(
            format!("Expected text with args, got {:?}", pair.as_rule()),
            pair,
        )),
    }
}

fn parse_text_part(pair: Pair<Rule>) -> Result<TextPart, ParseError> {
    match pair.as_rule() {
        Rule::text => Ok(TextPart {
            part_kind: Some(PartKind::Text(pair.as_str().to_string())),
        }),
        Rule::function_arg | Rule::function_param => {
            let func_arg = FunctionArg {
                text: pair.into_inner().next().unwrap().as_str().to_string(),
            };
            Ok(TextPart {
                part_kind: Some(PartKind::FunctionArg(func_arg)),
            })
        }
        _ => Err(ParseError::from_pair(
            format!("Expected text or function arg, got {:?}", pair.as_rule()),
            pair,
        )),
    }
}