Skip to content

Commit

Permalink
Class properties now working
Browse files Browse the repository at this point in the history
  • Loading branch information
bdw429s committed Jan 6, 2024
1 parent 902e38b commit 194e6f2
Show file tree
Hide file tree
Showing 22 changed files with 708 additions and 138 deletions.
25 changes: 13 additions & 12 deletions src/main/antlr/CFParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@ importStatement:
include: INCLUDE expression eos?;

component:
// TODO Properties
javadoc? (preannotation)* ABSTRACT? COMPONENT identifier? postannotation* LBRACE
javadoc? (preannotation)* ABSTRACT? COMPONENT postannotation* LBRACE property*
functionOrStatement* RBRACE;

interface:
javadoc? (preannotation)* INTERFACE identifier? postannotation* LBRACE interfaceFunction* RBRACE
;
javadoc? (preannotation)* INTERFACE postannotation* LBRACE interfaceFunction* RBRACE;

// TODO: default method implementations
interfaceFunction: functionSignature eos;
Expand All @@ -44,9 +42,13 @@ param: (REQUIRED)? (type)? identifier (EQUAL expression)? postannotation*

preannotation: AT fqn (literalExpression)*;

// TODO: check if keys can be quoted here and values can be expressions
postannotation:
key = identifier ((EQUAL | COLON) value = literalExpression)?;
key = identifier ((EQUAL | COLON) value = attributeSimple)?;

// This allows [1, 2, 3], "foo", or foo Adobe allows more chars than an identifer, Lucee allows darn
// near anything, but ANTLR is incapable of matching any tokens until the next whitespace. The
// literalExpression is just a BoxLang flourish to allow for more flexible expressions.
attributeSimple: literalExpression | identifier;

returnType: type | identifier;

Expand All @@ -66,12 +68,10 @@ type:

functionOrStatement: function | statement;

javadoc: JAVADOC_COMMENT;

property:
PROPERTY (identifier EQUAL expression)+ (
TYPE EQUAL stringLiteral
)? (DEFAULT EQUAL stringLiteral)? eos?;
javadoc? (preannotation)* PROPERTY postannotation* eos;

javadoc: JAVADOC_COMMENT;

anonymousFunction:
FUNCTION LPAREN paramList? RPAREN (postannotation)* (
Expand Down Expand Up @@ -239,7 +239,8 @@ reservedKeyword:
| CONTAIN
| JAVA
| MESSAGE
| NULL;
| NULL
| PROPERTY;
// | ASSERT
scope:
APPLICATION
Expand Down
16 changes: 12 additions & 4 deletions src/main/java/ortus/boxlang/ast/BoxClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,21 @@
import ortus.boxlang.ast.statement.BoxAnnotation;
import ortus.boxlang.ast.statement.BoxDocumentationAnnotation;
import ortus.boxlang.ast.statement.BoxImport;
import ortus.boxlang.ast.statement.BoxProperty;

/**
* Root node for a script (program) cf/cfm/box
* Root node for a Class
*/
public class BoxClass extends BoxNode {

private final List<BoxStatement> body;
private final List<BoxImport> imports;
private final List<BoxAnnotation> annotations;
private final List<BoxDocumentationAnnotation> documentation;
// TODO: properties
private final List<BoxProperty> properties;

/**
* Creates an AST for a program which is represented by a list of statements
* Creates an AST for a Class
*
* @param statements list of the statements nodes
* @param position position within the source code
Expand All @@ -43,7 +44,7 @@ public class BoxClass extends BoxNode {
* @see BoxStatement
*/
public BoxClass( List<BoxImport> imports, List<BoxStatement> body, List<BoxAnnotation> annotations,
List<BoxDocumentationAnnotation> documentation, Position position,
List<BoxDocumentationAnnotation> documentation, List<BoxProperty> properties, Position position,
String sourceText ) {
super( position, sourceText );
this.body = body;
Expand All @@ -54,6 +55,8 @@ public BoxClass( List<BoxImport> imports, List<BoxStatement> body, List<BoxAnnot
this.documentation.forEach( arg -> arg.setParent( this ) );
this.imports = imports;
this.imports.forEach( arg -> arg.setParent( this ) );
this.properties = properties;
this.properties.forEach( arg -> arg.setParent( this ) );
}

public List<BoxStatement> getBody() {
Expand All @@ -72,6 +75,10 @@ public List<BoxImport> getImports() {
return imports;
}

public List<BoxProperty> getProperties() {
return properties;
}

@Override
public Map<String, Object> toMap() {
Map<String, Object> map = super.toMap();
Expand All @@ -80,6 +87,7 @@ public Map<String, Object> toMap() {
map.put( "body", body.stream().map( BoxStatement::toMap ).collect( java.util.stream.Collectors.toList() ) );
map.put( "annotations", annotations.stream().map( BoxAnnotation::toMap ).collect( java.util.stream.Collectors.toList() ) );
map.put( "documentation", documentation.stream().map( BoxDocumentationAnnotation::toMap ).collect( java.util.stream.Collectors.toList() ) );
map.put( "properties", properties.stream().map( BoxProperty::toMap ).collect( java.util.stream.Collectors.toList() ) );
return map;
}
}
10 changes: 8 additions & 2 deletions src/main/java/ortus/boxlang/ast/statement/BoxAnnotation.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ public BoxAnnotation( BoxFQN key, BoxExpr value, Position position, String sourc
this.key = key;
this.key.setParent( this );
this.value = value;
this.value.setParent( this );
if ( this.value != null ) {
this.value.setParent( this );
}
}

public BoxFQN getKey() {
Expand All @@ -63,7 +65,11 @@ public Map<String, Object> toMap() {
Map<String, Object> map = super.toMap();

map.put( "key", key.toMap() );
map.put( "value", value.toMap() );
if ( value != null ) {
map.put( "value", value.toMap() );
} else {
map.put( "value", null );
}
return map;
}
}
66 changes: 66 additions & 0 deletions src/main/java/ortus/boxlang/ast/statement/BoxProperty.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* [BoxLang]
*
* Copyright [2023] [Ortus Solutions, Corp]
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package ortus.boxlang.ast.statement;

import java.util.List;
import java.util.Map;

import ortus.boxlang.ast.BoxNode;
import ortus.boxlang.ast.BoxStatement;
import ortus.boxlang.ast.Position;

/**
* Root node for a property
*/
public class BoxProperty extends BoxNode {

private final List<BoxAnnotation> annotations;
private final List<BoxDocumentationAnnotation> documentation;

/**
* Creates an AST for a program which is represented by a list of statements
*
* @param statements list of the statements nodes
* @param position position within the source code
* @param sourceText source code
*
* @see Position
* @see BoxStatement
*/
public BoxProperty( List<BoxAnnotation> annotations, List<BoxDocumentationAnnotation> documentation, Position position, String sourceText ) {
super( position, sourceText );
this.annotations = annotations;
this.annotations.forEach( arg -> arg.setParent( this ) );
this.documentation = documentation;
this.documentation.forEach( arg -> arg.setParent( this ) );
}

public List<BoxAnnotation> getAnnotations() {
return annotations;
}

public List<BoxDocumentationAnnotation> getDocumentation() {
return documentation;
}

@Override
public Map<String, Object> toMap() {
Map<String, Object> map = super.toMap();

map.put( "annotations", annotations.stream().map( BoxAnnotation::toMap ).collect( java.util.stream.Collectors.toList() ) );
map.put( "documentation", documentation.stream().map( BoxDocumentationAnnotation::toMap ).collect( java.util.stream.Collectors.toList() ) );
return map;
}
}
70 changes: 66 additions & 4 deletions src/main/java/ortus/boxlang/parser/BoxCFParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
import ortus.boxlang.ast.statement.BoxIfElse;
import ortus.boxlang.ast.statement.BoxImport;
import ortus.boxlang.ast.statement.BoxInclude;
import ortus.boxlang.ast.statement.BoxProperty;
import ortus.boxlang.ast.statement.BoxRethrow;
import ortus.boxlang.ast.statement.BoxReturn;
import ortus.boxlang.ast.statement.BoxReturnType;
Expand Down Expand Up @@ -290,6 +291,7 @@ private BoxNode toAst( File file, ComponentContext component, List<BoxImport> im
List<BoxStatement> body = new ArrayList<>();
List<BoxAnnotation> annotations = new ArrayList<>();
List<BoxDocumentationAnnotation> documentation = new ArrayList<>();
List<BoxProperty> property = new ArrayList<>();

component.functionOrStatement().forEach( stmt -> {
body.add( toAst( file, stmt ) );
Expand All @@ -303,7 +305,11 @@ private BoxNode toAst( File file, ComponentContext component, List<BoxImport> im
for ( CFParser.PostannotationContext annotation : component.postannotation() ) {
annotations.add( toAst( file, annotation ) );
}
return new BoxClass( imports, body, annotations, documentation, getPosition( component ), getSourceText( component ) );
for ( CFParser.PropertyContext annotation : component.property() ) {
property.add( toAst( file, annotation ) );
}

return new BoxClass( imports, body, annotations, documentation, property, getPosition( component ), getSourceText( component ) );
}

/**
Expand Down Expand Up @@ -1529,6 +1535,15 @@ private BoxStatement toAst( File file, CFParser.FunctionContext node ) {
return new BoxFunctionDeclaration( modifier, name, returnType, args, annotations, documentation, body, getPosition( node ), getSourceText( node ) );
}

/**
* Converts the JavadocContext parser rule to the corresponding AST nodes.
*
* @param file source file, if any
* @param node ANTLR JavadocContext rule
*
* @return corresponding AST JavadocContext
*
*/
private List<BoxDocumentationAnnotation> toAst( File file, CFParser.JavadocContext node ) {
List<BoxDocumentationAnnotation> documentation = new ArrayList<>();
try {
Expand All @@ -1546,6 +1561,26 @@ private List<BoxDocumentationAnnotation> toAst( File file, CFParser.JavadocConte
return documentation;
}

/**
* Converts the AttributeSimple parser rule to the corresponding AST node.
*
* @param file source file, if any
* @param node ANTLR AttributeSimpleContext rule
*
* @return corresponding AST AttributeSimpleContext
*
*/
private BoxExpr toAst( File file, CFParser.AttributeSimpleContext node ) {
if ( node.literalExpression() != null ) {
return toAst( file, node.literalExpression() );
} else if ( node.identifier() != null ) {
// Converting an identifer to a string literal here in the AST removes ambiguity, but also loses the
// lexical context of the original source code.
return new BoxStringLiteral( node.identifier().getText(), getPosition( node ), getSourceText( node ) );
}
throw new IllegalStateException( "AttributeSimple not implemented: " + getSourceText( node ) );
}

/**
* Converts the pre annotation parser rule to the corresponding AST node.
*
Expand All @@ -1568,7 +1603,7 @@ private BoxAnnotation toAst( File file, CFParser.PreannotationContext annotation
}
avalue = new BoxArrayLiteral( values, getPosition( annotation ), getSourceText( annotation ) );
} else {
avalue = new BoxStringLiteral( "", getPosition( annotation ), getSourceText( annotation ) );
avalue = null;
}
return new BoxAnnotation( ( BoxFQN ) aname, avalue, getPosition( annotation ), getSourceText( annotation ) );
}
Expand Down Expand Up @@ -1621,14 +1656,41 @@ private BoxArgumentDeclaration toAst( File file, CFParser.ParamContext node ) {
*/
private BoxAnnotation toAst( File file, CFParser.PostannotationContext node ) {

BoxFQN name = new BoxFQN( node.key.IDENTIFIER().getText(), getPosition( node.key ), getSourceText( node.key ) );
BoxFQN name = new BoxFQN( node.key.getText(), getPosition( node.key ), getSourceText( node.key ) );
BoxExpr value;
if ( node.value != null ) {
value = toAst( file, node.value );
} else {
value = new BoxStringLiteral( "", getPosition( node.key ), getSourceText( node.key ) );
value = null;
}
return new BoxAnnotation( name, value, getPosition( node ), getSourceText( node ) );
}

/**
* Converts a Property into the corresponding AST node
*
* @param file source file, if any
* @param node ANTLR PropertyContext rule
*
* @return corresponding AST PropertyContext
*
*/
private BoxProperty toAst( File file, CFParser.PropertyContext node ) {
List<BoxAnnotation> annotations = new ArrayList<>();
List<BoxDocumentationAnnotation> documentation = new ArrayList<>();

for ( CFParser.PreannotationContext annotation : node.preannotation() ) {
annotations.add( toAst( file, annotation ) );
}
for ( CFParser.PostannotationContext annotation : node.postannotation() ) {
annotations.add( toAst( file, annotation ) );
}

if ( node.javadoc() != null ) {
documentation.addAll( toAst( file, node.javadoc() ) );
}

return new BoxProperty( annotations, documentation, getPosition( node ), getSourceText( node ) );
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@
*/
package ortus.boxlang.runtime.runnables;

import java.util.Map;

import ortus.boxlang.runtime.context.IBoxContext;
import ortus.boxlang.runtime.dynamic.IReferenceable;
import ortus.boxlang.runtime.scopes.IScope;
import ortus.boxlang.runtime.scopes.Key;
import ortus.boxlang.runtime.types.Property;
import ortus.boxlang.runtime.types.Struct;
import ortus.boxlang.runtime.types.meta.BoxMeta;

Expand Down Expand Up @@ -57,6 +60,11 @@ public interface IClassRunnable extends ITemplateRunnable, IReferenceable {
*/
public Struct getDocumentation();

/**
* Get the properties
*/
public Map<Key, Property> getProperties();

/**
* Run the pseudo constructor
*/
Expand Down
Loading

0 comments on commit 194e6f2

Please sign in to comment.