use rustc_hash::FxHashMap;
use std::{
fmt::Display,
path::{Path, PathBuf},
rc::Rc,
};
use crate::position::Position;
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct TypeName {
pub name: String,
}
impl TypeName {
pub fn is_no_value(&self) -> bool {
self.name == "NoValue"
}
}
impl std::fmt::Debug for TypeName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name)
}
}
impl Display for TypeName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name)
}
}
impl From<&str> for TypeName {
fn from(s: &str) -> Self {
TypeName { name: s.to_owned() }
}
}
impl From<&String> for TypeName {
fn from(s: &String) -> Self {
TypeName { name: s.to_owned() }
}
}
#[derive(Clone, Eq)]
pub struct TypeSymbol {
pub name: TypeName,
pub position: Position,
pub id: SyntaxId,
}
impl PartialEq for TypeSymbol {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}
impl std::fmt::Debug for TypeSymbol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if std::env::var("VERBOSE").is_ok() {
f.debug_struct("TypeSymbol")
.field("name", &self.name)
.field("position", &self.position)
.field("id", &self.id)
.finish()
} else {
write!(f, "TypeSymbol\"{}\"", self.name.name)
}
}
}
impl Display for TypeSymbol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name)
}
}
impl TypeSymbol {
pub fn is_placeholder(&self) -> bool {
self.name.name == "__placeholder" || self.name.name == "__reserved_word_placeholder"
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TypeHint {
pub sym: TypeSymbol,
pub args: Vec<TypeHint>,
pub position: Position,
}
impl TypeHint {
pub fn as_src(&self) -> String {
if self.args.is_empty() {
format!("{}", self.sym.name)
} else if self.sym.name.name == "Tuple" {
let formatted_args = self
.args
.iter()
.map(|a| a.as_src())
.collect::<Vec<_>>()
.join(", ");
format!("({})", formatted_args)
} else {
let formatted_args = self
.args
.iter()
.map(|a| a.as_src())
.collect::<Vec<_>>()
.join(", ");
format!("{}<{}>", self.sym.name, formatted_args)
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct SymbolName {
pub name: String,
}
impl Display for SymbolName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name)
}
}
impl SymbolName {
pub fn is_underscore(&self) -> bool {
self.name == "_"
}
pub fn is_placeholder(&self) -> bool {
self.name == "__placeholder" || self.name == "__reserved_word_placeholder"
}
}
impl From<&str> for SymbolName {
fn from(s: &str) -> Self {
SymbolName { name: s.to_owned() }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
pub struct InternedSymbolId(pub usize);
#[derive(Clone, PartialEq, Eq)]
pub struct Symbol {
pub position: Position,
pub name: SymbolName,
pub id: SyntaxId,
pub interned_id: InternedSymbolId,
}
impl Symbol {
pub fn new<S: AsRef<str>>(position: Position, name: S, id_gen: &mut IdGenerator) -> Self {
let name = SymbolName {
name: name.as_ref().to_owned(),
};
Self {
interned_id: id_gen.intern_symbol(&name),
position,
name,
id: id_gen.next(),
}
}
pub fn is_placeholder(&self) -> bool {
self.name.is_placeholder()
}
}
impl std::fmt::Debug for Symbol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if std::env::var("VERBOSE").is_ok() {
f.debug_struct("Symbol")
.field("name", &self.name)
.field("position", &self.position)
.field("id", &self.id)
.field("interned_id", &self.interned_id)
.finish()
} else {
write!(f, "Symbol\"{}\"", self.name.name)
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SymbolWithHint {
pub symbol: Symbol,
pub hint: Option<TypeHint>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BinaryOperatorKind {
Add,
Subtract,
Multiply,
Divide,
Modulo,
Exponent,
Equal,
NotEqual,
LessThan,
LessThanOrEqual,
GreaterThan,
GreaterThanOrEqual,
And,
Or,
StringConcat,
}
impl Display for BinaryOperatorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
BinaryOperatorKind::Add => "+",
BinaryOperatorKind::Subtract => "-",
BinaryOperatorKind::Multiply => "*",
BinaryOperatorKind::Divide => "/",
BinaryOperatorKind::Modulo => "%",
BinaryOperatorKind::Exponent => "^",
BinaryOperatorKind::Equal => "==",
BinaryOperatorKind::NotEqual => "!=",
BinaryOperatorKind::LessThan => "<",
BinaryOperatorKind::LessThanOrEqual => "<=",
BinaryOperatorKind::GreaterThan => ">",
BinaryOperatorKind::GreaterThanOrEqual => ">=",
BinaryOperatorKind::And => "&&",
BinaryOperatorKind::Or => "||",
BinaryOperatorKind::StringConcat => "^",
};
write!(f, "{}", s)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AssignUpdateKind {
Add,
Subtract,
}
impl AssignUpdateKind {
pub fn as_src(&self) -> &'static str {
match self {
AssignUpdateKind::Add => "+=",
AssignUpdateKind::Subtract => "-=",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Pattern {
pub variant_sym: Symbol,
pub payload: Option<LetDestination>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExpressionWithComma {
pub expr: Rc<Expression>,
pub comma: Option<Position>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParenthesizedExpression {
pub open_paren: Position,
pub expr: Rc<Expression>,
pub close_paren: Position,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParenthesizedArguments {
pub open_paren: Position,
pub arguments: Vec<ExpressionWithComma>,
pub close_paren: Position,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LetDestination {
Symbol(Symbol),
Destructure(Vec<Symbol>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Expression_ {
Match(Rc<Expression>, Vec<(Pattern, Block)>),
If(Rc<Expression>, Block, Option<Block>),
While(Rc<Expression>, Block),
ForIn(LetDestination, Rc<Expression>, Block),
Break,
Continue,
Assign(Symbol, Rc<Expression>),
AssignUpdate(Symbol, AssignUpdateKind, Rc<Expression>),
Let(LetDestination, Option<TypeHint>, Rc<Expression>),
Return(Option<Rc<Expression>>),
IntLiteral(i64),
StringLiteral(String),
ListLiteral(Vec<ExpressionWithComma>),
TupleLiteral(Vec<Rc<Expression>>),
StructLiteral(TypeSymbol, Vec<(Symbol, Rc<Expression>)>),
BinaryOperator(Rc<Expression>, BinaryOperatorKind, Rc<Expression>),
Variable(Symbol),
Call(Rc<Expression>, ParenthesizedArguments),
MethodCall(Rc<Expression>, Symbol, ParenthesizedArguments),
DotAccess(Rc<Expression>, Symbol),
FunLiteral(FunInfo),
Assert(Rc<Expression>),
Parentheses(Position, Rc<Expression>, Position),
Invalid,
}
impl Expression_ {
pub(crate) fn is_invalid_or_placeholder(&self) -> bool {
match self {
Expression_::Variable(sym) => sym.is_placeholder(),
Expression_::Invalid => true,
_ => false,
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, Copy)]
pub struct SyntaxId(pub usize);
impl std::fmt::Debug for SyntaxId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "SyntaxId({})", self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum AstId {
Expr(SyntaxId),
Sym(SyntaxId),
TypeSym(SyntaxId),
Import(SyntaxId),
}
impl AstId {
pub fn id(&self) -> SyntaxId {
match self {
AstId::Expr(syntax_id) => *syntax_id,
AstId::Sym(syntax_id) => *syntax_id,
AstId::TypeSym(syntax_id) => *syntax_id,
AstId::Import(syntax_id) => *syntax_id,
}
}
}
#[derive(Debug, Clone)]
pub struct IdGenerator {
pub next_id: SyntaxId,
pub interned: FxHashMap<SymbolName, InternedSymbolId>,
pub intern_id_to_name: FxHashMap<InternedSymbolId, SymbolName>,
}
impl Default for IdGenerator {
fn default() -> Self {
Self {
next_id: SyntaxId(0),
interned: FxHashMap::default(),
intern_id_to_name: FxHashMap::default(),
}
}
}
impl IdGenerator {
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> SyntaxId {
let next_id = self.next_id;
self.next_id = SyntaxId(next_id.0 + 1);
next_id
}
pub fn intern_symbol(&mut self, name: &SymbolName) -> InternedSymbolId {
match self.interned.get(name) {
Some(id) => *id,
None => {
let id = InternedSymbolId(self.interned.len());
self.interned.insert(name.clone(), id);
self.intern_id_to_name.insert(id, name.clone());
id
}
}
}
}
#[derive(Debug, Clone, Default)]
pub struct Vfs {
file_srcs: FxHashMap<PathBuf, String>,
}
impl Vfs {
pub fn insert(&mut self, path: PathBuf, src: String) {
self.file_srcs.insert(path, src);
}
pub fn file_src(&self, path: &Path) -> Option<&String> {
self.file_srcs.get(path)
}
pub fn pos_src(&self, pos: &Position) -> Option<&str> {
match self.file_srcs.get(pos.path.as_path()) {
Some(file) => Some(&file[pos.start_offset..pos.end_offset]),
None => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Expression {
pub position: Position,
pub expr_: Expression_,
pub value_is_used: bool,
pub id: SyntaxId,
}
impl Expression {
pub(crate) fn new(position: Position, expr_: Expression_, id: SyntaxId) -> Self {
Self {
position,
expr_,
value_is_used: true,
id,
}
}
pub fn invalid(position: Position, id: SyntaxId) -> Self {
Self {
position,
expr_: Expression_::Invalid,
value_is_used: true,
id,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Block {
pub open_brace: Position,
pub exprs: Vec<Rc<Expression>>,
pub close_brace: Position,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Visibility {
External(Position),
CurrentFile,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ToplevelExpression(pub Expression);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FunInfo {
pub pos: Position,
pub doc_comment: Option<String>,
pub name_sym: Option<Symbol>,
pub item_id: Option<ToplevelItemId>,
pub type_params: Vec<TypeSymbol>,
pub params: ParenthesizedParameters,
pub return_hint: Option<TypeHint>,
pub body: Block,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParenthesizedParameters {
pub open_paren: Position,
pub params: Vec<SymbolWithHint>,
pub close_paren: Position,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TestInfo {
pub pos: Position,
pub doc_comment: Option<String>,
pub name_sym: Symbol,
pub body: Block,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VariantInfo {
pub name_sym: Symbol,
pub payload_hint: Option<TypeHint>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FieldInfo {
pub sym: Symbol,
pub hint: TypeHint,
pub doc_comment: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumInfo {
pub pos: Position,
pub visibility: Visibility,
pub doc_comment: Option<String>,
pub name_sym: TypeSymbol,
pub type_params: Vec<TypeSymbol>,
pub variants: Vec<VariantInfo>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StructInfo {
pub pos: Position,
pub visibility: Visibility,
pub doc_comment: Option<String>,
pub name_sym: TypeSymbol,
pub type_params: Vec<TypeSymbol>,
pub fields: Vec<FieldInfo>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ImportInfo {
pub pos: Position,
pub path: PathBuf,
pub path_pos: Position,
pub id: SyntaxId,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BuiltinMethodKind {
ListAppend,
ListContains,
ListGet,
ListLen,
PathExists,
PathRead,
StringIndexOf,
StringLen,
StringLines,
StringSubstring,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MethodKind {
BuiltinMethod(BuiltinMethodKind, Option<FunInfo>),
UserDefinedMethod(FunInfo),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MethodInfo {
pub pos: Position,
pub receiver_hint: TypeHint,
pub receiver_sym: Symbol,
pub name_sym: Symbol,
pub kind: MethodKind,
}
impl MethodInfo {
pub fn fun_info(&self) -> Option<&FunInfo> {
match &self.kind {
MethodKind::BuiltinMethod(_, fun_info) => fun_info.as_ref(),
MethodKind::UserDefinedMethod(fun_info) => Some(fun_info),
}
}
pub fn full_name(&self) -> String {
format!("{}::{}", self.receiver_hint.sym, self.name_sym.name)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
pub struct ToplevelItemId(pub usize);
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ToplevelItem {
Fun(Symbol, FunInfo, Visibility),
Method(MethodInfo, Visibility),
Test(TestInfo),
Enum(EnumInfo),
Struct(StructInfo),
Import(ImportInfo),
Expr(ToplevelExpression),
Block(Block),
}
impl ToplevelItem {
pub fn position(&self) -> Position {
match self {
ToplevelItem::Fun(_, fun_info, _) => fun_info.pos.clone(),
ToplevelItem::Method(method_info, _) => method_info.pos.clone(),
ToplevelItem::Test(test_info) => test_info.pos.clone(),
ToplevelItem::Enum(enum_info) => enum_info.pos.clone(),
ToplevelItem::Struct(struct_info) => struct_info.pos.clone(),
ToplevelItem::Import(import_info) => import_info.pos.clone(),
ToplevelItem::Expr(toplevel_expression) => toplevel_expression.0.position.clone(),
ToplevelItem::Block(block) => Position::merge(&block.open_brace, &block.close_brace),
}
}
pub(crate) fn is_invalid_or_placeholder(&self) -> bool {
match self {
ToplevelItem::Fun(symbol, _, _) => symbol.is_placeholder(),
ToplevelItem::Method(method_info, _) => method_info.name_sym.is_placeholder(),
ToplevelItem::Test(test_info) => test_info.name_sym.is_placeholder(),
ToplevelItem::Enum(enum_info) => enum_info.name_sym.is_placeholder(),
ToplevelItem::Struct(struct_info) => struct_info.name_sym.is_placeholder(),
ToplevelItem::Expr(e) => e.0.expr_.is_invalid_or_placeholder(),
ToplevelItem::Block(_) => false,
ToplevelItem::Import(_) => false,
}
}
}