diff --git a/src/ast.rs b/src/ast.rs index 9805edd..30091ef 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,420 +1,22 @@ -use std::fmt; +mod basic; +mod call; +mod expr; +mod field; +mod lit; +mod table_constr; +mod table_destr; +mod var; use crate::span::{HasSpan, Span}; -#[derive(Clone)] -pub struct Space { - pub comment: Vec<(String, Span)>, - pub span: Span, -} - -impl fmt::Debug for Space { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Space").finish() - } -} - -impl HasSpan for Space { - fn span(&self) -> Span { - self.span - } -} - -#[derive(Clone)] -pub struct Ident { - pub name: String, - pub span: Span, -} - -impl fmt::Debug for Ident { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "i#{}", self.name) - } -} - -impl HasSpan for Ident { - fn span(&self) -> Span { - self.span - } -} - -#[derive(Clone)] -pub enum NumLitStr { - /// - `0b_0001_1011` - /// - `0b10` - Bin(String), - - /// - `12_345` - /// - `7` - Dec(String), - - /// - `0x_c0_f3` - /// - `0xB` - Hex(String), -} - -impl fmt::Debug for NumLitStr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Bin(str) => write!(f, "0b{str}"), - Self::Dec(str) => write!(f, "{str}"), - Self::Hex(str) => write!(f, "0x{str}"), - } - } -} - -/// Positive number literal. -/// -/// Possible bases are binary, decimal, hexadecimal. Underscores can be inserted -/// before and after any digit. -#[derive(Clone)] -pub struct NumLit { - pub value: i64, - pub str: NumLitStr, - pub span: Span, -} - -impl fmt::Debug for NumLit { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.str.fmt(f) - } -} - -impl HasSpan for NumLit { - fn span(&self) -> Span { - self.span - } -} - -#[derive(Debug, Clone)] -pub enum StringLit { - /// - `"Hello world\n"` - /// - `""` - Inline(String, Span), - - /// ```text - /// """ - /// Hello, - /// world! - /// """ - /// ``` - Multiline(String, Span), -} - -impl HasSpan for StringLit { - fn span(&self) -> Span { - match self { - StringLit::Inline(_, span) => *span, - StringLit::Multiline(_, span) => *span, - } - } -} - -#[derive(Debug, Clone)] -pub enum TableLitElem { - /// `a` - Positional(Box), - - /// `foo: a` - /// - /// Structure: `name s0 : s1 value` - Named { - name: Ident, - s0: Space, - s1: Space, - value: Box, - }, -} - -impl HasSpan for TableLitElem { - fn span(&self) -> Span { - match self { - TableLitElem::Positional(value) => value.span(), - TableLitElem::Named { name, value, .. } => name.span().join(value.span()), - } - } -} - -/// `'{ a, foo: b }` -#[derive(Debug, Clone)] -pub struct TableLit { - pub elems: Vec<(Space, TableLitElem, Space)>, - /// `Some` if there is a trailing comma, `None` otherwise. - pub trailing_comma: Option, - pub span: Span, -} - -impl HasSpan for TableLit { - fn span(&self) -> Span { - self.span - } -} - -#[derive(Clone)] -pub enum Lit { - /// `nil` - Nil(Span), - - /// - `true` - /// - `false` - Bool(bool, Span), - - /// See [`NumLit`]. - Num(NumLit), - - /// See [`StringLit`] - String(StringLit), - - /// See [`TableLit`]. - Table(TableLit), -} - -impl fmt::Debug for Lit { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Nil(_) => write!(f, "l#nil"), - Self::Bool(b, _) => write!(f, "l#{b:?}"), - Self::Num(n) => write!(f, "l#{n:?}"), - Self::String(s) => write!(f, "l#{s:?}"), - Self::Table(t) => { - write!(f, "l#")?; - t.fmt(f) - } - } - } -} - -impl HasSpan for Lit { - fn span(&self) -> Span { - match self { - Lit::Nil(span) => *span, - Lit::Bool(_, span) => *span, - Lit::Num(n) => n.span(), - Lit::String(s) => s.span(), - Lit::Table(t) => t.span(), - } - } -} - -#[derive(Debug, Clone)] -pub enum TableConstrElem { - /// See [`TableLitElem`]. - Lit(TableLitElem), - - /// `[a]: b` - /// - /// Structure: `[ s0 index s1 ] s2 : s3 value` - Indexed { - s0: Space, - index: Box, - s1: Space, - s2: Space, - s3: Space, - value: Box, - span: Span, - }, -} - -impl HasSpan for TableConstrElem { - fn span(&self) -> Span { - match self { - TableConstrElem::Lit(lit) => lit.span(), - TableConstrElem::Indexed { span, .. } => *span, - } - } -} - -/// `{ a, b, foo: c, [d]: e }` -#[derive(Debug, Clone)] -pub struct TableConstr { - pub elems: Vec<(Space, TableConstrElem, Space)>, - /// `Some` if there is a trailing comma, `None` otherwise. - pub trailing_comma: Option, - pub span: Span, -} - -impl HasSpan for TableConstr { - fn span(&self) -> Span { - self.span - } -} - -#[derive(Debug, Clone, Copy)] -pub enum BinOp { - /// `+` - Add, - /// `-` - Sub, - /// `*` - Mul, - /// `/` - Div, - /// `%` - Mod, - /// `==` - Eq, - /// `!=` - Neq, - /// `and` - And, - /// `or` - Or, -} - -#[derive(Debug, Clone)] -pub enum Expr { - Lit(Lit), - - /// `(a)` - /// - /// Structure: `( s0 inner s1 )` - Paren { - s0: Space, - inner: Box, - s1: Space, - span: Span, - }, - - /// See [`TableConstr`]. - TableConstr(TableConstr), - - /// `[a]` - /// - /// Structure: `[ s0 index s1 ]` - Var { - s0: Space, - index: Box, - s1: Space, - span: Span, - }, - - /// `foo` - VarIdent(Ident), - - /// `[a] = b` - /// - /// Structure: `[ s0 index s1 ] s2 = s3 value` - VarAssign { - s0: Space, - index: Box, - s1: Space, - s2: Space, - s3: Space, - value: Box, - span: Span, - }, - - /// `foo = a` - /// - /// Structure: `name s0 = s1 value` - VarIdentAssign { - name: Ident, - s0: Space, - s1: Space, - value: Box, - }, - - /// `-a` - /// - /// Structure: `- s0 expr` - Neg { - minus: Span, - s0: Space, - expr: Box, - }, - - /// `not a` - /// - /// Structure: `'not' s0 expr` - Not { - not: Span, - s0: Space, - expr: Box, - }, - - /// `a[b]` - /// - /// Structure: `expr s0 [ s1 index s2 ]` - Field { - expr: Box, - s0: Space, - s1: Space, - index: Box, - s2: Space, - span: Span, - }, - - /// `a.foo` - /// - /// Structure: `expr s0 . s1 ident` - FieldIdent { - expr: Box, - s0: Space, - s1: Space, - ident: Ident, - }, - - /// `a[b] = c` - /// - /// Structure: `expr s0 [ s1 index s2 ] s3 = s4 value` - FieldAssign { - expr: Box, - s0: Space, - s1: Space, - index: Box, - s2: Space, - s3: Space, - s4: Space, - value: Box, - }, - - /// `a.foo = b` - /// - /// Structure: `expr s0 . s1 ident s2 = s3 value` - FieldIdentAssign { - expr: Box, - s0: Space, - s1: Space, - ident: Ident, - s2: Space, - s3: Space, - value: Box, - }, - - /// - `a + b` - /// - `a == b` - /// - `a and b` - /// - /// Structure: `left s0 op s1 right` - BinOp { - left: Box, - s0: Space, - op: BinOp, - s1: Space, - right: Box, - }, -} - -impl HasSpan for Expr { - fn span(&self) -> Span { - match self { - Expr::Lit(lit) => lit.span(), - Expr::Paren { span, .. } => *span, - Expr::TableConstr(tcr) => tcr.span(), - Expr::Var { span, .. } => *span, - Expr::VarIdent(name) => name.span(), - Expr::VarAssign { span, .. } => *span, - Expr::VarIdentAssign { name, value, .. } => name.span().join(value.span()), - Expr::Neg { minus, expr, .. } => minus.join(expr.span()), - Expr::Not { not, expr, .. } => not.join(expr.span()), - Expr::Field { span, .. } => *span, - Expr::FieldIdent { expr, ident, .. } => expr.span().join(ident.span()), - Expr::FieldAssign { expr, value, .. } => expr.span().join(value.span()), - Expr::FieldIdentAssign { expr, value, .. } => expr.span().join(value.span()), - Expr::BinOp { left, right, .. } => left.span().join(right.span()), - } - } -} +pub use self::basic::*; +pub use self::call::*; +pub use self::expr::*; +pub use self::field::*; +pub use self::lit::*; +pub use self::table_constr::*; +pub use self::table_destr::*; +pub use self::var::*; #[derive(Debug, Clone)] pub struct Program { diff --git a/src/ast/basic.rs b/src/ast/basic.rs new file mode 100644 index 0000000..628b459 --- /dev/null +++ b/src/ast/basic.rs @@ -0,0 +1,39 @@ +use std::fmt; + +use crate::span::{HasSpan, Span}; + +#[derive(Clone)] +pub struct Space { + pub comment: Vec<(String, Span)>, + pub span: Span, +} + +impl fmt::Debug for Space { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Space").finish() + } +} + +impl HasSpan for Space { + fn span(&self) -> Span { + self.span + } +} + +#[derive(Clone)] +pub struct Ident { + pub name: String, + pub span: Span, +} + +impl fmt::Debug for Ident { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "i#{}", self.name) + } +} + +impl HasSpan for Ident { + fn span(&self) -> Span { + self.span + } +} diff --git a/src/ast/call.rs b/src/ast/call.rs new file mode 100644 index 0000000..413dab5 --- /dev/null +++ b/src/ast/call.rs @@ -0,0 +1,50 @@ +use crate::span::{HasSpan, Span}; + +use super::basic::Space; +use super::expr::Expr; +use super::table_constr::TableConstr; + +#[derive(Debug, Clone)] +pub enum Call { + /// `a(b)` + /// + /// Structure: `expr s0 ( s1 arg s2) + Arg { + expr: Box, + s0: Space, + s1: Space, + arg: Box, + s2: Space, + span: Span, + }, + + /// `a()` + /// + /// Structure: `expr s0 ( s1 )` + NoArg { + expr: Box, + s0: Space, + s1: Space, + span: Span, + }, + + /// `a{..}` + /// + /// Structure: `expr s0 constr` + Constr { + expr: Box, + s0: Space, + constr: TableConstr, + span: Span, + }, +} + +impl HasSpan for Call { + fn span(&self) -> Span { + match self { + Call::Arg { span, .. } => *span, + Call::NoArg { span, .. } => *span, + Call::Constr { span, .. } => *span, + } + } +} diff --git a/src/ast/expr.rs b/src/ast/expr.rs new file mode 100644 index 0000000..8a0cf04 --- /dev/null +++ b/src/ast/expr.rs @@ -0,0 +1,102 @@ +use crate::span::{HasSpan, Span}; + +use super::basic::Space; +use super::call::Call; +use super::field::Field; +use super::lit::Lit; +use super::table_constr::TableConstr; +use super::table_destr::TableDestr; +use super::var::Var; + +#[derive(Debug, Clone, Copy)] +pub enum BinOp { + /// `+` + Add, + /// `-` + Sub, + /// `*` + Mul, + /// `/` + Div, + /// `%` + Mod, + /// `==` + Eq, + /// `!=` + Neq, + /// `and` + And, + /// `or` + Or, +} + +#[derive(Debug, Clone)] +pub enum Expr { + Lit(Lit), + Call(Call), + Field(Field), + Var(Var), + TableConstr(TableConstr), + TableDestr(TableDestr), + + /// `(a)` + /// + /// Structure: `( s0 inner s1 )` + Paren { + s0: Space, + inner: Box, + s1: Space, + span: Span, + }, + + /// `-a` + /// + /// Structure: `- s0 expr` + Neg { + minus: Span, + s0: Space, + expr: Box, + span: Span, + }, + + /// `not a` + /// + /// Structure: `'not' s0 expr` + Not { + not: Span, + s0: Space, + expr: Box, + span: Span, + }, + + /// - `a + b` + /// - `a == b` + /// - `a and b` + /// + /// Structure: `left s0 op s1 right` + BinOp { + left: Box, + s0: Space, + op: BinOp, + s1: Space, + right: Box, + span: Span, + }, +} + +impl HasSpan for Expr { + fn span(&self) -> Span { + match self { + Expr::Lit(lit) => lit.span(), + Expr::Call(call) => call.span(), + Expr::Field(field) => field.span(), + Expr::Var(var) => var.span(), + Expr::TableConstr(constr) => constr.span(), + Expr::TableDestr(destr) => destr.span(), + Expr::Paren { span, .. } => *span, + Expr::Neg { span, .. } => *span, + Expr::Not { span, .. } => *span, + Expr::BinOp { span, .. } => *span, + } + } +} diff --git a/src/ast/field.rs b/src/ast/field.rs new file mode 100644 index 0000000..2ca6db8 --- /dev/null +++ b/src/ast/field.rs @@ -0,0 +1,70 @@ +use crate::span::{HasSpan, Span}; + +use super::basic::{Ident, Space}; +use super::expr::Expr; + +#[derive(Debug, Clone)] +pub enum Field { + /// `a[b]` + /// + /// Structure: `expr s0 [ s1 index s2 ]` + Access { + expr: Box, + s0: Space, + s1: Space, + index: Box, + s2: Space, + span: Span, + }, + + /// `a[b] = c` + /// + /// Structure: `expr s0 [ s1 index s2 ] s3 = s4 value` + Assign { + expr: Box, + s0: Space, + s1: Space, + index: Box, + s2: Space, + s3: Space, + s4: Space, + value: Box, + span: Span, + }, + + /// `a.foo` + /// + /// Structure: `expr s0 . s1 ident` + AccessIdent { + expr: Box, + s0: Space, + s1: Space, + ident: Ident, + span: Span, + }, + + /// `a.foo = b` + /// + /// Structure: `expr s0 . s1 ident s2 = s3 value` + AssignIdent { + expr: Box, + s0: Space, + s1: Space, + ident: Ident, + s2: Space, + s3: Space, + value: Box, + span: Span, + }, +} + +impl HasSpan for Field { + fn span(&self) -> Span { + match self { + Field::Access { span, .. } => *span, + Field::Assign { span, .. } => *span, + Field::AccessIdent { span, .. } => *span, + Field::AssignIdent { span, .. } => *span, + } + } +} diff --git a/src/ast/lit.rs b/src/ast/lit.rs new file mode 100644 index 0000000..59cef61 --- /dev/null +++ b/src/ast/lit.rs @@ -0,0 +1,165 @@ +use std::fmt; + +use crate::span::{HasSpan, Span}; + +use super::basic::{Ident, Space}; +use super::expr::Expr; + +#[derive(Clone)] +pub enum NumLitStr { + /// - `0b_0001_1011` + /// - `0b10` + Bin(String), + + /// - `12_345` + /// - `7` + Dec(String), + + /// - `0x_c0_f3` + /// - `0xB` + Hex(String), +} + +impl fmt::Debug for NumLitStr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Bin(str) => write!(f, "0b{str}"), + Self::Dec(str) => write!(f, "{str}"), + Self::Hex(str) => write!(f, "0x{str}"), + } + } +} + +/// Positive number literal. +/// +/// Possible bases are binary, decimal, hexadecimal. Underscores can be inserted +/// before and after any digit. +#[derive(Clone)] +pub struct NumLit { + pub value: i64, + pub str: NumLitStr, + pub span: Span, +} + +impl fmt::Debug for NumLit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.str.fmt(f) + } +} + +impl HasSpan for NumLit { + fn span(&self) -> Span { + self.span + } +} + +#[derive(Debug, Clone)] +pub enum StringLit { + /// - `"Hello world\n"` + /// - `""` + Inline(String, Span), + + /// ```text + /// """ + /// Hello, + /// world! + /// """ + /// ``` + Multiline(String, Span), +} + +impl HasSpan for StringLit { + fn span(&self) -> Span { + match self { + StringLit::Inline(_, span) => *span, + StringLit::Multiline(_, span) => *span, + } + } +} + +#[derive(Debug, Clone)] +pub enum TableLitElem { + /// `a` + Positional(Box), + + /// `foo: a` + /// + /// Structure: `name s0 : s1 value` + Named { + name: Ident, + s0: Space, + s1: Space, + value: Box, + span: Span, + }, +} + +impl HasSpan for TableLitElem { + fn span(&self) -> Span { + match self { + TableLitElem::Positional(value) => value.span(), + TableLitElem::Named { span, .. } => *span, + } + } +} + +/// `'{ a, foo: b }` +#[derive(Debug, Clone)] +pub struct TableLit { + pub elems: Vec<(Space, TableLitElem, Space)>, + /// `Some` if there is a trailing comma, `None` otherwise. + pub trailing_comma: Option, + pub span: Span, +} + +impl HasSpan for TableLit { + fn span(&self) -> Span { + self.span + } +} + +#[derive(Clone)] +pub enum Lit { + /// `nil` + Nil(Span), + + /// - `true` + /// - `false` + Bool(bool, Span), + + /// See [`NumLit`]. + Num(NumLit), + + /// See [`StringLit`] + String(StringLit), + + /// See [`TableLit`]. + Table(TableLit), +} + +impl fmt::Debug for Lit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Nil(_) => write!(f, "l#nil"), + Self::Bool(b, _) => write!(f, "l#{b:?}"), + Self::Num(n) => write!(f, "l#{n:?}"), + Self::String(s) => write!(f, "l#{s:?}"), + Self::Table(t) => { + write!(f, "l#")?; + t.fmt(f) + } + } + } +} + +impl HasSpan for Lit { + fn span(&self) -> Span { + match self { + Lit::Nil(span) => *span, + Lit::Bool(_, span) => *span, + Lit::Num(n) => n.span(), + Lit::String(s) => s.span(), + Lit::Table(t) => t.span(), + } + } +} diff --git a/src/ast/table_constr.rs b/src/ast/table_constr.rs new file mode 100644 index 0000000..c065643 --- /dev/null +++ b/src/ast/table_constr.rs @@ -0,0 +1,48 @@ +use crate::span::{HasSpan, Span}; + +use super::basic::Space; +use super::expr::Expr; +use super::lit::TableLitElem; + +#[derive(Debug, Clone)] +pub enum TableConstrElem { + /// See [`TableLitElem`]. + Lit(TableLitElem), + + /// `[a]: b` + /// + /// Structure: `[ s0 index s1 ] s2 : s3 value` + Indexed { + s0: Space, + index: Box, + s1: Space, + s2: Space, + s3: Space, + value: Box, + span: Span, + }, +} + +impl HasSpan for TableConstrElem { + fn span(&self) -> Span { + match self { + TableConstrElem::Lit(lit) => lit.span(), + TableConstrElem::Indexed { span, .. } => *span, + } + } +} + +/// `{ a, b, foo: c, [d]: e }` +#[derive(Debug, Clone)] +pub struct TableConstr { + pub elems: Vec<(Space, TableConstrElem, Space)>, + /// `Some` if there is a trailing comma, `None` otherwise. + pub trailing_comma: Option, + pub span: Span, +} + +impl HasSpan for TableConstr { + fn span(&self) -> Span { + self.span + } +} diff --git a/src/ast/table_destr.rs b/src/ast/table_destr.rs new file mode 100644 index 0000000..b450bf8 --- /dev/null +++ b/src/ast/table_destr.rs @@ -0,0 +1,65 @@ +use crate::span::{HasSpan, Span}; + +use super::basic::{Ident, Space}; +use super::expr::Expr; + +#[derive(Debug, Clone)] +pub enum TablePatternElem { + /// `foo` + Positional(Ident), + + /// `foo: bar` + /// + /// Structure: `name s0 : s1 ident` + Named { + name: Ident, + s0: Space, + s1: Space, + ident: Ident, + span: Span, + }, +} + +impl HasSpan for TablePatternElem { + fn span(&self) -> Span { + match self { + TablePatternElem::Positional(ident) => ident.span(), + TablePatternElem::Named { span, .. } => *span, + } + } +} + +/// `'{ foo, bar: baz }` +#[derive(Debug, Clone)] +pub struct TablePattern { + pub elems: Vec<(Space, TablePatternElem, Space)>, + /// `Some` if there is a trailing comma, `None` otherwise. + pub trailing_comma: Option, + pub span: Span, +} + +impl HasSpan for TablePattern { + fn span(&self) -> Span { + self.span + } +} + +/// - `{ foo, bar: baz } = a` +/// - `local { foo, bar: baz } = a` +/// +/// Structure: `local pattern s0 = s1 value` +#[derive(Debug, Clone)] +pub struct TableDestr { + pub local: Option, + pub pattern: TablePattern, + pub s0: Space, + pub s1: Space, + pub value: Box, + pub span: Span, +} + +impl HasSpan for TableDestr { + fn span(&self) -> Span { + self.span + } +} diff --git a/src/ast/var.rs b/src/ast/var.rs new file mode 100644 index 0000000..30c1686 --- /dev/null +++ b/src/ast/var.rs @@ -0,0 +1,59 @@ +use crate::span::{HasSpan, Span}; + +use super::basic::{Ident, Space}; +use super::expr::Expr; + +#[derive(Debug, Clone)] +pub enum Var { + /// `[a]` + /// + /// Structure: `[ s0 index s1 ]` + Access { + s0: Space, + index: Box, + s1: Space, + span: Span, + }, + + /// - `[a] = b` + /// - `local [a] = b` + /// + /// Structure: `local [ s0 index s1 ] s2 = s3 value` + Assign { + local: Option, + s0: Space, + index: Box, + s1: Space, + s2: Space, + s3: Space, + value: Box, + span: Span, + }, + + /// `foo` + AccessIdent(Ident), + + /// - `foo = a` + /// - `local foo = a` + /// + /// Structure: `local name s0 = s1 value` + AssignIdent { + local: Option, + name: Ident, + s0: Space, + s1: Space, + value: Box, + span: Span, + }, +} + +impl HasSpan for Var { + fn span(&self) -> Span { + match self { + Var::Access { span, .. } => *span, + Var::Assign { span, .. } => *span, + Var::AccessIdent(ident) => ident.span(), + Var::AssignIdent { span, .. } => *span, + } + } +}