add: compiler
This commit is contained in:
651
src/ast.rs
Normal file
651
src/ast.rs
Normal file
@@ -0,0 +1,651 @@
|
||||
//! ast.rs — Typed Abstract Syntax Tree for RPG IV free-format programs.
|
||||
//!
|
||||
//! This module defines the in-memory representation produced by the lowering
|
||||
//! pass (`lower.rs`) and consumed by the LLVM code-generator (`codegen.rs`).
|
||||
//!
|
||||
//! Only the subset of the language that is needed to compile `hello.rpg` (and
|
||||
//! small programs like it) is fully fleshed out. Everything else is kept as
|
||||
//! placeholder variants so the lowering pass can represent the whole parse tree
|
||||
//! without panicking, and the codegen can skip unimplemented nodes gracefully.
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Top-level program
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// A complete RPG IV source file.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Program {
|
||||
/// Zero or more top-level declarations (CTL-OPT, DCL-S, DCL-C, DCL-DS,
|
||||
/// file declarations, subroutines …).
|
||||
pub declarations: Vec<Declaration>,
|
||||
/// Zero or more procedure definitions (`DCL-PROC … END-PROC`).
|
||||
pub procedures: Vec<Procedure>,
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Declarations
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Declaration {
|
||||
/// `CTL-OPT keyword-list;`
|
||||
ControlSpec(ControlSpec),
|
||||
/// `DCL-S name type [keywords];`
|
||||
Standalone(StandaloneDecl),
|
||||
/// `DCL-C name literal;` or `DCL-C name CONST(literal);`
|
||||
Constant(ConstantDecl),
|
||||
/// `DCL-C name *named-constant;`
|
||||
NamedConstantDecl(NamedConstantDecl),
|
||||
/// `DCL-DS name … END-DS;`
|
||||
DataStructure(DataStructureDecl),
|
||||
/// `DCL-F name …;`
|
||||
File(FileDecl),
|
||||
/// `BEG-SR name; … END-SR;`
|
||||
Subroutine(Subroutine),
|
||||
}
|
||||
|
||||
// ── Control spec ──────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ControlSpec {
|
||||
pub keywords: Vec<CtlKeyword>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CtlKeyword {
|
||||
DftActGrp(bool), // *YES / *NO
|
||||
NoMain,
|
||||
Main(String),
|
||||
Other(String), // catch-all for keywords we don't generate code for
|
||||
}
|
||||
|
||||
// ── Standalone variable ───────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StandaloneDecl {
|
||||
pub name: String,
|
||||
pub ty: TypeSpec,
|
||||
pub keywords: Vec<VarKeyword>,
|
||||
}
|
||||
|
||||
// ── Constant declaration ──────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConstantDecl {
|
||||
pub name: String,
|
||||
pub value: Literal,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NamedConstantDecl {
|
||||
pub name: String,
|
||||
pub value: NamedConstant,
|
||||
}
|
||||
|
||||
// ── Data structure ────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DataStructureDecl {
|
||||
pub name: String,
|
||||
pub keywords: Vec<DsKeyword>,
|
||||
pub fields: Vec<DsField>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum DsKeyword {
|
||||
Qualified,
|
||||
Template,
|
||||
Other(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DsField {
|
||||
pub name: String,
|
||||
pub ty: TypeSpec,
|
||||
pub keywords: Vec<VarKeyword>,
|
||||
}
|
||||
|
||||
// ── File declaration ──────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FileDecl {
|
||||
pub name: String,
|
||||
pub keywords: Vec<String>, // simplified — not code-gen'd
|
||||
}
|
||||
|
||||
// ── Subroutine ────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Subroutine {
|
||||
pub name: String,
|
||||
pub body: Vec<Statement>,
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Type specifications
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum TypeSpec {
|
||||
/// `CHAR(n)` — fixed-length character field.
|
||||
Char(Box<Expression>),
|
||||
/// `VARCHAR(n)` — variable-length character.
|
||||
VarChar(Box<Expression>),
|
||||
/// `INT(n)` — signed integer (n = 3, 5, 10, or 20).
|
||||
Int(Box<Expression>),
|
||||
/// `UNS(n)` — unsigned integer.
|
||||
Uns(Box<Expression>),
|
||||
/// `FLOAT(n)` — floating-point.
|
||||
Float(Box<Expression>),
|
||||
/// `PACKED(digits:decimals)`
|
||||
Packed(Box<Expression>, Box<Expression>),
|
||||
/// `ZONED(digits:decimals)`
|
||||
Zoned(Box<Expression>, Box<Expression>),
|
||||
/// `BINDEC(digits:decimals)`
|
||||
Bindec(Box<Expression>, Box<Expression>),
|
||||
/// `IND` — indicator (boolean).
|
||||
Ind,
|
||||
/// `DATE [(*fmt)]`
|
||||
Date,
|
||||
/// `TIME [(*fmt)]`
|
||||
Time,
|
||||
/// `TIMESTAMP`
|
||||
Timestamp,
|
||||
/// `POINTER`
|
||||
Pointer,
|
||||
/// `LIKE(name)`
|
||||
Like(String),
|
||||
/// `LIKEDS(name)`
|
||||
LikeDs(String),
|
||||
/// Unrecognised / not yet implemented type.
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl TypeSpec {
|
||||
/// Return the number of bytes this type occupies at runtime on a 64-bit
|
||||
/// Linux host. Returns `None` for types whose size is not statically known.
|
||||
pub fn byte_size(&self) -> Option<u64> {
|
||||
match self {
|
||||
TypeSpec::Char(expr) | TypeSpec::VarChar(expr) => {
|
||||
if let Expression::Literal(Literal::Integer(n)) = expr.as_ref() {
|
||||
Some(*n as u64)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
TypeSpec::Int(expr) | TypeSpec::Uns(expr) => {
|
||||
if let Expression::Literal(Literal::Integer(n)) = expr.as_ref() {
|
||||
Some(match n {
|
||||
3 => 1,
|
||||
5 => 2,
|
||||
10 => 4,
|
||||
20 => 8,
|
||||
_ => 8, // default to 8 bytes
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
TypeSpec::Float(expr) => {
|
||||
if let Expression::Literal(Literal::Integer(n)) = expr.as_ref() {
|
||||
Some(if *n <= 4 { 4 } else { 8 })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
TypeSpec::Ind => Some(1),
|
||||
TypeSpec::Pointer => Some(8),
|
||||
TypeSpec::Packed(digits, _) => {
|
||||
if let Expression::Literal(Literal::Integer(n)) = digits.as_ref() {
|
||||
Some((*n as u64 / 2) + 1)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Variable / declaration keywords
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum VarKeyword {
|
||||
/// `INZ` — default initialisation.
|
||||
Inz,
|
||||
/// `INZ(expr)` — explicit initialisation value.
|
||||
InzExpr(Expression),
|
||||
/// `INZ(*named-constant)` — initialise to named constant.
|
||||
InzNamed(NamedConstant),
|
||||
Static,
|
||||
Other(String),
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Procedures
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Procedure {
|
||||
pub name: String,
|
||||
pub exported: bool,
|
||||
pub pi: Option<PiSpec>,
|
||||
/// Local declarations (DCL-S, DCL-C, etc.) inside the procedure.
|
||||
pub locals: Vec<Declaration>,
|
||||
pub body: Vec<Statement>,
|
||||
}
|
||||
|
||||
/// Procedure Interface specification (`DCL-PI … END-PI`).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PiSpec {
|
||||
pub name: String,
|
||||
pub return_ty: Option<TypeSpec>,
|
||||
pub params: Vec<PiParam>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PiParam {
|
||||
pub name: String,
|
||||
pub ty: TypeSpec,
|
||||
pub keywords: Vec<ParamKeyword>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ParamKeyword {
|
||||
Value,
|
||||
Const,
|
||||
Other(String),
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Statements
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Statement {
|
||||
/// `lvalue = expr;` or `EVAL lvalue = expr;`
|
||||
Assign(AssignStmt),
|
||||
/// `IF expr; … [ELSEIF …] [ELSE …] ENDIF;`
|
||||
If(IfStmt),
|
||||
/// `DOW expr; … ENDDO;`
|
||||
DoWhile(DoWhileStmt),
|
||||
/// `DOU expr; … ENDDO;`
|
||||
DoUntil(DoUntilStmt),
|
||||
/// `FOR i = start TO/DOWNTO end [BY step]; … ENDFOR;`
|
||||
For(ForStmt),
|
||||
/// `SELECT; WHEN … [OTHER …] ENDSL;`
|
||||
Select(SelectStmt),
|
||||
/// `MONITOR; … ON-ERROR … ENDMON;`
|
||||
Monitor(MonitorStmt),
|
||||
/// `CALLP name(args);` or bare procedure call `name(args);`
|
||||
CallP(CallPStmt),
|
||||
/// `RETURN [expr];`
|
||||
Return(ReturnStmt),
|
||||
/// `LEAVE;`
|
||||
Leave,
|
||||
/// `ITER;`
|
||||
Iter,
|
||||
/// `LEAVESR;`
|
||||
LeaveSr,
|
||||
/// `EXSR name;`
|
||||
ExSr(String),
|
||||
/// `DSPLY expr;`
|
||||
Dsply(DsplyStmt),
|
||||
/// `RESET lvalue;` / `RESET *ALL;`
|
||||
Reset(ResetStmt),
|
||||
/// `CLEAR lvalue;`
|
||||
Clear(LValue),
|
||||
/// Any I/O statement (READ, WRITE, CHAIN, etc.) — kept as opaque for now.
|
||||
Io(IoStatement),
|
||||
/// Catch-all for statements not yet lowered.
|
||||
Unimplemented(String),
|
||||
}
|
||||
|
||||
// ── Assignment ────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AssignStmt {
|
||||
pub target: LValue,
|
||||
pub value: Expression,
|
||||
}
|
||||
|
||||
// ── If / ElseIf / Else ────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IfStmt {
|
||||
pub condition: Expression,
|
||||
pub then_body: Vec<Statement>,
|
||||
pub elseifs: Vec<ElseIf>,
|
||||
pub else_body: Option<Vec<Statement>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ElseIf {
|
||||
pub condition: Expression,
|
||||
pub body: Vec<Statement>,
|
||||
}
|
||||
|
||||
// ── DOW loop ──────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DoWhileStmt {
|
||||
pub condition: Expression,
|
||||
pub body: Vec<Statement>,
|
||||
}
|
||||
|
||||
// ── DOU loop ──────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DoUntilStmt {
|
||||
pub condition: Expression,
|
||||
pub body: Vec<Statement>,
|
||||
}
|
||||
|
||||
// ── FOR loop ──────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ForStmt {
|
||||
pub var: String,
|
||||
pub start: Expression,
|
||||
pub limit: Expression,
|
||||
pub step: Option<Expression>,
|
||||
pub downto: bool,
|
||||
pub body: Vec<Statement>,
|
||||
}
|
||||
|
||||
// ── SELECT / WHEN ─────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SelectStmt {
|
||||
pub whens: Vec<WhenClause>,
|
||||
pub other: Option<Vec<Statement>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WhenClause {
|
||||
pub condition: Expression,
|
||||
pub body: Vec<Statement>,
|
||||
}
|
||||
|
||||
// ── MONITOR ───────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MonitorStmt {
|
||||
pub body: Vec<Statement>,
|
||||
pub handlers: Vec<OnError>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OnError {
|
||||
pub codes: Vec<ErrorCode>,
|
||||
pub body: Vec<Statement>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ErrorCode {
|
||||
Integer(u32),
|
||||
Program,
|
||||
File,
|
||||
All,
|
||||
}
|
||||
|
||||
// ── CALLP ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CallPStmt {
|
||||
pub name: String,
|
||||
pub args: Vec<Arg>,
|
||||
}
|
||||
|
||||
// ── RETURN ────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ReturnStmt {
|
||||
pub value: Option<Expression>,
|
||||
}
|
||||
|
||||
// ── DSPLY ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DsplyStmt {
|
||||
/// The expression to display.
|
||||
pub expr: Expression,
|
||||
/// Optional message queue identifier (two-operand form).
|
||||
pub msg_q: Option<String>,
|
||||
pub response: Option<String>,
|
||||
}
|
||||
|
||||
// ── RESET ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ResetStmt {
|
||||
Target(LValue),
|
||||
All,
|
||||
}
|
||||
|
||||
// ── I/O (opaque) ──────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum IoStatement {
|
||||
Read { file: String },
|
||||
ReadP { file: String },
|
||||
Write { record: String },
|
||||
Update { record: String },
|
||||
Delete { key: Expression, file: String },
|
||||
Chain { key: Expression, file: String },
|
||||
SetLL { key: SetKey, file: String },
|
||||
SetGT { key: SetKey, file: String },
|
||||
Open { file: String },
|
||||
Close { file: Option<String> }, // None = *ALL
|
||||
Except { format: Option<String> },
|
||||
ExFmt { format: String },
|
||||
Post { file: String },
|
||||
Feod { file: String },
|
||||
Unlock { file: String },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SetKey {
|
||||
Expr(Expression),
|
||||
Start,
|
||||
End,
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// L-values
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// An assignable location.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum LValue {
|
||||
/// Simple or dotted name: `myVar` or `ds.field`.
|
||||
Name(QualifiedName),
|
||||
/// Array element: `arr(i)`.
|
||||
Index(QualifiedName, Vec<Expression>),
|
||||
}
|
||||
|
||||
impl LValue {
|
||||
/// Return the base name (first component of the qualified name).
|
||||
pub fn base_name(&self) -> &str {
|
||||
match self {
|
||||
LValue::Name(q) | LValue::Index(q, _) => &q.parts[0],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Expressions
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Expression {
|
||||
Literal(Literal),
|
||||
Named(NamedConstant),
|
||||
Special(SpecialValue),
|
||||
Variable(QualifiedName),
|
||||
/// Array / function-style subscript: `name(idx)`.
|
||||
Index(QualifiedName, Vec<Expression>),
|
||||
/// Procedure / built-in call as expression: `name(args)`.
|
||||
Call(String, Vec<Arg>),
|
||||
BuiltIn(BuiltIn),
|
||||
UnaryMinus(Box<Expression>),
|
||||
UnaryPlus(Box<Expression>),
|
||||
BinOp(BinOp, Box<Expression>, Box<Expression>),
|
||||
Not(Box<Expression>),
|
||||
Paren(Box<Expression>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum BinOp {
|
||||
Add, Sub, Mul, Div, Pow,
|
||||
Eq, Ne, Lt, Le, Gt, Ge,
|
||||
And, Or,
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Literals
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Literal {
|
||||
String(String),
|
||||
Integer(i64),
|
||||
Float(f64),
|
||||
Hex(Vec<u8>),
|
||||
/// `*ON` / `*OFF` as a literal.
|
||||
Indicator(bool),
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Named constants (`*ON`, `*OFF`, `*BLANK`, …)
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum NamedConstant {
|
||||
On,
|
||||
Off,
|
||||
Blank,
|
||||
Blanks,
|
||||
Zero,
|
||||
Zeros,
|
||||
HiVal,
|
||||
LoVal,
|
||||
Null,
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Special values (`*IN`, `*START`, …)
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum SpecialValue {
|
||||
/// `*IN(n)` — indicator by number.
|
||||
In(Box<Expression>),
|
||||
InAll,
|
||||
On,
|
||||
Off,
|
||||
Blank,
|
||||
Blanks,
|
||||
Zero,
|
||||
Zeros,
|
||||
HiVal,
|
||||
LoVal,
|
||||
Null,
|
||||
/// `*ALL'string'`
|
||||
All(String),
|
||||
Omit,
|
||||
This,
|
||||
Same,
|
||||
Start,
|
||||
End,
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Built-in functions
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// The RPG IV `%BUILTIN(…)` functions we actually lower to code.
|
||||
/// All others are wrapped in `Other`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum BuiltIn {
|
||||
/// `%LEN(identifier)` — byte length of a field.
|
||||
Len(Box<Expression>),
|
||||
/// `%TRIM(expr)` — trim leading and trailing blanks.
|
||||
Trim(Box<Expression>),
|
||||
/// `%TRIML(expr)` — trim leading blanks.
|
||||
TrimL(Box<Expression>),
|
||||
/// `%TRIMR(expr)` — trim trailing blanks.
|
||||
TrimR(Box<Expression>),
|
||||
/// `%CHAR(expr)` — convert to character string.
|
||||
Char(Box<Expression>),
|
||||
/// `%INT(expr)` — convert to integer.
|
||||
Int(Box<Expression>),
|
||||
/// `%DEC(expr:digits:decimals)` — convert to packed decimal.
|
||||
Dec(Box<Expression>, Box<Expression>, Box<Expression>),
|
||||
/// `%ABS(expr)` — absolute value.
|
||||
Abs(Box<Expression>),
|
||||
/// `%SQRT(expr)` — square root.
|
||||
Sqrt(Box<Expression>),
|
||||
/// `%EOF[(file)]`
|
||||
Eof(Option<String>),
|
||||
/// `%FOUND[(file)]`
|
||||
Found(Option<String>),
|
||||
/// `%ERROR()`
|
||||
Error,
|
||||
/// `%SUBST(str:start:len)` or `%SUBST(str:start)`.
|
||||
Subst(Box<Expression>, Box<Expression>, Option<Box<Expression>>),
|
||||
/// `%SCAN(pattern:source[:start])`.
|
||||
Scan(Box<Expression>, Box<Expression>, Option<Box<Expression>>),
|
||||
/// `%SIZE(identifier)`.
|
||||
Size(Box<Expression>),
|
||||
/// `%ADDR(identifier)`.
|
||||
Addr(Box<Expression>),
|
||||
/// `%ALLOC(size)`.
|
||||
Alloc(Box<Expression>),
|
||||
/// `%REM(a:b)`.
|
||||
Rem(Box<Expression>, Box<Expression>),
|
||||
/// `%DIV(a:b)`.
|
||||
Div(Box<Expression>, Box<Expression>),
|
||||
/// Any built-in we haven't individually modelled.
|
||||
Other(String, Vec<Expression>),
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Qualified names and argument lists
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// A dot-separated name: `ds.subDs.leaf`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct QualifiedName {
|
||||
pub parts: Vec<String>,
|
||||
}
|
||||
|
||||
impl QualifiedName {
|
||||
pub fn simple(name: impl Into<String>) -> Self {
|
||||
QualifiedName { parts: vec![name.into()] }
|
||||
}
|
||||
|
||||
pub fn is_simple(&self) -> bool {
|
||||
self.parts.len() == 1
|
||||
}
|
||||
|
||||
/// Return the leaf (last) component.
|
||||
pub fn leaf(&self) -> &str {
|
||||
self.parts.last().map(|s| s.as_str()).unwrap_or("")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for QualifiedName {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.parts.join("."))
|
||||
}
|
||||
}
|
||||
|
||||
/// A call argument.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Arg {
|
||||
Expr(Expression),
|
||||
Omit,
|
||||
}
|
||||
Reference in New Issue
Block a user