Full AST based syntax highlighting
This commit is contained in:
@@ -8,3 +8,8 @@ arcstr = "1.2.0"
|
||||
derive_more = { version = "2.1.0", features = ["deref", "debug", "display"] }
|
||||
indexmap = "2.13.0"
|
||||
peg = "0.8.5"
|
||||
rangemap = "1.7.1"
|
||||
ropey = { version = "1.6.1", optional = true }
|
||||
|
||||
[features]
|
||||
rope = ["dep:ropey"]
|
||||
|
||||
+152
-186
@@ -1,146 +1,117 @@
|
||||
use arcstr::Substr;
|
||||
use derive_more::Deref;
|
||||
use indexmap::IndexMap;
|
||||
use std::ops::Range;
|
||||
use std::sync::Arc;
|
||||
use std::{ops::Range, sync::Arc};
|
||||
|
||||
#[derive(Debug, Deref)]
|
||||
pub struct Ident(pub Substr);
|
||||
|
||||
#[derive(Debug, Deref)]
|
||||
pub struct Import(pub Expr);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Number {
|
||||
pub text: Substr,
|
||||
pub r#type: Option<Ident>,
|
||||
#[rustfmt::skip]
|
||||
#[derive(derive_more::Debug, Clone, Copy)]
|
||||
pub enum BinaryOperator {
|
||||
#[debug("+")] Add,
|
||||
#[debug("-")] Sub,
|
||||
#[debug("*")] Mul,
|
||||
#[debug("/")] Div,
|
||||
#[debug("%")] Mod,
|
||||
#[debug("==")] Eq,
|
||||
#[debug("!=")] Ne,
|
||||
#[debug("<")] Lt,
|
||||
#[debug(">")] Gt,
|
||||
#[debug("<=")] Le,
|
||||
#[debug(">=")] Ge,
|
||||
#[debug("..")] Range,
|
||||
#[debug("as")] Cast,
|
||||
#[debug("as")] Assign,
|
||||
}
|
||||
|
||||
#[derive(derive_more::Debug)]
|
||||
#[derive(Debug, Deref, Clone)]
|
||||
pub struct AstNode<T> {
|
||||
pub range: Range<usize>,
|
||||
#[deref]
|
||||
pub node: T,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Number {
|
||||
pub number: Range<usize>,
|
||||
pub ty: Option<Range<usize>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Expr {
|
||||
#[debug("uninit")]
|
||||
Uninit(Substr),
|
||||
#[debug("Ident({:?})", _0.0)]
|
||||
Ident(Ident),
|
||||
#[debug("{_0:?}")]
|
||||
Number(Number),
|
||||
String(Substr),
|
||||
#[debug("{_0:?}")]
|
||||
Binary(Box<BinaryExpr>),
|
||||
Index(Box<IndexingExpr>),
|
||||
Access(Box<AccessExpr>),
|
||||
Deref(Box<Expr>),
|
||||
Tuple(Vec<Expr>),
|
||||
List(Vec<Expr>),
|
||||
Struct(Box<StructCtor>),
|
||||
#[debug("{_0:?}")]
|
||||
Block(Arc<Block>),
|
||||
#[debug("{_0:?}")]
|
||||
Func(Arc<Function>),
|
||||
#[debug("{_0:?}")]
|
||||
Type(Box<Type>),
|
||||
|
||||
#[debug("{_0:?}")]
|
||||
ConstDecl(Box<ConstDecl>),
|
||||
#[debug("{_0:?}")]
|
||||
VarDecl(Box<VarDecl>),
|
||||
#[debug("{_0:?}")]
|
||||
For(Box<For>),
|
||||
#[debug("{_0:?}")]
|
||||
While(Box<While>),
|
||||
#[debug("{_0:?}")]
|
||||
String(Range<usize>),
|
||||
Ident(Range<usize>),
|
||||
Uninit(Range<usize>),
|
||||
If(Box<If>),
|
||||
|
||||
Func(Arc<AstNode<Function>>),
|
||||
While(Box<While>),
|
||||
Block(Arc<AstNode<Block>>),
|
||||
VarDecl(Box<VarDecl>),
|
||||
ConstDecl(Box<ConstDecl>),
|
||||
List(Vec<AstNode<Expr>>),
|
||||
Tuple(Vec<AstNode<Expr>>),
|
||||
Access {
|
||||
value: Box<AstNode<Expr>>,
|
||||
operator: Range<usize>,
|
||||
field: Range<usize>,
|
||||
},
|
||||
Deref {
|
||||
value: Box<AstNode<Expr>>,
|
||||
operator: Range<usize>,
|
||||
},
|
||||
Index {
|
||||
value: Box<AstNode<Expr>>,
|
||||
index: Vec<AstNode<Expr>>,
|
||||
},
|
||||
Binary {
|
||||
lhs: Box<AstNode<Expr>>,
|
||||
operator: AstNode<BinaryOperator>,
|
||||
rhs: Box<AstNode<Expr>>,
|
||||
},
|
||||
Call {
|
||||
func: Box<Expr>,
|
||||
args: Vec<Expr>,
|
||||
func: Box<AstNode<Expr>>,
|
||||
args: Vec<AstNode<Expr>>,
|
||||
},
|
||||
Ptr {
|
||||
ptr_tok: Range<usize>,
|
||||
mutable: Option<Range<usize>>,
|
||||
base: Box<AstNode<Expr>>,
|
||||
},
|
||||
Ref {
|
||||
ref_tok: Range<usize>,
|
||||
mutable: Range<usize>,
|
||||
base: Box<AstNode<Expr>>,
|
||||
},
|
||||
Struct {
|
||||
struct_tok: Range<usize>,
|
||||
fields: Vec<AstNode<Field>>,
|
||||
},
|
||||
StructCtor {
|
||||
ty: Option<Box<AstNode<Expr>>>,
|
||||
values: Vec<AstNode<NameValuePair>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
pub fn range(&self) -> Range<usize> {
|
||||
match self {
|
||||
Self::Ident(e) => e.range(),
|
||||
Self::Access(e) => e.range(),
|
||||
Self::Number(e) => e.text.range(),
|
||||
_ => todo!("{self:?}"),
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Import {
|
||||
pub import_tok: Range<usize>,
|
||||
pub expr: AstNode<Expr>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BinaryExpr {
|
||||
pub lhs: Expr,
|
||||
pub op: BinaryOp,
|
||||
pub rhs: Expr,
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Field {
|
||||
pub name: Range<usize>,
|
||||
pub ty: AstNode<Expr>,
|
||||
pub public: Option<Range<usize>>,
|
||||
pub mutable: Option<Range<usize>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IndexingExpr {
|
||||
pub value: Expr,
|
||||
pub index: Expr,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AccessExpr {
|
||||
pub value: Expr,
|
||||
pub field: Ident,
|
||||
}
|
||||
|
||||
impl AccessExpr {
|
||||
pub fn range(&self) -> Range<usize> {
|
||||
self.value.range().start..self.field.0.range().end
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(derive_more::Debug)]
|
||||
pub enum BinaryOp {
|
||||
#[debug("{_0}")] Add(Substr),
|
||||
#[debug("{_0}")] Sub(Substr),
|
||||
#[debug("{_0}")] Mul(Substr),
|
||||
#[debug("{_0}")] Div(Substr),
|
||||
#[debug("{_0}")] Mod(Substr),
|
||||
#[debug("{_0}")] Eq(Substr),
|
||||
#[debug("{_0}")] Ne(Substr),
|
||||
#[debug("{_0}")] Lt(Substr),
|
||||
#[debug("{_0}")] Gt(Substr),
|
||||
#[debug("{_0}")] Le(Substr),
|
||||
#[debug("{_0}")] Ge(Substr),
|
||||
#[debug("{_0}")] Range(Substr),
|
||||
#[debug("{_0}")] Assign(Substr),
|
||||
#[debug("{_0}")] Cast(Substr),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Type {
|
||||
Ptr { base: Expr, mutable: Option<Substr> },
|
||||
Struct(Struct),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ConstDecl {
|
||||
pub names: NamePattern,
|
||||
pub r#type: Option<Expr>,
|
||||
pub value: Expr,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VarDecl {
|
||||
pub names: NamePattern,
|
||||
pub r#type: Option<Expr>,
|
||||
pub value: Expr,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum NamePattern {
|
||||
Single(Ident),
|
||||
Tuple(Vec<Ident>),
|
||||
List(Vec<Ident>),
|
||||
Single(Range<usize>),
|
||||
Tuple(Vec<Range<usize>>),
|
||||
List(Vec<Range<usize>>),
|
||||
}
|
||||
|
||||
impl NamePattern {
|
||||
pub fn as_slice(&self) -> &[Ident] {
|
||||
pub fn as_slice(&self) -> &[Range<usize>] {
|
||||
match self {
|
||||
NamePattern::Single(ident) => std::slice::from_ref(ident),
|
||||
NamePattern::Tuple(idents) => idents.as_slice(),
|
||||
@@ -149,77 +120,72 @@ impl NamePattern {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Function {
|
||||
pub text: Substr,
|
||||
pub args: Vec<NameValuePair>,
|
||||
pub ret: Option<Expr>,
|
||||
pub block: Option<Arc<Block>>,
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConstDecl {
|
||||
pub names: AstNode<NamePattern>,
|
||||
pub r#type: Option<AstNode<Expr>>,
|
||||
pub value: AstNode<Expr>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NameValuePair {
|
||||
pub name: Ident,
|
||||
pub value: Expr,
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VarDecl {
|
||||
pub names: AstNode<NamePattern>,
|
||||
pub r#type: Option<AstNode<Expr>>,
|
||||
pub value: AstNode<Expr>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Struct {
|
||||
pub fields: Vec<Field>,
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Block(pub Vec<AstNode<Expr>>);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Field {
|
||||
pub name: Ident,
|
||||
pub ty: Expr,
|
||||
pub public: Option<Substr>,
|
||||
pub mutable: Option<Substr>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StructCtor {
|
||||
pub r#type: Option<Expr>,
|
||||
pub r#values: IndexMap<Substr, NameValuePair>,
|
||||
pub(crate) range: Range<usize>,
|
||||
}
|
||||
|
||||
impl StructCtor {
|
||||
pub fn range(&self) -> Range<usize> {
|
||||
self.range.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Block(pub Vec<Expr>);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct For {
|
||||
pub names: NamePattern,
|
||||
pub value: Expr,
|
||||
pub block: Block,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct While {
|
||||
pub cond: Expr,
|
||||
pub block: Block,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct If {
|
||||
pub cond: Expr,
|
||||
pub block: Block,
|
||||
pub if_tok: Range<usize>,
|
||||
pub cond: AstNode<Expr>,
|
||||
pub block: AstNode<Block>,
|
||||
pub else_: Option<Box<Else>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Else {
|
||||
If(If),
|
||||
Block(Block),
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct While {
|
||||
pub while_tok: Range<usize>,
|
||||
pub cond: AstNode<Expr>,
|
||||
pub block: AstNode<Block>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CompilationUnit {
|
||||
pub imports: Vec<Import>,
|
||||
pub decls: Vec<ConstDecl>,
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Else {
|
||||
If {
|
||||
else_tok: Range<usize>,
|
||||
expr: AstNode<If>,
|
||||
},
|
||||
Block {
|
||||
else_tok: Range<usize>,
|
||||
expr: AstNode<Block>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Function {
|
||||
pub fn_tok: Range<usize>,
|
||||
pub args: Vec<AstNode<Parameter>>,
|
||||
pub ret: Option<AstNode<Expr>>,
|
||||
pub block: Option<Arc<AstNode<Block>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Parameter {
|
||||
pub name: Range<usize>,
|
||||
pub value: AstNode<Expr>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NameValuePair {
|
||||
pub name: Range<usize>,
|
||||
pub value: AstNode<Expr>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AstRoot {
|
||||
pub imports: Vec<AstNode<Import>>,
|
||||
pub decls: Vec<AstNode<ConstDecl>>,
|
||||
}
|
||||
|
||||
+55
-4
@@ -1,19 +1,70 @@
|
||||
pub use arcstr::ArcStr;
|
||||
pub use arcstr::{ArcStr, Substr};
|
||||
use derive_more::Display;
|
||||
pub use parser::{compilation_unit as parse, *};
|
||||
use std::{fmt::Debug, path::PathBuf};
|
||||
use rangemap::RangeMap;
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
path::{Path, PathBuf},
|
||||
sync::OnceLock,
|
||||
};
|
||||
|
||||
#[cfg(feature = "rope")]
|
||||
pub use ropey::Rope;
|
||||
|
||||
use crate::ast::AstRoot;
|
||||
|
||||
pub mod ast;
|
||||
mod parser;
|
||||
|
||||
#[derive(Display)]
|
||||
pub enum Text {
|
||||
#[cfg(feature = "rope")]
|
||||
#[display("{_0}")]
|
||||
Rope(Rope),
|
||||
#[display("{_0}")]
|
||||
ArcStr(ArcStr, OnceLock<RangeMap<usize, (usize, Substr)>>),
|
||||
}
|
||||
|
||||
pub struct SourceCode {
|
||||
pub text: ArcStr,
|
||||
pub text: Text,
|
||||
pub file: PathBuf,
|
||||
pub ast: OnceLock<Result<AstRoot, ParseError<LineCol>>>,
|
||||
}
|
||||
|
||||
impl SourceCode {
|
||||
#[cfg(feature = "rope")]
|
||||
pub fn from_file_rope(path: &dyn AsRef<Path>) -> std::io::Result<Self> {
|
||||
let file = path.as_ref().to_path_buf();
|
||||
let text = Text::Rope(Rope::from(std::fs::read_to_string(path)?));
|
||||
Ok(Self {
|
||||
text,
|
||||
file,
|
||||
ast: OnceLock::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_file_arcstr(path: &dyn AsRef<Path>) -> std::io::Result<Self> {
|
||||
let file = path.as_ref().to_path_buf();
|
||||
let text = Text::ArcStr(
|
||||
ArcStr::from(std::fs::read_to_string(path)?),
|
||||
OnceLock::new(),
|
||||
);
|
||||
Ok(Self {
|
||||
text,
|
||||
file,
|
||||
ast: OnceLock::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ast(&self) -> &Result<AstRoot, ParseError<LineCol>> {
|
||||
self.ast.get_or_init(|| crate::parse(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for SourceCode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("SourceCode")
|
||||
.field("text", &self.text)
|
||||
.field("text", &format_args!("{}", self.text))
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
+1
-6
@@ -1,12 +1,7 @@
|
||||
use arcstr::ArcStr;
|
||||
use leaf_parser::SourceCode;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
let source = SourceCode {
|
||||
text: ArcStr::from(std::fs::read_to_string("../test.leaf").unwrap()),
|
||||
file: PathBuf::from("../test.leaf"),
|
||||
};
|
||||
let source = SourceCode::from_file_arcstr(&"../test.leaf").unwrap();
|
||||
let unit = leaf_parser::parse(&source);
|
||||
let _ = dbg!(unit);
|
||||
}
|
||||
|
||||
+207
-114
@@ -1,26 +1,62 @@
|
||||
use crate::SourceCode;
|
||||
use crate::ast::*;
|
||||
use arcstr::Substr;
|
||||
use peg::{Parse, ParseElem, ParseLiteral, ParseSlice};
|
||||
pub use peg::Parse;
|
||||
use peg::ParseSlice;
|
||||
use peg::{ParseElem, ParseLiteral};
|
||||
pub use peg::{error::*, str::LineCol};
|
||||
use std::sync::Arc;
|
||||
use rangemap::RangeMap;
|
||||
use std::ops::Range;
|
||||
|
||||
impl Parse for SourceCode {
|
||||
type PositionRepr = LineCol;
|
||||
|
||||
#[inline]
|
||||
fn start(&self) -> usize {
|
||||
self.text.as_str().start()
|
||||
0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_eof(&self, p: usize) -> bool {
|
||||
self.text.as_str().is_eof(p)
|
||||
match &self.text {
|
||||
#[cfg(feature = "rope")]
|
||||
crate::Text::Rope(s) => p >= s.len_bytes(),
|
||||
crate::Text::ArcStr(s, _) => s.as_str().is_eof(p),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn position_repr(&self, p: usize) -> Self::PositionRepr {
|
||||
self.text.as_str().position_repr(p)
|
||||
match &self.text {
|
||||
#[cfg(feature = "rope")]
|
||||
crate::Text::Rope(s) => {
|
||||
let line = s.byte_to_line(p);
|
||||
let line_s = s.line_to_byte(line);
|
||||
let column = p - line_s;
|
||||
Self::PositionRepr {
|
||||
line,
|
||||
column,
|
||||
offset: p,
|
||||
}
|
||||
}
|
||||
crate::Text::ArcStr(s, lines) => {
|
||||
let lines = lines.get_or_init(|| {
|
||||
let mut lines = RangeMap::new();
|
||||
for (i, text) in s.split_inclusive('\n').enumerate() {
|
||||
let text = s.substr_from(text);
|
||||
let range = text.range();
|
||||
lines.insert(range, (i, text));
|
||||
}
|
||||
lines
|
||||
});
|
||||
let (line, text) = lines.get(&p).unwrap();
|
||||
let column = p - text.range().start;
|
||||
Self::PositionRepr {
|
||||
line: *line,
|
||||
column,
|
||||
offset: p,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,173 +65,230 @@ impl<'input> ParseElem<'input> for SourceCode {
|
||||
|
||||
#[inline]
|
||||
fn parse_elem(&'input self, pos: usize) -> peg::RuleResult<Self::Element> {
|
||||
self.text.as_str().parse_elem(pos)
|
||||
match &self.text {
|
||||
#[cfg(feature = "rope")]
|
||||
crate::Text::Rope(s) => match s.get_char(pos) {
|
||||
Some(c) => RuleResult::Matched(pos + c.len_utf8(), c),
|
||||
None => RuleResult::Failed,
|
||||
},
|
||||
crate::Text::ArcStr(s, _) => s.as_str().parse_elem(pos),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ParseLiteral for SourceCode {
|
||||
#[inline]
|
||||
fn parse_string_literal(&self, pos: usize, literal: &str) -> peg::RuleResult<()> {
|
||||
self.text.as_str().parse_string_literal(pos, literal)
|
||||
match &self.text {
|
||||
#[cfg(feature = "rope")]
|
||||
crate::Text::Rope(s) => match s.get_slice(pos..pos + literal.len()) {
|
||||
None => peg::RuleResult::Failed,
|
||||
Some(text) => match text == literal {
|
||||
false => peg::RuleResult::Failed,
|
||||
true => peg::RuleResult::Matched(pos + literal.len(), ()),
|
||||
},
|
||||
},
|
||||
crate::Text::ArcStr(s, _) => s.as_str().parse_string_literal(pos, literal),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'input> ParseSlice<'input> for SourceCode {
|
||||
type Slice = Substr;
|
||||
type Slice = Range<usize>;
|
||||
|
||||
#[inline]
|
||||
fn parse_slice(&'input self, p1: usize, p2: usize) -> Self::Slice {
|
||||
self.text.substr(p1..p2)
|
||||
p1..p2
|
||||
}
|
||||
}
|
||||
|
||||
peg::parser! {
|
||||
grammar leaf_parser() for SourceCode {
|
||||
// #### ATOMS ####
|
||||
rule number() -> Number
|
||||
= text:$(['0'..='9']+) r#type:ident()? { Number { text, r#type } }
|
||||
/ "0x" text:$(['0'..='9'|'a'..='f'|'A'..'F']+) r#type:ident()? { Number { text, r#type } }
|
||||
/ "0b" text:$(['0'|'1']+) r#type:ident()? { Number { text, r#type } }
|
||||
rule number() -> AstNode<Number>
|
||||
= s:position!() number:$(['0'..='9']+) ty:ident()? e:position!()
|
||||
{ AstNode { node: Number { number, ty }, range: s..e } }
|
||||
/ s:position!() "0x" number:$(['0'..='9'|'a'..='f'|'A'..'F']+) ty:ident()? e:position!()
|
||||
{ AstNode { node: Number { number, ty }, range: s..e } }
|
||||
/ s:position!() "0b" number:$(['0'|'1']+) ty:ident()? e:position!()
|
||||
{ AstNode { node: Number { number, ty }, range: s..e } }
|
||||
|
||||
rule ident() -> Ident
|
||||
= text:$(['_'|'a'..='z'|'A'..='Z']['_'|'a'..='z'|'A'..='Z'|'0'..='9']*) { Ident(text) }
|
||||
rule ident() -> Range<usize>
|
||||
= text:$(['_'|'a'..='z'|'A'..='Z']['_'|'a'..='z'|'A'..='Z'|'0'..='9']*)
|
||||
{ text }
|
||||
|
||||
rule ident2() -> Ident
|
||||
= text:$("#"? ['_'|'a'..='z'|'A'..='Z']['_'|'a'..='z'|'A'..='Z'|'0'..='9']*) { Ident(text) }
|
||||
rule ident2() -> Range<usize>
|
||||
= text:$(['#']? ['_'|'a'..='z'|'A'..='Z']['_'|'a'..='z'|'A'..='Z'|'0'..='9']*)
|
||||
{ text }
|
||||
|
||||
rule string() -> Substr
|
||||
= str:$("\"" char()* "\"") { str }
|
||||
|
||||
rule char() -> char = normal()
|
||||
|
||||
rule normal() -> char = [^'\\'|'"']
|
||||
rule string() -> Range<usize>
|
||||
= text:$("\"" [^'\\'|'"']* "\"")
|
||||
{ text }
|
||||
|
||||
// ### EXPRESSIONS ####
|
||||
|
||||
rule expr() -> Expr = precedence! {
|
||||
lhs:(@) __ op:$("as") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Cast(op), rhs }.into()) }
|
||||
--
|
||||
lhs:@ __ op:$("=") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Assign(op), rhs }.into()) }
|
||||
value:@ __ op:$(".") __ field:ident2() { Expr::Access(AccessExpr { value, field }.into()) }
|
||||
value:@ __ op:$(".^") { Expr::Deref(value.into()) }
|
||||
lhs:@ "(" __ args:(expr() ** list_separator()) __ ")" { Expr::Call { func: lhs.into(), args } }
|
||||
value:@ "[" __ index:expr() __ "]" { Expr::Index(IndexingExpr { value, index }.into()) }
|
||||
rule bop0() -> AstNode<BinaryOperator>
|
||||
= op:$("as") { AstNode { range: op, node: BinaryOperator::Cast } }
|
||||
/ op:$("=") { AstNode { range: op, node: BinaryOperator::Assign } }
|
||||
|
||||
rule bop1() -> AstNode<BinaryOperator>
|
||||
= op:$("+") { AstNode { range: op, node: BinaryOperator::Add } }
|
||||
/ op:$("-") { AstNode { range: op, node: BinaryOperator::Sub } }
|
||||
|
||||
rule bop2() -> AstNode<BinaryOperator>
|
||||
= op:$("*") { AstNode { range: op, node: BinaryOperator::Mul } }
|
||||
/ op:$("/") { AstNode { range: op, node: BinaryOperator::Div } }
|
||||
/ op:$("%") { AstNode { range: op, node: BinaryOperator::Mod } }
|
||||
|
||||
rule bop3() -> AstNode<BinaryOperator>
|
||||
= op:$("==") { AstNode { range: op, node: BinaryOperator::Eq } }
|
||||
/ op:$("!=") { AstNode { range: op, node: BinaryOperator::Ne } }
|
||||
/ op:$("<") { AstNode { range: op, node: BinaryOperator::Lt } }
|
||||
/ op:$(">") { AstNode { range: op, node: BinaryOperator::Gt } }
|
||||
/ op:$("<=") { AstNode { range: op, node: BinaryOperator::Le } }
|
||||
/ op:$(">=") { AstNode { range: op, node: BinaryOperator::Ge } }
|
||||
|
||||
rule bop4() -> AstNode<BinaryOperator>
|
||||
= op:$("..") { AstNode { range: op, node: BinaryOperator::Range } }
|
||||
|
||||
rule expr() -> AstNode<Expr> = precedence! {
|
||||
lhs:@ __ op:bop0() __ rhs:expr()
|
||||
{ AstNode { range: lhs.range.start..rhs.range.end, node: Expr::Binary { lhs: lhs.into(), rhs: rhs.into(), operator: op } } }
|
||||
// --
|
||||
value:@ __ op:$(".") __ field:ident2()
|
||||
{ AstNode { range: value.range.start..field.end, node: Expr::Access { value: value.into(), field, operator: op } } }
|
||||
|
||||
value:@ __ op:$(".^")
|
||||
{ AstNode { range: value.range.start..op.end, node: Expr::Deref { value: value.into(), operator: op } } }
|
||||
|
||||
lhs:@ "(" __ args:(expr() ** list_separator()) __ e:$")"
|
||||
{ AstNode { range: lhs.range.start..e.end, node: Expr::Call { func: lhs.into(), args } } }
|
||||
|
||||
value:@ "[" __ index:(expr() **<1,> ("," __)) __ e:$"]"
|
||||
{ AstNode { range: value.range.start..e.end, node: Expr::Index { value: value.into(), index } } }
|
||||
|
||||
ty:@ __ "#{" __ values:name_value_pairs() __ "}" e:position!()
|
||||
{ AstNode { range: ty.range.start..e, node: Expr::StructCtor { ty: Some(ty.into()), values } } }
|
||||
|
||||
s:$"#{" __ values:name_value_pairs() __ "}" e:position!()
|
||||
{ AstNode { range: s.start..e, node: Expr::StructCtor { ty: None, values } } }
|
||||
|
||||
ty:@ __ "#{" __ values:name_value_pairs() __ "}" e:position!() { Expr::Struct(
|
||||
StructCtor {
|
||||
range: ty.range().start..e,
|
||||
r#type: Some(ty),
|
||||
values: values.into_iter().map(|v| (v.name.0.clone(), v)).collect(),
|
||||
}.into()
|
||||
) }
|
||||
s:position!() "#{" __ values:name_value_pairs() __ "}" e:position!() { Expr::Struct(
|
||||
StructCtor {
|
||||
r#type: None,
|
||||
values: values.into_iter().map(|v| (v.name.0.clone(), v)).collect(),
|
||||
range: s..e,
|
||||
}.into()
|
||||
) }
|
||||
--
|
||||
lhs:@ __ op:$("+") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Add(op), rhs }.into()) }
|
||||
lhs:@ __ op:$("-") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Sub(op), rhs }.into()) }
|
||||
lhs:@ __ op:bop1() __ rhs:expr()
|
||||
{ AstNode { range: lhs.range.start..rhs.range.end, node: Expr::Binary { lhs: lhs.into(), rhs: rhs.into(), operator: op } } }
|
||||
--
|
||||
lhs:@ __ op:$("*") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Mul(op), rhs }.into()) }
|
||||
lhs:@ __ op:$("/") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Div(op), rhs }.into()) }
|
||||
lhs:@ __ op:$("%") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Mod(op), rhs }.into()) }
|
||||
lhs:@ __ op:bop2() __ rhs:expr()
|
||||
{ AstNode { range: lhs.range.start..rhs.range.end, node: Expr::Binary { lhs: lhs.into(), rhs: rhs.into(), operator: op } } }
|
||||
--
|
||||
lhs:@ __ op:$("..") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Range(op), rhs }.into()) }
|
||||
lhs:@ __ op:bop3() __ rhs:expr()
|
||||
{ AstNode { range: lhs.range.start..rhs.range.end, node: Expr::Binary { lhs: lhs.into(), rhs: rhs.into(), operator: op } } }
|
||||
--
|
||||
lhs:@ __ op:$("==") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Eq(op), rhs }.into()) }
|
||||
lhs:@ __ op:$("!=") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Ne(op), rhs }.into()) }
|
||||
lhs:@ __ op:$("<") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Lt(op), rhs }.into()) }
|
||||
lhs:@ __ op:$(">") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Gt(op), rhs }.into()) }
|
||||
lhs:@ __ op:$("<=") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Le(op), rhs }.into()) }
|
||||
lhs:@ __ op:$(">=") __ rhs:expr() { Expr::Binary(BinaryExpr { lhs, op: BinaryOp::Ge(op), rhs }.into()) }
|
||||
lhs:@ __ op:bop4() __ rhs:expr()
|
||||
{ AstNode { range: lhs.range.start..rhs.range.end, node: Expr::Binary { lhs: lhs.into(), rhs: rhs.into(), operator: op } } }
|
||||
--
|
||||
block:block() { Expr::Block(block.into())}
|
||||
for_loop:for_loop() { Expr::For(for_loop.into())}
|
||||
while_loop:while_loop() { Expr::While(while_loop.into())}
|
||||
if_statement:if_statement() { Expr::If(if_statement.into())}
|
||||
func:func() { Expr::Func(Arc::new(func))}
|
||||
var_decl:var_decl() { Expr::VarDecl(var_decl.into()) }
|
||||
const_decl:const_decl() { Expr::ConstDecl(const_decl.into()) }
|
||||
"(" __ tuple:(expr() **<2,> ("," __)) __ ")" { Expr::Tuple(tuple) }
|
||||
"[" __ list:(expr() ** ("," __)) __ "]" { Expr::List(list) }
|
||||
"(" __ v:expr() __ ")" { v }
|
||||
"*" __ m:$"mut"? __ v:expr() { Expr::Type(Type::Ptr { base:v, mutable: m }.into()) }
|
||||
v:struct_t() { Expr::Type(Type::Struct(v).into()) }
|
||||
v:string() { Expr::String(v) }
|
||||
v:number() { Expr::Number(v) }
|
||||
v:ident() { Expr::Ident(v) }
|
||||
block:block() { AstNode { range: block.range.clone(), node: Expr::Block(block.into()) } }
|
||||
|
||||
i:if_statement() { AstNode { range: i.range, node: Expr::If(i.node.into()) }}
|
||||
l:while_loop() { AstNode { range: l.range, node: Expr::While(l.node.into()) }}
|
||||
|
||||
func:func() { AstNode { range: func.range.clone(), node: Expr::Func(func.into()) } }
|
||||
|
||||
var_decl:var_decl()
|
||||
{ AstNode { range: var_decl.range, node: Expr::VarDecl(var_decl.node.into()) } }
|
||||
|
||||
const_decl:const_decl()
|
||||
{ AstNode { range: const_decl.range, node: Expr::ConstDecl(const_decl.node.into()) } }
|
||||
|
||||
s:$"(" __ elements:(expr() **<2,> ("," __)) __ e:$")"
|
||||
{ AstNode { range: s.start..e.end, node: Expr::Tuple(elements) } }
|
||||
|
||||
s:$"[" __ elements:(expr() **<2,> ("," __)) __ e:$"]"
|
||||
{ AstNode { range: s.start..e.end, node: Expr::List(elements) } }
|
||||
|
||||
s:$"(" __ v:expr() __ e:$")"
|
||||
{ AstNode { range: s.start..e.end, node: v.node } }
|
||||
|
||||
t:$"*" __ m:$"mut"? __ v:expr()
|
||||
{ AstNode { range: t.start..v.range.end, node: Expr::Ptr { ptr_tok: t, mutable: m, base: v.into() } } }
|
||||
|
||||
v:struct_t() { v }
|
||||
v:string() { AstNode { range: v.clone(), node: Expr::String(v) } }
|
||||
v:number() { AstNode { range: v.range, node: Expr::Number(v.node) } }
|
||||
v:ident() { AstNode { range: v.clone(), node: Expr::Ident(v) } }
|
||||
}
|
||||
|
||||
rule block() -> Block
|
||||
= "{" __ exprs:(i:expr() statement_separator() {i})* __ "}" { Block(exprs) }
|
||||
rule block() -> AstNode<Block>
|
||||
= s:$"{" __ exprs:(i:expr() statement_separator() {i})* __ e:$"}"
|
||||
{ AstNode { range: s.start..e.end, node: Block(exprs) } }
|
||||
|
||||
rule func() -> Function
|
||||
= s:position!() t:$"fn" __ "(" __ args:name_type_pairs() __ ")" __ ret:("->" __ e:expr() {e})? __ block:block()? e:position!()
|
||||
{ Function { args, ret, block: block.map(Into::into), text: t.parent().substr(s..e), } }
|
||||
rule func() -> AstNode<Function>
|
||||
= s:position!() t:$"fn" __ "(" __ args:parameters() __ ")" __ ret:("->" __ e:expr() {e})? __ block:block()? e:position!()
|
||||
{ AstNode { range: s..e, node: Function { args, ret, block: block.map(Into::into), fn_tok: t, } } }
|
||||
|
||||
rule name_type_pair() -> NameValuePair
|
||||
= name:ident() __ ":" __ value:expr() { NameValuePair { name, value } }
|
||||
rule parameter() -> AstNode<Parameter>
|
||||
= name:ident() __ ":" __ value:expr()
|
||||
{ AstNode { range: name.start..value.range.end, node: Parameter { name, value } } }
|
||||
|
||||
rule name_type_pairs() -> Vec<NameValuePair>
|
||||
= v:(name_type_pair() **<1,> list_separator()) list_separator()? { v }
|
||||
rule parameters() -> Vec<AstNode<Parameter>>
|
||||
= v:(parameter() **<1,> list_separator()) list_separator()? { v }
|
||||
/ { vec![] }
|
||||
|
||||
rule name_value_pair() -> NameValuePair
|
||||
= name:ident() __ "=" __ value:expr() { NameValuePair { name, value } }
|
||||
rule name_value_pair() -> AstNode<NameValuePair>
|
||||
= name:ident() __ "=" __ value:expr()
|
||||
{ AstNode { range: name.start..value.range.end, node: NameValuePair { name, value } } }
|
||||
|
||||
rule name_value_pairs() -> Vec<NameValuePair>
|
||||
rule name_value_pairs() -> Vec<AstNode<NameValuePair>>
|
||||
= v:(name_value_pair() **<1,> list_separator()) list_separator()? { v }
|
||||
/ { vec![] }
|
||||
|
||||
rule struct_t() -> Struct
|
||||
= "struct" __ "{" __ fields:fields() __ "}" { Struct { fields } }
|
||||
rule struct_t() -> AstNode<Expr>
|
||||
= t:$"struct" __ "{" __ fields:fields() __ e:$"}"
|
||||
{ AstNode { range: t.start..e.end, node: Expr::Struct { fields, struct_tok: t } } }
|
||||
|
||||
rule field() -> Field
|
||||
= public:$"pub"? __ mutable:$"mut"? __ name:ident() __ ":" __ ty:expr() { Field { name, ty, public, mutable } }
|
||||
rule field() -> AstNode<Field>
|
||||
= s:position!() public:$"pub"? __ mutable:$"mut"? __ name:ident() __ ":" __ ty:expr() e:position!()
|
||||
{ AstNode { node: Field { name, ty, public, mutable }, range: s..e } }
|
||||
|
||||
rule fields() -> Vec<Field>
|
||||
rule fields() -> Vec<AstNode<Field>>
|
||||
= v:(field() **<1,> list_separator()) list_separator()? { v }
|
||||
/ { vec![] }
|
||||
|
||||
rule import() -> Import
|
||||
= "import" _ expr:expr() { Import(expr) }
|
||||
rule import() -> AstNode<Import>
|
||||
= t:$"import" _ expr:expr()
|
||||
{ AstNode { range: t.start..expr.range.end, node: Import { import_tok: t, expr } } }
|
||||
|
||||
rule name_pattern() -> NamePattern
|
||||
= "(" __ tuple:(ident() ** ("," __)) __ ")" { NamePattern::Tuple(tuple) }
|
||||
/ "[" __ slice:(ident() ** ("," __)) __ "]" { NamePattern::List(slice) }
|
||||
/ ident:ident() { NamePattern::Single(ident) }
|
||||
rule name_pattern() -> AstNode<NamePattern>
|
||||
= s:$"(" __ tuple:(ident() ** ("," __)) __ e:$")"
|
||||
{ AstNode { range: s.start..e.end, node: NamePattern::Tuple(tuple) } }
|
||||
/ s:$"[" __ slice:(ident() ** ("," __)) __ e:$"]"
|
||||
{ AstNode { range: s.start..e.end, node: NamePattern::List(slice) } }
|
||||
/ ident:ident()
|
||||
{ AstNode { range: ident.clone(), node: NamePattern::Single(ident) } }
|
||||
|
||||
rule const_decl() -> ConstDecl =
|
||||
rule const_decl() -> AstNode<ConstDecl> =
|
||||
names:name_pattern() _? ":" r#type:(_ t:expr() _ {t})? ":" _ value:expr()
|
||||
{ ConstDecl { names, r#type, value } }
|
||||
{ AstNode { range: names.range.start..value.range.end, node: ConstDecl { names, r#type, value } } }
|
||||
|
||||
rule var_decl() -> VarDecl =
|
||||
rule var_decl() -> AstNode<VarDecl> =
|
||||
names:name_pattern() _? ":" r#type:(_ t:expr() _ {t})? "=" _ value:expr()
|
||||
{ VarDecl { names, r#type, value } }
|
||||
{ AstNode { range: names.range.start..value.range.end, node: VarDecl { names, r#type, value } } }
|
||||
|
||||
rule for_loop() -> For =
|
||||
"for" _ names:name_pattern() _ "in" _ value:expr() _ block:block()
|
||||
{ For { names, value, block } }
|
||||
rule while_loop() -> AstNode<While>
|
||||
= t:$"while" _ cond:expr() _ block:block()
|
||||
{ AstNode { range: t.start..block.range.end, node: While { cond, block, while_tok: t } } }
|
||||
|
||||
rule while_loop() -> While =
|
||||
"while" _ cond:expr() _ block:block()
|
||||
{ While { cond, block } }
|
||||
|
||||
rule if_statement() -> If =
|
||||
"if" _ cond:expr() __ block:block() e:(__ e:else_statement() {e})? { If { cond, block, else_: e.map(Box::new) } }
|
||||
rule if_statement() -> AstNode<If>
|
||||
= t:$"if" _ cond:expr() __ block:block() el:(__ e:else_statement() {e})? e:position!()
|
||||
{ AstNode { range: t.start..e, node: If { cond, block, else_: el.map(Box::new), if_tok: t } } }
|
||||
|
||||
rule else_statement() -> Else
|
||||
= "else" _ i:if_statement() { Else::If(i) }
|
||||
/ "else" __ b:block() { Else::Block(b) }
|
||||
= t:$"else" _ i:if_statement() { Else::If { else_tok: t, expr: i } }
|
||||
/ t:$"else" __ b:block() { Else::Block { else_tok: t, expr: b } }
|
||||
|
||||
pub rule compilation_unit() -> CompilationUnit =
|
||||
pub rule compilation_unit() -> AstRoot =
|
||||
__ imports:(i:import() statement_separator() {i})*
|
||||
__ decls:(d:const_decl() statement_separator() {d})*
|
||||
__
|
||||
{ CompilationUnit { imports, decls } }
|
||||
{ AstRoot { imports, decls } }
|
||||
|
||||
// #### MISC ####
|
||||
rule _ = quiet! { [' '|'\t']+ }
|
||||
|
||||
Reference in New Issue
Block a user