implement first parts of member access
This commit is contained in:
parent
dd97937feb
commit
927b0f52c1
|
@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Integer and boolean arrays (scoreboard and data storage)
|
||||
- Integer map (scoreboard)
|
||||
- Boolean map (tag)
|
||||
- Member access (e.g. `.objective` to get objective name where int is stored)
|
||||
- Return statement
|
||||
- Example: barebones compiler
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -26,7 +26,7 @@ zip = ["shulkerbox?/zip"]
|
|||
cfg-if = "1.0.0"
|
||||
chksum-md5 = { version = "0.1.0", optional = true }
|
||||
colored = "3.0.0"
|
||||
derive_more = { version = "2.0.1", default-features = false, features = ["deref", "deref_mut", "from"] }
|
||||
derive_more = { version = "2.0.1", default-features = false, features = [ "deref", "deref_mut", "from"] }
|
||||
enum-as-inner = "0.6.0"
|
||||
getset = "0.1.2"
|
||||
itertools = "0.14.0"
|
||||
|
@ -35,7 +35,7 @@ pathdiff = "0.2.3"
|
|||
serde = { version = "1.0.217", features = ["derive"], optional = true }
|
||||
serde_json = { version = "1.0.138", optional = true }
|
||||
# shulkerbox = { version = "0.1.0", default-features = false, optional = true }
|
||||
shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "5c4d340162fc16065add448ed387a1ce481c27d6", default-features = false, optional = true }
|
||||
shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "6ff544131b2518b8c92bc4d2de7682efd7141ec4", default-features = false, optional = true }
|
||||
strsim = "0.11.1"
|
||||
strum = { version = "0.27.0", features = ["derive"] }
|
||||
thiserror = "2.0.11"
|
||||
|
|
|
@ -623,6 +623,10 @@ impl Primary {
|
|||
Err(err)
|
||||
}
|
||||
}
|
||||
Self::MemberAccess(_) => {
|
||||
// TODO:
|
||||
Ok(())
|
||||
}
|
||||
Self::Parenthesized(expr) => expr.analyze_semantics(scope, handler),
|
||||
Self::Prefix(prefixed) => match prefixed.operator() {
|
||||
PrefixOperator::LogicalNot(_) => {
|
||||
|
@ -680,7 +684,9 @@ impl Primary {
|
|||
match self {
|
||||
Self::Boolean(_) => expected == ValueType::Boolean,
|
||||
Self::Integer(_) => expected == ValueType::Integer,
|
||||
Self::StringLiteral(_) | Self::MacroStringLiteral(_) => expected == ValueType::String,
|
||||
Self::StringLiteral(_) | Self::MacroStringLiteral(_) => {
|
||||
matches!(expected, ValueType::String | ValueType::Boolean)
|
||||
}
|
||||
Self::FunctionCall(_) => matches!(expected, ValueType::Boolean | ValueType::Integer),
|
||||
Self::Indexed(indexed) => match indexed.object().as_ref() {
|
||||
Self::Identifier(ident) => {
|
||||
|
@ -699,6 +705,10 @@ impl Primary {
|
|||
}
|
||||
_ => false,
|
||||
},
|
||||
Self::MemberAccess(_) => {
|
||||
// TODO:
|
||||
true
|
||||
}
|
||||
Self::Identifier(ident) => match scope.get_variable(ident.span.str()) {
|
||||
Some(VariableType::BooleanStorage) => expected == ValueType::Boolean,
|
||||
Some(VariableType::ScoreboardValue) => expected == ValueType::Integer,
|
||||
|
|
|
@ -175,6 +175,7 @@ impl SourceElement for Expression {
|
|||
/// | Boolean
|
||||
/// | StringLiteral
|
||||
/// | FunctionCall
|
||||
/// | MemberAccess
|
||||
/// | MacroStringLiteral
|
||||
/// | LuaCode
|
||||
/// ```
|
||||
|
@ -190,6 +191,7 @@ pub enum Primary {
|
|||
Boolean(Boolean),
|
||||
StringLiteral(StringLiteral),
|
||||
FunctionCall(FunctionCall),
|
||||
MemberAccess(MemberAccess),
|
||||
MacroStringLiteral(MacroStringLiteral),
|
||||
Lua(Box<LuaCode>),
|
||||
}
|
||||
|
@ -205,6 +207,7 @@ impl SourceElement for Primary {
|
|||
Self::Boolean(bool) => bool.span(),
|
||||
Self::StringLiteral(string_literal) => string_literal.span(),
|
||||
Self::FunctionCall(function_call) => function_call.span(),
|
||||
Self::MemberAccess(member_access) => member_access.span(),
|
||||
Self::MacroStringLiteral(macro_string_literal) => macro_string_literal.span(),
|
||||
Self::Lua(lua_code) => lua_code.span(),
|
||||
}
|
||||
|
@ -443,6 +446,45 @@ impl LuaCode {
|
|||
}
|
||||
}
|
||||
|
||||
/// Represents a member access in the syntax tree.
|
||||
///
|
||||
/// Syntax Synopsis:
|
||||
///
|
||||
/// ```ebnf
|
||||
/// MemberAccess:
|
||||
/// Primary '.' Identifier
|
||||
/// ```
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||
pub struct MemberAccess {
|
||||
/// The parent expression
|
||||
#[get = "pub"]
|
||||
parent: Box<Primary>,
|
||||
/// The dot in the middle
|
||||
#[get = "pub"]
|
||||
dot: Punctuation,
|
||||
/// The member being accessed
|
||||
#[get = "pub"]
|
||||
member: Identifier,
|
||||
}
|
||||
|
||||
impl SourceElement for MemberAccess {
|
||||
fn span(&self) -> Span {
|
||||
self.parent
|
||||
.span()
|
||||
.join(&self.member.span)
|
||||
.expect("invalid span")
|
||||
}
|
||||
}
|
||||
|
||||
impl MemberAccess {
|
||||
/// Dissolves the [`MemberAccess`] into its components.
|
||||
#[must_use]
|
||||
pub fn dissolve(self) -> (Box<Primary>, Punctuation, Identifier) {
|
||||
(self.parent, self.dot, self.member)
|
||||
}
|
||||
}
|
||||
|
||||
impl Parser<'_> {
|
||||
/// Parses an [`Expression`]
|
||||
///
|
||||
|
@ -509,7 +551,7 @@ impl Parser<'_> {
|
|||
/// - If the parser is not at a valid primary expression.
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub fn parse_primary(&mut self, handler: &impl Handler<base::Error>) -> ParseResult<Primary> {
|
||||
match self.stop_at_significant() {
|
||||
let prim = match self.stop_at_significant() {
|
||||
// prefixed expression with '!' or '-'
|
||||
Reading::Atomic(Token::Punctuation(punc)) if matches!(punc.punctuation, '!' | '-') => {
|
||||
// eat the prefix
|
||||
|
@ -702,6 +744,21 @@ impl Parser<'_> {
|
|||
|
||||
Err(err)
|
||||
}
|
||||
}?;
|
||||
|
||||
match self.stop_at_significant() {
|
||||
Reading::Atomic(Token::Punctuation(punc)) if punc.punctuation == '.' => {
|
||||
self.forward();
|
||||
|
||||
let member = self.parse_identifier(handler)?;
|
||||
|
||||
Ok(Primary::MemberAccess(MemberAccess {
|
||||
parent: Box::new(prim),
|
||||
dot: punc,
|
||||
member,
|
||||
}))
|
||||
}
|
||||
_ => Ok(prim),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,12 +17,13 @@ use super::{
|
|||
},
|
||||
Scope, TranspileResult, Transpiler, VariableData,
|
||||
};
|
||||
use crate::lexical::token::{Identifier, StringLiteral};
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
use crate::{
|
||||
base::{self, source_file::SourceElement, Handler, VoidHandler},
|
||||
lexical::token::MacroStringLiteralPart,
|
||||
syntax::syntax_tree::expression::{
|
||||
Binary, BinaryOperator, Expression, PrefixOperator, Primary,
|
||||
Binary, BinaryOperator, Expression, MemberAccess, PrefixOperator, Primary,
|
||||
},
|
||||
transpile::{
|
||||
error::{FunctionArgumentsNotAllowed, MissingValue},
|
||||
|
@ -319,6 +320,9 @@ impl Primary {
|
|||
false
|
||||
}
|
||||
}
|
||||
Self::MemberAccess(_) => {
|
||||
todo!()
|
||||
}
|
||||
#[cfg_attr(not(feature = "lua"), expect(unused_variables))]
|
||||
Self::Lua(lua) => {
|
||||
cfg_if::cfg_if! {
|
||||
|
@ -379,6 +383,11 @@ impl Primary {
|
|||
Self::Parenthesized(parenthesized) => {
|
||||
parenthesized.expression().comptime_eval(scope, handler)
|
||||
}
|
||||
Self::MemberAccess(member_access) => {
|
||||
member_access
|
||||
.parent()
|
||||
.comptime_member_access(member_access, scope, handler)
|
||||
}
|
||||
Self::Prefix(prefix) => {
|
||||
prefix
|
||||
.operand()
|
||||
|
@ -419,6 +428,176 @@ impl Primary {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn comptime_member_access(
|
||||
&self,
|
||||
member_access: &MemberAccess,
|
||||
scope: &Arc<Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> Result<ComptimeValue, NotComptime> {
|
||||
match self {
|
||||
Self::StringLiteral(s) => s.comptime_member_access(member_access, scope, handler),
|
||||
Self::Identifier(ident) => ident.comptime_member_access(member_access, scope, handler),
|
||||
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StringLiteral {
|
||||
fn comptime_member_access(
|
||||
&self,
|
||||
member_access: &MemberAccess,
|
||||
_scope: &Arc<Scope>,
|
||||
_handler: &impl Handler<base::Error>,
|
||||
) -> Result<ComptimeValue, NotComptime> {
|
||||
match member_access.member().span.str() {
|
||||
"length" => Ok(ComptimeValue::Integer(
|
||||
i64::try_from(self.str_content().len())
|
||||
.expect("string literal length should fit in i64"),
|
||||
)),
|
||||
_ => Err(NotComptime {
|
||||
expression: member_access.member().span(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Identifier {
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn comptime_member_access(
|
||||
&self,
|
||||
member_access: &MemberAccess,
|
||||
scope: &Arc<Scope>,
|
||||
_handler: &impl Handler<base::Error>,
|
||||
) -> Result<ComptimeValue, NotComptime> {
|
||||
scope.get_variable(self.span.str()).map_or_else(
|
||||
|| {
|
||||
Err(NotComptime {
|
||||
expression: self.span(),
|
||||
})
|
||||
},
|
||||
|data| match data.as_ref() {
|
||||
VariableData::ComptimeValue { value, .. } => {
|
||||
let value = value.read().unwrap();
|
||||
value.as_ref().map_or_else(
|
||||
|| {
|
||||
Err(NotComptime {
|
||||
expression: self.span(),
|
||||
})
|
||||
},
|
||||
|value| match value {
|
||||
ComptimeValue::String(s) => match member_access.member().span.str() {
|
||||
"length" => Ok(ComptimeValue::Integer(
|
||||
i64::try_from(s.len())
|
||||
.expect("comptime string length should fit in i64"),
|
||||
)),
|
||||
_ => Err(NotComptime {
|
||||
expression: member_access.member().span(),
|
||||
}),
|
||||
},
|
||||
_ => Err(NotComptime {
|
||||
expression: self.span(),
|
||||
}),
|
||||
},
|
||||
)
|
||||
}
|
||||
VariableData::BooleanStorage { storage_name, path } => {
|
||||
match member_access.member().span.str() {
|
||||
"storage" => Ok(ComptimeValue::String(storage_name.to_owned())),
|
||||
"path" => Ok(ComptimeValue::String(path.to_owned())),
|
||||
_ => Err(NotComptime {
|
||||
expression: member_access.member().span(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
VariableData::BooleanStorageArray {
|
||||
storage_name,
|
||||
paths: _,
|
||||
} => {
|
||||
match member_access.member().span.str() {
|
||||
"storage" => Ok(ComptimeValue::String(storage_name.to_owned())),
|
||||
"paths" => {
|
||||
// TODO: implement when comptime arrays are implemented
|
||||
Err(NotComptime {
|
||||
expression: member_access.member().span(),
|
||||
})
|
||||
}
|
||||
_ => Err(NotComptime {
|
||||
expression: member_access.member().span(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
VariableData::Function { path, .. } => {
|
||||
match member_access.member().span.str() {
|
||||
"path" => {
|
||||
#[expect(clippy::option_if_let_else)]
|
||||
if let Some(path) = path.get() {
|
||||
Ok(ComptimeValue::String(path.to_owned()))
|
||||
} else {
|
||||
// TODO: add support for non already compiled functions
|
||||
Err(NotComptime {
|
||||
expression: member_access.member().span(),
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => Err(NotComptime {
|
||||
expression: member_access.member().span(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
VariableData::InternalFunction { .. } => Err(NotComptime {
|
||||
expression: member_access.member().span(),
|
||||
}),
|
||||
VariableData::MacroParameter { macro_name, .. } => {
|
||||
match member_access.member().span.str() {
|
||||
"name" => Ok(ComptimeValue::String(macro_name.to_owned())),
|
||||
_ => Err(NotComptime {
|
||||
expression: member_access.member().span(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
VariableData::Scoreboard { objective } => match member_access.member().span.str() {
|
||||
"objective" => Ok(ComptimeValue::String(objective.to_owned())),
|
||||
_ => Err(NotComptime {
|
||||
expression: member_access.member().span(),
|
||||
}),
|
||||
},
|
||||
VariableData::ScoreboardArray {
|
||||
objective,
|
||||
targets: _,
|
||||
} => {
|
||||
match member_access.member().span.str() {
|
||||
"objective" => Ok(ComptimeValue::String(objective.to_owned())),
|
||||
"targets" => {
|
||||
// TODO: implement when comptime arrays are implemented
|
||||
Err(NotComptime {
|
||||
expression: member_access.member().span(),
|
||||
})
|
||||
}
|
||||
_ => Err(NotComptime {
|
||||
expression: member_access.member().span(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
VariableData::ScoreboardValue { objective, target } => {
|
||||
match member_access.member().span.str() {
|
||||
"objective" => Ok(ComptimeValue::String(objective.to_owned())),
|
||||
"target" => Ok(ComptimeValue::String(target.to_owned())),
|
||||
_ => Err(NotComptime {
|
||||
expression: member_access.member().span(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
VariableData::Tag { tag_name } => match member_access.member().span.str() {
|
||||
"name" => Ok(ComptimeValue::String(tag_name.to_owned())),
|
||||
_ => Err(NotComptime {
|
||||
expression: member_access.member().span(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
|
@ -699,6 +878,7 @@ impl Transpiler {
|
|||
Primary::Parenthesized(parenthesized) => {
|
||||
self.transpile_expression(parenthesized.expression(), target, scope, handler)
|
||||
}
|
||||
Primary::MemberAccess(_) => todo!(),
|
||||
Primary::Lua(lua) =>
|
||||
{
|
||||
#[expect(clippy::option_if_let_else)]
|
||||
|
@ -1261,6 +1441,7 @@ impl Transpiler {
|
|||
Err(err)
|
||||
}
|
||||
}
|
||||
Primary::MemberAccess(_) => todo!(),
|
||||
Primary::Prefix(prefix) => match prefix.operator() {
|
||||
PrefixOperator::LogicalNot(_) => {
|
||||
let (cmds, cond) = self.transpile_primary_expression_as_condition(
|
||||
|
|
|
@ -357,6 +357,7 @@ impl Transpiler {
|
|||
}
|
||||
}
|
||||
}
|
||||
Expression::Primary(Primary::MemberAccess(_)) => todo!(),
|
||||
Expression::Primary(
|
||||
Primary::Parenthesized(_)
|
||||
| Primary::Prefix(_)
|
||||
|
|
|
@ -653,6 +653,8 @@ impl Transpiler {
|
|||
}
|
||||
},
|
||||
|
||||
Primary::MemberAccess(_) => todo!(),
|
||||
|
||||
Primary::Parenthesized(parenthesized) => match parenthesized.expression().as_ref() {
|
||||
Expression::Primary(expression) => {
|
||||
self.transpile_run_expression(expression, scope, handler)
|
||||
|
@ -660,7 +662,7 @@ impl Transpiler {
|
|||
Expression::Binary(bin) => match bin.comptime_eval(scope, handler) {
|
||||
Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
||||
Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
|
||||
_ => {
|
||||
Ok(_) => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expression: bin.span(),
|
||||
expected_type: ExpectedType::String,
|
||||
|
@ -668,6 +670,11 @@ impl Transpiler {
|
|||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
}
|
||||
Err(not_comptime) => {
|
||||
let err = TranspileError::NotComptime(not_comptime);
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue