pliparser 0.1.1

Aleph Parser for PL/I : generate Aleph Syntax Tree.
Documentation
grammar;

use aleph_syntax_tree::syntax::AlephTree as at;

// --- Program ---
// A PL/I program consists of a list of declarations and statements.
pub Program: at = {
    <stmts:StatementList> => at::Module {
        name: "PLI_PROGRAM".to_string(),
        module_type: "program".to_string(),
        id: None,
        declarations: Vec::new(),
        body: Some(stmts),
        initialization: None,
    }
}

// --- Statement List ---
// A list of statements (can be empty).
pub StatementList: Vec<Box<at>> = {
    => Vec::new(),
    <stmts:NonEmptyStatementList> => stmts,
}

// --- Non-Empty Statement List ---
// A non-empty list of statements.
pub NonEmptyStatementList: Vec<Box<at>> = {
    <stmt:Statement> => vec![Box::new(stmt)],
    <stmts:NonEmptyStatementList> <stmt:Statement> => {
        let mut v = stmts;
        v.push(Box::new(stmt));
        v
    }
}

// --- Statement ---
// A single statement (assignment, procedure call, etc.).
pub Statement: at = {
    <stmt:Assignment> => stmt,
    <stmt:Call> => stmt,
    <stmt:IfStmt> => stmt,
    <stmt:Loop> => stmt,
    <stmt:DeclareStmt> => stmt,
}

// --- Declare Statement ---
// A declare statement: "DECLARE Name TYPE [INIT(InitialValue)];"
pub DeclareStmt: at = {
    "DECLARE" <name:Ident> <typ:Type> ";" => at::VarDecl {
        name: name,
        var_type: Some(Box::new(typ)),
        initial_value: None,
        is_constant: false,
        is_aliased: false,
        storage: None,
        occurs: None,
        usage: None,
        attributes: Vec::new(),
        level: None,
    },
    "DECLARE" <name:Ident> <typ:Type> "INIT" "(" <init:Expr> ")" ";" => at::VarDecl {
        name: name,
        var_type: Some(Box::new(typ)),
        initial_value: Some(Box::new(init)),
        is_constant: false,
        is_aliased: false,
        storage: None,
        occurs: None,
        usage: None,
        attributes: Vec::new(),
        level: None,
    }
}

// --- Type ---
// A type: "FIXED", "FLOAT", "CHAR", etc.
pub Type: at = {
    "FIXED" => at::TypeRef {
        name: "FIXED".to_string(),
        qualifiers: Vec::new(),
    },
    "FLOAT" => at::TypeRef {
        name: "FLOAT".to_string(),
        qualifiers: Vec::new(),
    },
    "CHAR" => at::TypeRef {
        name: "CHAR".to_string(),
        qualifiers: Vec::new(),
    },
    "BINARY" => at::TypeRef {
        name: "BINARY".to_string(),
        qualifiers: Vec::new(),
    }
}

// --- Assignment ---
// An assignment statement: "Target = Expression;"
pub Assignment: at = {
    <target:Ident> "=" <expr:Expr> ";" => at::Assignment {
        target: Box::new(at::Ident { value: target }),
        value: Box::new(expr),
    }
}

// --- Procedure Call ---
// A procedure call: "CALL Name [Parameters];"
pub Call: at = {
    "CALL" <name:Ident> ";" => at::Call {
        target: Box::new(at::Ident { value: name }),
        parameters: None,
        returning: None,
        on_error: None,
    },
    "CALL" <name:Ident> "(" <params:ParameterList> ")" ";" => at::Call {
        target: Box::new(at::Ident { value: name }),
        parameters: Some(params),
        returning: None,
        on_error: None,
    }
}

// --- Parameter List ---
// A list of parameters (can be empty).
pub ParameterList: Vec<Box<at>> = {
    => Vec::new(),
    <params:NonEmptyParameterList> => params,
}

// --- Non-Empty Parameter List ---
// A non-empty list of parameters.
pub NonEmptyParameterList: Vec<Box<at>> = {
    <param:Parameter> => vec![Box::new(param)],
    <params:NonEmptyParameterList> "," <param:Parameter> => {
        let mut v = params;
        v.push(Box::new(param));
        v
    }
}

// --- Parameter ---
// A single parameter: "Expression"
pub Parameter: at = {
    <expr:Expr> => expr,
}

// --- If Statement ---
// An if statement: "IF Condition THEN [Statements] [ELSE [Statements]];"
pub IfStmt: at = {
    "IF" <cond:Condition> "THEN" <then:StatementList> ";" => at::If {
        condition: Box::new(cond),
        then: Box::new(at::Block { statements: then }),
        els: Box::new(at::Unit),
    },
    "IF" <cond:Condition> "THEN" <then:StatementList> "ELSE" <els:StatementList> ";" => at::If {
        condition: Box::new(cond),
        then: Box::new(at::Block { statements: then }),
        els: Box::new(at::Block { statements: els }),
    }
}

// --- Loop Statement ---
// A loop statement: "DO [Statements] END;"
pub Loop: at = {
    "DO" <body:StatementList> "END" ";" => at::Loop {
        name: None,
        body: body,
    }
}

// --- Condition ---
// A condition: "Expression"
pub Condition: at = {
    <expr:Expr> => expr,
}

// --- Expression ---
// An expression (literal, identifier, operation, etc.).
pub Expr: at = {
    <expr:AddExpr> => expr,
}

// --- AddExpr ---
// Addition and subtraction expressions.
pub AddExpr: at = {
    <expr:MulExpr> => expr,
    <left:AddExpr> "+" <right:MulExpr> => at::Add {
        number_expr1: Box::new(left),
        number_expr2: Box::new(right),
    },
    <left:AddExpr> "-" <right:MulExpr> => at::Sub {
        number_expr1: Box::new(left),
        number_expr2: Box::new(right),
    },
}

// --- MulExpr ---
// Multiplication and division expressions.
pub MulExpr: at = {
    <expr:EqExpr> => expr,
    <left:MulExpr> "*" <right:EqExpr> => at::Mul {
        number_expr1: Box::new(left),
        number_expr2: Box::new(right),
    },
    <left:MulExpr> "/" <right:EqExpr> => at::Div {
        number_expr1: Box::new(left),
        number_expr2: Box::new(right),
    },
}

// --- EqExpr ---
// Equality expressions.
pub EqExpr: at = {
    <expr:PrimaryExpr> => expr,
    <left:EqExpr> "=" <right:PrimaryExpr> => at::Eq {
        expr1: Box::new(left),
        expr2: Box::new(right),
    },
    <left:EqExpr> "<>" <right:PrimaryExpr> => at::NotEq {
        expr1: Box::new(left),
        expr2: Box::new(right),
    },
}

// --- PrimaryExpr ---
// Primary expressions (literals, identifiers, parentheses).
pub PrimaryExpr: at = {
    <lit:Literal> => lit,
    <id:Ident> => at::Ident { value: id },
    "(" <expr:Expr> ")" => expr,
}

// --- Literal ---
// A literal value (integer, string, etc.).
pub Literal: at = {
    <lit:Int> => at::Int { value: lit },
    <lit:String> => at::String { value: lit },
    <lit:Bool> => at::Bool { value: lit },
}

// --- Integer ---
// An integer literal.
pub Int: String = {
    r"-?[0-9]+" => <>.to_string(),
};

// --- String ---
// A string literal.
pub String: String = {
    r#"'[^']*'"# => <>.to_string().trim_matches('\'').to_string(),
};

// --- Boolean ---
// A boolean literal.
pub Bool: String = {
    "'1'B" => "true".to_string(),
    "'0'B" => "false".to_string(),
    "TRUE" => "true".to_string(),
    "FALSE" => "false".to_string(),
};

// --- Identifier ---
// An identifier.
pub Ident: String = {
    r#"[A-Za-z_][A-Za-z0-9_]*"# => <>.to_string(),
};