Skip to content

Commit

Permalink
Merge pull request #132 from ortus-boxlang/development
Browse files Browse the repository at this point in the history
Beta 21
  • Loading branch information
jclausen authored Nov 1, 2024
2 parents 683a52d + 6c35963 commit 3cc36ce
Show file tree
Hide file tree
Showing 74 changed files with 1,242 additions and 498 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ dependencies {
testImplementation "com.google.truth:truth:1.+"
testImplementation "commons-cli:commons-cli:1.9.0"
// https://wiremock.org/
testImplementation "org.wiremock:wiremock:3.9.1"
testImplementation "org.wiremock:wiremock:3.9.2"
// https://mvnrepository.com/artifact/org.apache.derby/derby
testImplementation 'org.apache.derby:derby:10.17.1.0'
testImplementation 'io.undertow:undertow-core:2.3.18.Final'
Expand Down Expand Up @@ -128,7 +128,7 @@ dependencies {
// https://mvnrepository.com/artifact/org.slf4j/slf4j-api
implementation 'org.slf4j:slf4j-api:2.0.16'
// https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
implementation 'ch.qos.logback:logback-classic:1.5.11'
implementation 'ch.qos.logback:logback-classic:1.5.12'
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP
implementation 'com.zaxxer:HikariCP:6.0.0'
// https://mvnrepository.com/artifact/org.ow2.asm/asm-tree
Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#Fri Oct 18 14:34:03 UTC 2024
#Fri Oct 25 14:06:18 UTC 2024
antlrVersion=4.13.1
jdkVersion=21
version=1.0.0-beta20
version=1.0.0-beta21
3 changes: 1 addition & 2 deletions src/main/antlr/BoxScriptGrammar.g4
Original file line number Diff line number Diff line change
Expand Up @@ -539,14 +539,13 @@ el2
| LPAREN expression RPAREN # exprPrecedence // (foo)
| new # exprNew // new foo.bar.Baz()
| el2 LPAREN argumentList? RPAREN # exprFunctionCall // foo(bar, baz)
| el2 QM? DOT el2 # exprDotAccess // xc.y?.z recursive
| el2 (QM? DOT | COLONCOLON) el2 # exprDotOrColonAccess // xc.y?.z or foo::bar recursive
| el2 QM? DOT_FLOAT_LITERAL # exprDotFloat // foo.50
| el2 QM? DOT_NUMBER_PREFIXED_IDENTIFIER # exprDotFloatID // foo.50bar
| el2 LBRACKET expression RBRACKET # exprArrayAccess // foo[bar]
| <assoc = right> op = (NOT | BANG | MINUS | PLUS) el2 # exprUnary // !foo, -foo, +foo
| <assoc = right> op = (PLUSPLUS | MINUSMINUS | BITWISE_COMPLEMENT) el2 # exprPrefix // ++foo, --foo, ~foo
| el2 op = (PLUSPLUS | MINUSMINUS) # exprPostfix // foo++, bar--
| el2 COLONCOLON el2 # exprStaticAccess // foo::bar
| el2 POWER el2 # exprPower // foo ^ bar
| el2 op = (STAR | SLASH | PERCENT | MOD | BACKSLASH) el2 # exprMult // foo * bar
| el2 op = (PLUS | MINUS) el2 # exprAdd // foo + bar
Expand Down
3 changes: 1 addition & 2 deletions src/main/antlr/CFGrammar.g4
Original file line number Diff line number Diff line change
Expand Up @@ -518,14 +518,13 @@ el2
| LPAREN expression RPAREN # exprPrecedence // ( foo )
| new # exprNew // new foo.bar.Baz()
| el2 LPAREN argumentList? RPAREN # exprFunctionCall // foo(bar, baz)
| el2 QM? DOT DOT? el2 # exprDotAccess // xc.y?.z recursive and Adobe's stupid foo..bar bug they allow
| el2 (QM? DOT DOT? | COLONCOLON) el2 # exprDotOrColonAccess // xc.y?.z or foo::bar recursive and Adobe's stupid foo..bar bug they allow
| el2 QM? DOT? DOT_FLOAT_LITERAL # exprDotFloat // foo.50
| el2 QM? DOT? DOT_NUMBER_PREFIXED_IDENTIFIER # exprDotFloatID // foo.50bar
| el2 LBRACKET expression RBRACKET # exprArrayAccess // foo[bar]
| <assoc = right> op = (NOT | BANG | MINUS | PLUS) el2 # exprUnary // !foo, -foo, +foo
| <assoc = right> op = (PLUSPLUS | MINUSMINUS | BITWISE_COMPLEMENT) el2 # exprPrefix // ++foo, --foo, ~foo
| el2 op = (PLUSPLUS | MINUSMINUS) # exprPostfix // foo++, bar--
| el2 COLONCOLON el2 # exprStaticAccess // foo::bar
| el2 POWER el2 # exprPower // foo ^ bar
| el2 op = (STAR | SLASH | PERCENT | MOD | BACKSLASH) el2 # exprMult // foo * bar
| el2 op = (PLUS | MINUS) el2 # exprAdd // foo + bar
Expand Down
32 changes: 32 additions & 0 deletions src/main/java/ortus/boxlang/compiler/asmboxpiler/AsmHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;

import ortus.boxlang.compiler.asmboxpiler.transformer.ReturnValueContext;
import ortus.boxlang.compiler.asmboxpiler.transformer.TransformerContext;
Expand All @@ -33,6 +34,7 @@
import ortus.boxlang.compiler.ast.BoxNode;
import ortus.boxlang.compiler.ast.BoxStatement;
import ortus.boxlang.compiler.ast.expression.BoxArgument;
import ortus.boxlang.compiler.ast.expression.BoxIdentifier;
import ortus.boxlang.compiler.ast.statement.BoxFunctionDeclaration;
import ortus.boxlang.compiler.ast.statement.BoxReturnType;
import ortus.boxlang.compiler.ast.statement.BoxType;
Expand Down Expand Up @@ -987,4 +989,34 @@ public static void addLazySingleton( ClassVisitor classVisitor, Type type, Consu
methodVisitor.visitMaxs( 0, 0 );
methodVisitor.visitEnd();
}

/**
* Create the nodes for loading a class from the class locator
*
* classLocator.load( context, "NameOfClass", imports )
*
* @param transpiler The transpiler
* @param identifier The identifier of the class to load
*
* @return The nodes
*/
public static List<AbstractInsnNode> loadClass( Transpiler transpiler, BoxIdentifier identifier ) {
List<AbstractInsnNode> nodes = new ArrayList<>();
nodes.add( new VarInsnNode( Opcodes.ALOAD, 2 ) );
transpiler.getCurrentMethodContextTracker().ifPresent( ( t ) -> nodes.addAll( t.loadCurrentContext() ) );
nodes.add( new LdcInsnNode( identifier.getName() ) );
nodes.add( new FieldInsnNode( Opcodes.GETSTATIC,
transpiler.getProperty( "packageName" ).replace( '.', '/' )
+ "/"
+ transpiler.getProperty( "classname" ),
"imports",
Type.getDescriptor( List.class ) ) );
nodes.add( new MethodInsnNode( Opcodes.INVOKEVIRTUAL,
Type.getInternalName( ClassLocator.class ),
"load",
Type.getMethodDescriptor( Type.getType( DynamicObject.class ), Type.getType( IBoxContext.class ), Type.getType( String.class ),
Type.getType( List.class ) ),
false ) );
return nodes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,13 @@ public List<AbstractInsnNode> transformEquals( BoxExpression left, List<Abstract

List<AbstractInsnNode> nodes = new ArrayList<>();
if ( furthestLeft instanceof BoxIdentifier id ) {
if ( transpiler.matchesImport( id.getName() ) && transpiler.getProperty( "sourceType" ).toLowerCase().startsWith( "box" ) ) {
throw new ExpressionException( "You cannot assign a variable with the same name as an import: [" + id.getName() + "]",
furthestLeft.getPosition(), furthestLeft.getSourceText() );
// imported.foo = 5 is ok, but imported = 5 is not
if ( left instanceof BoxIdentifier idl && transpiler.matchesImport( idl.getName() )
&& transpiler.getProperty( "sourceType" ).toLowerCase().startsWith( "box" ) ) {
throw new ExpressionException( "You cannot assign a variable with the same name as an import: [" + idl.getName() + "]",
idl.getPosition(), idl.getSourceText() );
}

/*
* Referencer.setDeep(
* ${contextName},
Expand Down Expand Up @@ -229,19 +232,29 @@ public List<AbstractInsnNode> transformEquals( BoxExpression left, List<Abstract
nodes.add( new InsnNode( Opcodes.ACONST_NULL ) );
}

tracker.ifPresent( t -> nodes.addAll( t.loadCurrentContext() ) );
nodes.addAll( transpiler.createKey( id.getName() ) );
tracker.ifPresent( t -> nodes.addAll( t.loadCurrentContext() ) );
nodes.add( new MethodInsnNode( Opcodes.INVOKEINTERFACE,
Type.getInternalName( IBoxContext.class ),
"getDefaultAssignmentScope",
Type.getMethodDescriptor( Type.getType( IScope.class ) ),
true ) );
nodes.add( new MethodInsnNode( Opcodes.INVOKEINTERFACE,
Type.getInternalName( IBoxContext.class ),
"scopeFindNearby",
Type.getMethodDescriptor( Type.getType( IBoxContext.ScopeSearchResult.class ), Type.getType( Key.class ), Type.getType( IScope.class ) ),
true ) );
Class<?> baseObjectClass;
// If id is an imported class name, load the class directly instead of searching scopes for it
boolean isBoxSyntax = transpiler.getProperty( "sourceType" ).toLowerCase().startsWith( "box" );
if ( transpiler.matchesImport( id.getName() ) && isBoxSyntax ) {
baseObjectClass = Object.class;
nodes.addAll( AsmHelper.loadClass( transpiler, id ) );
} else {
// Otherwise, search for varible in scopes
baseObjectClass = IBoxContext.ScopeSearchResult.class;
tracker.ifPresent( t -> nodes.addAll( t.loadCurrentContext() ) );
nodes.addAll( transpiler.createKey( id.getName() ) );
tracker.ifPresent( t -> nodes.addAll( t.loadCurrentContext() ) );
nodes.add( new MethodInsnNode( Opcodes.INVOKEINTERFACE,
Type.getInternalName( IBoxContext.class ),
"getDefaultAssignmentScope",
Type.getMethodDescriptor( Type.getType( IScope.class ) ),
true ) );
nodes.add( new MethodInsnNode( Opcodes.INVOKEINTERFACE,
Type.getInternalName( IBoxContext.class ),
"scopeFindNearby",
Type.getMethodDescriptor( Type.getType( IBoxContext.ScopeSearchResult.class ), Type.getType( Key.class ), Type.getType( IScope.class ) ),
true ) );
}

nodes.addAll( jRight );

Expand All @@ -255,7 +268,7 @@ public List<AbstractInsnNode> transformEquals( BoxExpression left, List<Abstract
Type.getType( IBoxContext.class ),
Type.BOOLEAN_TYPE,
Type.getType( Key.class ),
Type.getType( IBoxContext.ScopeSearchResult.class ),
Type.getType( baseObjectClass ),
Type.getType( Object.class ),
Type.getType( Key[].class ) ),
false ) );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,17 @@
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.VarInsnNode;

import ortus.boxlang.compiler.asmboxpiler.AsmHelper;
import ortus.boxlang.compiler.asmboxpiler.Transpiler;
import ortus.boxlang.compiler.asmboxpiler.transformer.AbstractTransformer;
import ortus.boxlang.compiler.asmboxpiler.transformer.ReturnValueContext;
import ortus.boxlang.compiler.asmboxpiler.transformer.TransformerContext;
import ortus.boxlang.compiler.ast.BoxNode;
import ortus.boxlang.compiler.ast.expression.BoxIdentifier;
import ortus.boxlang.runtime.context.IBoxContext;
import ortus.boxlang.runtime.interop.DynamicObject;
import ortus.boxlang.runtime.loader.ClassLocator;
import ortus.boxlang.runtime.scopes.IScope;
import ortus.boxlang.runtime.scopes.Key;

Expand All @@ -51,21 +47,8 @@ public List<AbstractInsnNode> transform( BoxNode node, TransformerContext contex
List<AbstractInsnNode> nodes = new ArrayList<>();

if ( transpiler.matchesImport( identifier.getName() ) && transpiler.getProperty( "sourceType" ).toLowerCase().startsWith( "box" ) ) {
nodes.add( new VarInsnNode( Opcodes.ALOAD, 2 ) );
nodes.add( new VarInsnNode( Opcodes.ALOAD, 1 ) );
nodes.add( new LdcInsnNode( identifier.getName() ) );
nodes.add( new FieldInsnNode( Opcodes.GETSTATIC,
transpiler.getProperty( "packageName" ).replace( '.', '/' )
+ "/"
+ transpiler.getProperty( "classname" ),
"imports",
Type.getDescriptor( List.class ) ) );
nodes.add( new MethodInsnNode( Opcodes.INVOKEVIRTUAL,
Type.getInternalName( ClassLocator.class ),
"load",
Type.getMethodDescriptor( Type.getType( DynamicObject.class ), Type.getType( IBoxContext.class ), Type.getType( String.class ),
Type.getType( List.class ) ),
false ) );
// If id is an imported class name, load the class directly instead of searching scopes for it
nodes.addAll( AsmHelper.loadClass( transpiler, identifier ) );
} else {
transpiler.getCurrentMethodContextTracker().ifPresent( ( t ) -> nodes.addAll( t.loadCurrentContext() ) );
nodes.addAll( transpiler.createKey( identifier.getName() ) );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,22 @@ public Node transformEquals( BoxExpression left, Expression jRight, BoxAssignmen
}

if ( furthestLeft instanceof BoxIdentifier id ) {
if ( transpiler.matchesImport( id.getName() ) && transpiler.getProperty( "sourceType" ).toLowerCase().startsWith( "box" ) ) {
throw new ExpressionException( "You cannot assign a variable with the same name as an import: [" + id.getName() + "]",
furthestLeft.getPosition(), furthestLeft.getSourceText() );
boolean isBoxSyntax = transpiler.getProperty( "sourceType" ).toLowerCase().startsWith( "box" );
// imported.foo = 5 is ok, but imported = 5 is not
if ( left instanceof BoxIdentifier idl && transpiler.matchesImport( idl.getName() ) && isBoxSyntax ) {
throw new ExpressionException( "You cannot assign a variable with the same name as an import: [" + idl.getName() + "]",
idl.getPosition(), idl.getSourceText() );
}

String baseObjTemplate = "${contextName}.scopeFindNearby( ${accessKey}, ${contextName}.getDefaultAssignmentScope() ),";
// imported.foo needs to swap out the furthest left object
if ( transpiler.matchesImport( id.getName() ) && isBoxSyntax ) {
baseObjTemplate = "classLocator.load( ${contextName}, \"${accessName}\", imports ),";
}

Node keyNode = createKey( id.getName() );
String thisKey = keyNode.toString();
values.put( "accessName", id.getName() );
values.put( "accessKey", thisKey );
values.put( "mustBeScopeName", mustBeScopeName == null ? "null" : createKey( mustBeScopeName ).toString() );
values.put( "hasFinal", hasFinal ? "true" : "false" );
Expand All @@ -227,11 +236,11 @@ public Node transformEquals( BoxExpression left, Expression jRight, BoxAssignmen
${contextName},
${hasFinal},
${mustBeScopeName},
${contextName}.scopeFindNearby( ${accessKey}, ${contextName}.getDefaultAssignmentScope() ),
${right}
${accessKeys}
)
""";
""" + baseObjTemplate + """
${right}
${accessKeys}
)
""";
} else {
if ( accessKeys.size() == 0 ) {
throw new ExpressionException( "You cannot assign a value to " + left.getClass().getSimpleName(), left.getPosition(), left.getSourceText() );
Expand Down
26 changes: 18 additions & 8 deletions src/main/java/ortus/boxlang/compiler/parser/BoxScriptParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import ortus.boxlang.compiler.ast.expression.BoxDecimalLiteral;
import ortus.boxlang.compiler.ast.expression.BoxDotAccess;
import ortus.boxlang.compiler.ast.expression.BoxExpressionInvocation;
import ortus.boxlang.compiler.ast.expression.BoxFQN;
import ortus.boxlang.compiler.ast.expression.BoxFunctionInvocation;
import ortus.boxlang.compiler.ast.expression.BoxIdentifier;
import ortus.boxlang.compiler.ast.expression.BoxIntegerLiteral;
Expand All @@ -59,6 +60,8 @@
import ortus.boxlang.compiler.ast.expression.BoxNull;
import ortus.boxlang.compiler.ast.expression.BoxParenthesis;
import ortus.boxlang.compiler.ast.expression.BoxScope;
import ortus.boxlang.compiler.ast.expression.BoxStaticAccess;
import ortus.boxlang.compiler.ast.expression.BoxStaticMethodInvocation;
import ortus.boxlang.compiler.ast.expression.BoxStringLiteral;
import ortus.boxlang.compiler.ast.expression.BoxStructLiteral;
import ortus.boxlang.compiler.toolchain.BoxExpressionVisitor;
Expand Down Expand Up @@ -548,13 +551,13 @@ public BoxScriptParser setSubParser( boolean subParser ) {
* @param left the left side of the dot access left.right
* @param right the right side of the dot access left.right
*/
public void checkDotAccess( BoxExpression left, BoxExpression right ) {
public void checkDotAccess( BoxExpression left, BoxExpression right, boolean isStatic ) {

// Check the right hand side to see if it is a valid access method
checkRight( right );
checkRight( right, isStatic );

// Check to see if the left hand side is something that is valid to be accessed via a dot access
checkLeft( left );
checkLeft( left, isStatic );

// Now we know the LHS is valid for access by a dot method and the RHS is a valid access method, so
// we can check the combinations here if needed.
Expand All @@ -566,7 +569,7 @@ public void checkDotAccess( BoxExpression left, BoxExpression right ) {
*
* @param right the right side of the dot access left.right
*/
private void checkRight( BoxExpression right ) {
private void checkRight( BoxExpression right, boolean isStatic ) {
// Check the right hand side is a valid access method and fall through if it is not
switch ( right ) {
case BoxFunctionInvocation ignored -> {
Expand All @@ -587,7 +590,8 @@ private void checkRight( BoxExpression right ) {
}
case BoxExpressionInvocation ignored -> {
}
default -> errorListener.semanticError( "dot access via " + right.getDescription() + " is not a valid access method", right.getPosition() );
default -> errorListener.semanticError( ( isStatic ? "static" : "dot" ) + " access via " + right.getDescription() + " is not a valid access method",
right.getPosition() );
}
}

Expand All @@ -596,7 +600,7 @@ private void checkRight( BoxExpression right ) {
*
* @param left the left side of the dot access left.right
*/
private void checkLeft( BoxExpression left ) {
private void checkLeft( BoxExpression left, boolean isStatic ) {
// Check the left hand side is a valid construct for dot access and fall through if it is not
switch ( left ) {
case BoxFunctionInvocation ignored -> {
Expand Down Expand Up @@ -624,9 +628,15 @@ private void checkLeft( BoxExpression left ) {
case BoxDecimalLiteral ignored -> {
}
case BoxParenthesis ignored -> {
// TODO: Brad - Should we allow this always, or check what is inside the parenthesis?
}
default -> errorListener.semanticError( left.getDescription() + " is not a valid construct for dot access", left.getPosition() );
case BoxStaticMethodInvocation ignored -> {
}
case BoxStaticAccess ignored -> {
}
case BoxFQN ignored -> {
}
default -> errorListener.semanticError( left.getDescription() + " is not a valid construct for " + ( isStatic ? "static" : "dot" ) + " access",
left.getPosition() );
}
}

Expand Down
Loading

0 comments on commit 3cc36ce

Please sign in to comment.