diff --git a/compiler/src/main/java/ourtus/boxlang/ast/Node.java b/compiler/src/main/java/ourtus/boxlang/ast/Node.java index 16c475253..d9a18c1e2 100644 --- a/compiler/src/main/java/ourtus/boxlang/ast/Node.java +++ b/compiler/src/main/java/ourtus/boxlang/ast/Node.java @@ -30,7 +30,7 @@ public class Node { /** * AST node constructor - * + * * @param position the position within the source code that originated the node * @param sourceText the original source code that represented by the node */ @@ -42,9 +42,9 @@ public Node( Position position, String sourceText ) { /** * Returns the position in code that the node represents - * + * * @return a Position instance - * + * * @see Position */ public Position getPosition() { @@ -53,7 +53,7 @@ public Position getPosition() { /** * Returns the source code that originated the Node - * + * * @return the snipped of the source code */ public String getSourceText() { @@ -62,7 +62,7 @@ public String getSourceText() { /** * Set the parent and the children of the Node - * + * * @param parent an instance of the parent code */ public void setParent( Node parent ) { @@ -75,7 +75,7 @@ public void setParent( Node parent ) { /** * Returns the parent Node of node or null if has no parent - * + * * @return the parent Node of the current Node */ public Node getParent() { @@ -84,7 +84,7 @@ public Node getParent() { /** * Returns the list ov children of the current node - * + * * @return a list of children Node */ public List getChildren() { @@ -97,7 +97,7 @@ public Node getOriginator() { /** * Walk the tree - * + * * @return a list of nodes traversed */ public List walk() { @@ -108,4 +108,5 @@ public List walk() { } return result; } + } diff --git a/compiler/src/main/java/ourtus/boxlang/ast/Position.java b/compiler/src/main/java/ourtus/boxlang/ast/Position.java index 0bfd4b61b..c187ad95a 100644 --- a/compiler/src/main/java/ourtus/boxlang/ast/Position.java +++ b/compiler/src/main/java/ourtus/boxlang/ast/Position.java @@ -28,7 +28,7 @@ public class Position { /** * Creates a position - * + * * @param start the start position in the source code * @param end the end position in the source code */ @@ -40,7 +40,7 @@ public Position( Point start, Point end ) { /** * Creates a position including the file information - * + * * @param start the start position in the source code * @param end the end position in the source code * @param source the source file reference @@ -53,7 +53,7 @@ public Position( Point start, Point end, Source source ) { /** * Returns the start point - * + * * @return the start point of the region */ public Point getStart() { @@ -62,7 +62,7 @@ public Point getStart() { /** * Returns the end point - * + * * @return the end point of the region */ public Point getEnd() { @@ -71,9 +71,9 @@ public Point getEnd() { /** * Returns the source of the position - * + * * @return the start point of the region - * + * * @see Source */ public Source getSource() { @@ -82,12 +82,34 @@ public Source getSource() { /** * Set the source of the position - * + * * @param source The source of the position (i.e. file) - * + * * @see Source */ public void setSource( Source source ) { this.source = source; } + + /** + * String representation of the Position + * @return a String representation of the position including the source file if available + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if ( this.getSource() != null ) { + sb.append( this.getSource() ); + sb.append( ": " ); + } + sb.append( this.getStart().getLine() ) + .append( "," ) + .append( this.getStart().getColumn() ); + sb.append( " - " ); + sb.append( this.getEnd().getLine() ) + .append( "," ) + .append( this.getEnd().getColumn() ); + + return sb.toString(); + } } diff --git a/compiler/src/main/java/ourtus/boxlang/ast/SourceFile.java b/compiler/src/main/java/ourtus/boxlang/ast/SourceFile.java index 3b3fdf29d..f508345e9 100644 --- a/compiler/src/main/java/ourtus/boxlang/ast/SourceFile.java +++ b/compiler/src/main/java/ourtus/boxlang/ast/SourceFile.java @@ -37,10 +37,19 @@ public SourceFile( File file ) { /** * Returns the File associate to the source - * + * * @return a File instance */ public File getFile() { return file; } + + /** + * String representation of a file source + * @return the absolute path of the File + */ + @Override + public String toString() { + return file.getAbsolutePath(); + } } diff --git a/compiler/src/main/java/ourtus/boxlang/executor/JavaClassByteCode.java b/compiler/src/main/java/ourtus/boxlang/executor/JavaClassByteCode.java new file mode 100644 index 000000000..96d50b346 --- /dev/null +++ b/compiler/src/main/java/ourtus/boxlang/executor/JavaClassByteCode.java @@ -0,0 +1,43 @@ +/** + * [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 ourtus.boxlang.executor; + +import javax.tools.SimpleJavaFileObject; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.net.URI; + +/** + * JavaFileObject implementation + */ +public class JavaClassByteCode extends SimpleJavaFileObject { + + protected ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + public JavaClassByteCode( String name, Kind kind ) { + super( URI.create( "string:///" + name.replace( '.', '/' ) + + kind.extension ), kind ); + } + + public byte[] getBytes() { + return bos.toByteArray(); + } + + @Override + public OutputStream openOutputStream() { + return bos; + } +} diff --git a/compiler/src/main/java/ourtus/boxlang/executor/JavaDynamicClassLoader.java b/compiler/src/main/java/ourtus/boxlang/executor/JavaDynamicClassLoader.java new file mode 100644 index 000000000..63fb42b63 --- /dev/null +++ b/compiler/src/main/java/ourtus/boxlang/executor/JavaDynamicClassLoader.java @@ -0,0 +1,58 @@ +/** + * [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 ourtus.boxlang.executor; + +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Paths; +import java.util.Map; + +/** + * Dynamic in Memory classloader + */ +public class JavaDynamicClassLoader extends URLClassLoader { + + private JavaMemoryManager manager; + + public JavaDynamicClassLoader( URL[] urls, ClassLoader parent, JavaMemoryManager manager ) { + super( urls, parent ); + this.manager = manager; + + } + + @Override + protected Class findClass( String name ) throws ClassNotFoundException { + + Map compiledClasses = manager + .getBytesMap(); + + if ( compiledClasses.containsKey( name ) ) { + byte[] bytes = compiledClasses.get( name ) + .getBytes(); + return defineClass( name, bytes, 0, bytes.length ); + } else { + return super.findClass( name ); + } + } + + /* + * Required for Java Agents when this classloader is used as the system classloader + */ + @SuppressWarnings( "unused" ) + private void appendToClassPathForInstrumentation( String jarfile ) throws IOException { + addURL( Paths.get( jarfile ).toRealPath().toUri().toURL() ); + } +} diff --git a/compiler/src/main/java/ourtus/boxlang/executor/JavaMemoryManager.java b/compiler/src/main/java/ourtus/boxlang/executor/JavaMemoryManager.java new file mode 100644 index 000000000..c0b1c8b36 --- /dev/null +++ b/compiler/src/main/java/ourtus/boxlang/executor/JavaMemoryManager.java @@ -0,0 +1,52 @@ +/** + * [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 ourtus.boxlang.executor; + +import javax.tools.*; +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Hashtable; +import java.util.Map; + + +/** + * In Memory File Manager + */ +public class JavaMemoryManager extends ForwardingJavaFileManager { + + private final Map compiledClasses; + + public JavaMemoryManager( StandardJavaFileManager standardFileManager ) { + super( standardFileManager ); + this.compiledClasses = new Hashtable<>(); + } + + @Override + public JavaFileObject getJavaFileForOutput( Location location, String className, JavaFileObject.Kind kind, + FileObject sibling ) { + + JavaClassByteCode classAsBytes = new JavaClassByteCode( + className, kind ); + compiledClasses.put( className, classAsBytes ); + + return classAsBytes; + } + + public Map getBytesMap() { + return compiledClasses; + } +} diff --git a/compiler/src/main/java/ourtus/boxlang/executor/JavaRunner.java b/compiler/src/main/java/ourtus/boxlang/executor/JavaRunner.java new file mode 100644 index 000000000..2ebdb606f --- /dev/null +++ b/compiler/src/main/java/ourtus/boxlang/executor/JavaRunner.java @@ -0,0 +1,174 @@ +/** + * [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 ourtus.boxlang.executor; + +import org.apache.commons.text.StringSubstitutor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.tools.DiagnosticCollector; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.ToolProvider; +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.*; +import java.util.*; + +/** + * Dynamic in memory Java executor + */ +public class JavaRunner { + + final static String fqn = "ortus.boxlang.test.TestClass"; + String template = """ + package ortus.boxlang.test; + + import ortus.boxlang.runtime.BoxRuntime; + import ortus.boxlang.runtime.context.IBoxContext; + + // BoxLang Auto Imports + import ortus.boxlang.runtime.dynamic.BaseTemplate; + import ortus.boxlang.runtime.dynamic.Referencer; + import ortus.boxlang.runtime.interop.DynamicObject; + import ortus.boxlang.runtime.loader.ClassLocator; + import ortus.boxlang.runtime.operators.*; + import ortus.boxlang.runtime.scopes.Key; + import ortus.boxlang.runtime.scopes.IScope; + import ortus.boxlang.runtime.dynamic.casters.*; + + import ourtus.boxlang.executor.BoxJavaClass; + + public class TestClass extends BaseTemplate { + + private static TestClass instance; + + public TestClass() { + } + + public static synchronized TestClass getInstance() { + if ( instance == null ) { + instance = new TestClass(); + } + return instance; + } + /** + * Each template must implement the invoke() method which executes the template + * + * @param context The execution context requesting the execution + */ + public void invoke( IBoxContext context ) throws Throwable { + // Reference to the variables scope + IScope variablesScope = context.getScopeNearby( Key.of( "variables" ) ); + ClassLocator JavaLoader = ClassLocator.getInstance(); + ${javaCode}; + String result = variablesScope.toString(); + System.out.println(result); + } + + public static void main(String[] args) { + BoxRuntime rt = BoxRuntime.getInstance(); + + try { + rt.executeTemplate( TestClass.getInstance() ); + } catch ( Throwable e ) { + e.printStackTrace(); + System.exit( 1 ); + } + + // Bye bye! Ciao Bella! + rt.shutdown(); + + + } + } + """; + + Logger logger = LoggerFactory.getLogger( JavaRunner.class ); + + public void runExpression( String javaCode ) { + + try { + Map values = new HashMap<>() { + + { + put( "javaCode", javaCode ); + } + }; + + StringSubstitutor sub = new StringSubstitutor( values ); + String javaClass = sub.replace( template ); + + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + DiagnosticCollector diagnostics = new DiagnosticCollector<>(); + JavaMemoryManager manager = new JavaMemoryManager( compiler.getStandardFileManager( null, null, null ) ); + + String javaRT = System.getProperty( "java.class.path" ); + String boxRT = "/home/madytyoo/IdeaProjects/boxlang/runtime/build/classes/java/main"; + String compRT = "/home/madytyoo/IdeaProjects/boxlang1/compiler/build/classes/java/main"; + + List sourceFiles = Collections.singletonList( new JavaSourceString( fqn, javaClass ) ); + List options = new ArrayList<>() { + + { + add( "-g" ); + add( "-cp" ); + add( javaRT + File.pathSeparator + boxRT + File.pathSeparator + File.pathSeparator + + compRT ); + + } + }; + JavaCompiler.CompilationTask task = compiler.getTask( null, manager, diagnostics, options, null, sourceFiles ); + boolean result = task.call(); + + if ( !result ) { + diagnostics.getDiagnostics() + .forEach( d -> logger.error( String.valueOf( d ) ) ); + throw new RuntimeException( "Compiler Error" ); + } else { + // ClassLoader classLoader = manager.getClassLoader( null ); + // Class clazz = ( ( JavaDynamicClassLoader ) classLoader ).define( fqn ); + // BoxJavaClass instanceOfClass = ( BoxJavaClass ) clazz.newInstance(); + + JavaDynamicClassLoader classLoader = new JavaDynamicClassLoader( + new URL[] { + new File( boxRT ).toURI().toURL() + }, + this.getClass().getClassLoader(), + manager ); + + // JavaDynamicClassLoader classLoader = (JavaDynamicClassLoader) manager.getClassLoader( null ); + // classLoader.defineClass(fqn); + Class cls = Class.forName( fqn, true, classLoader ); + Method meth = cls.getMethod( "main", String[].class ); + String[] params = null; // init params accordingly + meth.invoke( null, ( Object ) params ); + + } + } catch ( ClassNotFoundException e ) { + throw new RuntimeException( e ); + } catch ( IllegalAccessException e ) { + throw new RuntimeException( e ); + } catch ( InvocationTargetException e ) { + throw new RuntimeException( e ); + } catch ( NoSuchMethodException e ) { + throw new RuntimeException( e ); + } catch ( MalformedURLException e ) { + throw new RuntimeException( e ); + } + } +} diff --git a/compiler/src/main/java/ourtus/boxlang/executor/JavaSourceString.java b/compiler/src/main/java/ourtus/boxlang/executor/JavaSourceString.java new file mode 100644 index 000000000..705f553ae --- /dev/null +++ b/compiler/src/main/java/ourtus/boxlang/executor/JavaSourceString.java @@ -0,0 +1,40 @@ +/** + * [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 ourtus.boxlang.executor; + +import javax.tools.SimpleJavaFileObject; + +import java.net.URI; + +import static java.util.Objects.requireNonNull; + +/** + * Java Source code as a String + */ +public class JavaSourceString extends SimpleJavaFileObject { + + private String sourceCode; + + public JavaSourceString( String name, String sourceCode ) { + super( URI.create( "string:///" + name.replace( '.', '/' ) + Kind.SOURCE.extension ), + Kind.SOURCE ); + this.sourceCode = requireNonNull( sourceCode, "sourceCode must not be null" ); + } + + @Override + public CharSequence getCharContent( boolean ignoreEncodingErrors ) { + return sourceCode; + } +} diff --git a/compiler/src/main/java/ourtus/boxlang/parser/BoxCFParser.java b/compiler/src/main/java/ourtus/boxlang/parser/BoxCFParser.java index d99b308ec..321a5f8f2 100644 --- a/compiler/src/main/java/ourtus/boxlang/parser/BoxCFParser.java +++ b/compiler/src/main/java/ourtus/boxlang/parser/BoxCFParser.java @@ -59,8 +59,11 @@ public BoxCFParser() { public ParsingResult parse( File file ) throws IOException { BOMInputStream inputStream = getInputStream( file ); CFParser.ScriptContext parseTree = ( CFParser.ScriptContext ) parserFirstStage( inputStream ); - BoxScript ast = parseTreeToAst( file, parseTree ); - return new ParsingResult( ast, issues ); + if ( issues.isEmpty() ) { + BoxScript ast = parseTreeToAst( file, parseTree ); + return new ParsingResult( ast, issues ); + } + return new ParsingResult( null, issues ); } /** @@ -79,8 +82,11 @@ public ParsingResult parse( String code ) throws IOException { InputStream inputStream = IOUtils.toInputStream( code ); CFParser.ScriptContext parseTree = ( CFParser.ScriptContext ) parserFirstStage( inputStream ); - BoxScript ast = parseTreeToAst( file, parseTree ); - return new ParsingResult( ast, issues ); + if ( issues.isEmpty() ) { + BoxScript ast = parseTreeToAst( file, parseTree ); + return new ParsingResult( ast, issues ); + } + return new ParsingResult( null, issues ); } /** diff --git a/compiler/src/main/java/ourtus/boxlang/transpiler/BoxLangTranspiler.java b/compiler/src/main/java/ourtus/boxlang/transpiler/BoxLangTranspiler.java index 7cdd8b75c..c7ad52910 100644 --- a/compiler/src/main/java/ourtus/boxlang/transpiler/BoxLangTranspiler.java +++ b/compiler/src/main/java/ourtus/boxlang/transpiler/BoxLangTranspiler.java @@ -14,11 +14,17 @@ */ package ourtus.boxlang.transpiler; +import com.github.javaparser.JavaParser; +import com.github.javaparser.ParseResult; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.stmt.BlockStmt; import com.github.javaparser.ast.stmt.Statement; +import com.github.javaparser.ast.visitor.VoidVisitor; +import com.github.javaparser.printer.DefaultPrettyPrinterVisitor; +import com.github.javaparser.printer.Printer; +import com.github.javaparser.printer.configuration.DefaultPrinterConfiguration; import ourtus.boxlang.ast.BoxNode; import ourtus.boxlang.ast.BoxScript; import ourtus.boxlang.ast.BoxStatement; @@ -28,8 +34,11 @@ import ourtus.boxlang.transpiler.transformer.expression.*; import java.util.HashMap; +import java.util.Optional; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ourtus.boxlang.transpiler.transformer.indexer.IndexPrettyPrinterVisitor; import ourtus.boxlang.transpiler.transformer.statement.*; /** @@ -59,6 +68,7 @@ public class BoxLangTranspiler { put( BoxStringLiteral.class, new BoxStringLiteralTransformer() ); put( BoxIntegerLiteral.class, new BoxIntegerLiteralTransformer() ); put( BoxBooleanLiteral.class, new BoxBooleanLiteralTransformer() ); + put( BoxDecimalLiteral.class, new BoxDecimalLiteralTransformer() ); put( BoxArgument.class, new BoxArgumentTransformer() ); put( BoxParenthesis.class, new BoxParenthesisTransformer() ); @@ -148,6 +158,10 @@ public CompilationUnit transpile( BoxNode node ) throws IllegalStateException { invokeMethod.getBody().get().addStatement( ( Statement ) javaStmt ); } } + + VoidVisitor visitor = new IndexPrettyPrinterVisitor( new DefaultPrinterConfiguration() ); + javaClass.accept( visitor, null ); + return javaClass; } } diff --git a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxArgumentTransformer.java b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxArgumentTransformer.java index ac20554de..57f967239 100644 --- a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxArgumentTransformer.java +++ b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxArgumentTransformer.java @@ -29,7 +29,7 @@ public class BoxArgumentTransformer extends AbstractTransformer { * @return Generates a Java Parser Expression * * @throws IllegalStateException - * + * * @see BoxArgument */ @Override @@ -49,7 +49,7 @@ public Node transform( BoxNode node, TransformerContext context ) throws Illegal Node javaExpr = parseExpression( template, values ); logger.info( side + node.getSourceText() + " -> " + javaExpr ); - addIndex( javaExpr, arg ); + return javaExpr; } } diff --git a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxArrayAccessTransformer.java b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxArrayAccessTransformer.java index d96cb6782..053d2c12f 100644 --- a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxArrayAccessTransformer.java +++ b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxArrayAccessTransformer.java @@ -44,9 +44,9 @@ public class BoxArrayAccessTransformer extends AbstractTransformer { * @param context transformation context * * @return generates a Java Parser Expression accessing the scope - * + * * @throws IllegalStateException - * + * * @see BoxArrayAccess */ @Override @@ -90,7 +90,7 @@ public Node transform( BoxNode node, TransformerContext context ) throws Illegal Node javaNode = parseExpression( template, values ); // logger.info(side + node.getSourceText() + " -> " + javaNode); - addIndex( javaNode, expr ); + addIndex( javaNode, node ); return javaNode; } throw new IllegalStateException( "Not implemented" ); diff --git a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxBinaryOperationTransformer.java b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxBinaryOperationTransformer.java index 197db17f5..f530a1735 100644 --- a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxBinaryOperationTransformer.java +++ b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxBinaryOperationTransformer.java @@ -44,7 +44,7 @@ public class BoxBinaryOperationTransformer extends AbstractTransformer { * @return generates a Java Parser Method invocation to the corresponding runtime implementation * * @throws IllegalStateException - * + * * @see BoxBinaryOperation * @see BoxBinaryOperator foe the supported operators */ @@ -99,7 +99,7 @@ public Node transform( BoxNode node, TransformerContext context ) throws Illegal } Node javaExpr = parseExpression( template, values ); logger.info( node.getSourceText() + " -> " + javaExpr ); - addIndex( javaExpr, node ); + // addIndex( javaExpr, node ); return javaExpr; } diff --git a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxDecimalLiteralTransformer.java b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxDecimalLiteralTransformer.java new file mode 100644 index 000000000..1183b0a60 --- /dev/null +++ b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxDecimalLiteralTransformer.java @@ -0,0 +1,50 @@ +/** + * [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 ourtus.boxlang.transpiler.transformer.expression; + +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.expr.DoubleLiteralExpr; +import com.github.javaparser.ast.expr.IntegerLiteralExpr; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ourtus.boxlang.ast.BoxNode; +import ourtus.boxlang.ast.expression.BoxDecimalLiteral; +import ourtus.boxlang.ast.expression.BoxIntegerLiteral; +import ourtus.boxlang.transpiler.transformer.AbstractTransformer; +import ourtus.boxlang.transpiler.transformer.TransformerContext; + +/** + * Transform a BoxBooleanLiteral Node the equivalent Java Parser AST nodes + */ +public class BoxDecimalLiteralTransformer extends AbstractTransformer { + Logger logger = LoggerFactory.getLogger( BoxDecimalLiteralTransformer.class ); + + /** + * Transform BoxDecimalLiteral argument + * + * @param node a BoxDecimalLiteral instance + * @param context transformation context + * + * @return generates a Java Parser boolean Literal + */ + @Override + public Node transform( BoxNode node, TransformerContext context ) throws IllegalStateException { + BoxDecimalLiteral literal = ( BoxDecimalLiteral ) node; + DoubleLiteralExpr javaExpr = new DoubleLiteralExpr( literal.getValue() ); + logger.info( node.getSourceText() + " -> " + javaExpr ); + addIndex( javaExpr, node ); + return javaExpr; + } +} diff --git a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxFunctionInvocationTransformer.java b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxFunctionInvocationTransformer.java index 874f098ce..e5e86988b 100644 --- a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxFunctionInvocationTransformer.java +++ b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxFunctionInvocationTransformer.java @@ -42,7 +42,12 @@ public Node transform( BoxNode node, TransformerContext context ) throws Illegal String side = context == TransformerContext.NONE ? "" : "(" + context.toString() + ") "; logger.info( side + node.getSourceText() ); - Map values = new HashMap<>(); + Map values = new HashMap<>() { + + { + put( "name", function.getName().getName() ); + } + }; for ( int i = 0; i < function.getArguments().size(); i++ ) { Expression expr = ( Expression ) BoxLangTranspiler.transform( function.getArguments().get( i ) ); values.put( "arg" + i, expr.toString() ); @@ -50,6 +55,7 @@ public Node transform( BoxNode node, TransformerContext context ) throws Illegal String template = getTemplate( function ); Node javaExpr = parseExpression( template, values ); logger.info( side + node.getSourceText() + " -> " + javaExpr ); + addIndex( javaExpr, node ); return javaExpr; } @@ -59,14 +65,16 @@ private String getTemplate( BoxFunctionInvocation function ) { ; if ( target != null ) return target; - StringBuilder sb = new StringBuilder( function.getName().getName() ); - sb.append( "(" ); + StringBuilder sb = new StringBuilder( "context.invokeFunction( Key.of( \"${name}\" )" ); + + sb.append( ", new Object[] { " ); for ( int i = 0; i < function.getArguments().size(); i++ ) { sb.append( "${" ).append( "arg" ).append( i ).append( "}" ); if ( i < function.getArguments().size() - 1 ) { sb.append( "," ); } } + sb.append( "}" ); sb.append( ")" ); return sb.toString(); } diff --git a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxMethodInvocationTransformer.java b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxMethodInvocationTransformer.java index d5f4766d7..4ad86315a 100644 --- a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxMethodInvocationTransformer.java +++ b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxMethodInvocationTransformer.java @@ -57,6 +57,7 @@ public Node transform( BoxNode node, TransformerContext context ) throws Illegal } Node javaExpr = parseExpression( template, values ); logger.info( side + node.getSourceText() + " -> " + javaExpr ); + addIndex( javaExpr, node ); return javaExpr; } } diff --git a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxObjectAccessTransformer.java b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxObjectAccessTransformer.java index 96fff62e3..c9053f84c 100644 --- a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxObjectAccessTransformer.java +++ b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/expression/BoxObjectAccessTransformer.java @@ -26,7 +26,6 @@ public class BoxObjectAccessTransformer extends AbstractTransformer { public Node transform( BoxNode node, TransformerContext context ) throws IllegalStateException { BoxObjectAccess objectAccess = ( BoxObjectAccess ) node; String side = context == TransformerContext.NONE ? "" : "(" + context.toString() + ") "; - logger.info( side + node.getSourceText() ); if ( objectAccess.getContext() instanceof BoxScope && objectAccess.getAccess() instanceof BoxObjectAccess ) { Expression scope = ( Expression ) BoxLangTranspiler.transform( objectAccess.getContext(), TransformerContext.LEFT ); @@ -52,7 +51,10 @@ public Node transform( BoxNode node, TransformerContext context ) throws Illegal Referencer.get(${scope}.get( Key.of( ${var0} ) ).get(Key.of( ${var1} )),false) """; }; - return parseExpression( template, values ); + Node javaExpr = parseExpression( template, values ); + logger.info( side + node.getSourceText() + " -> " + javaExpr ); + addIndex( javaExpr, node ); + return javaExpr; } else { Map values = new HashMap<>() { @@ -72,7 +74,10 @@ public Node transform( BoxNode node, TransformerContext context ) throws Illegal """; }; - return parseExpression( template, values ); + Node javaExpr = parseExpression( template, values ); + logger.info( side + node.getSourceText() + " -> " + javaExpr ); + addIndex( javaExpr, node ); + return javaExpr; } } else if ( objectAccess.getContext() instanceof BoxIdentifier && objectAccess.getAccess() instanceof BoxIdentifier ) { @@ -111,7 +116,10 @@ public Node transform( BoxNode node, TransformerContext context ) throws Illegal ${scope}.get( Key.of( "${variable}" ) ) """; }; - return parseExpression( template, values ); + Node javaExpr = parseExpression( template, values ); + logger.info( side + node.getSourceText() + " -> " + javaExpr ); + addIndex( javaExpr, node ); + return javaExpr; } throw new IllegalStateException( "" ); diff --git a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/indexer/BoxLangCrossReferencer.java b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/indexer/BoxLangCrossReferencer.java index 5b6fffdff..be69e350b 100644 --- a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/indexer/BoxLangCrossReferencer.java +++ b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/indexer/BoxLangCrossReferencer.java @@ -15,10 +15,10 @@ public abstract class BoxLangCrossReferencer { /** * Store a reference into the index - * + * * @param javaNode Java Parser AST Node * @param boxNode BoxLang AST Node - * + * * @return */ public abstract Node storeReference( Node javaNode, ourtus.boxlang.ast.BoxNode boxNode ); diff --git a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/indexer/BoxLangCrossReferencerDefault.java b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/indexer/BoxLangCrossReferencerDefault.java index 490669f13..38881cf03 100644 --- a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/indexer/BoxLangCrossReferencerDefault.java +++ b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/indexer/BoxLangCrossReferencerDefault.java @@ -2,19 +2,34 @@ import com.github.javaparser.Position; import com.github.javaparser.Range; +import com.github.javaparser.ast.DataKey; import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.comments.BlockComment; +import com.github.javaparser.ast.comments.LineComment; +import com.github.javaparser.ast.stmt.Statement; +import ourtus.boxlang.ast.BoxNode; +import static ourtus.boxlang.transpiler.transformer.indexer.BoxNodeKey.BOX_NODE_DATA_KEY; + +/** + * Cross Reference default implementation + */ public class BoxLangCrossReferencerDefault extends BoxLangCrossReferencer { + /** + * Creates the cross-reference between the Java AST node and BoxNone + * + * @param javaNode Java Parser AST Node + * @param boxNode BoxLang AST Node + * + * @return the Java AST with a BoxNode in the data collection + */ public Node storeReference( Node javaNode, ourtus.boxlang.ast.BoxNode boxNode ) { if ( this.enabled ) { - Range range = new Range( - new Position( boxNode.getPosition().getStart().getLine(), boxNode.getPosition().getStart().getColumn() ), - new Position( boxNode.getPosition().getEnd().getLine(), boxNode.getPosition().getEnd().getColumn() ) - ); - javaNode.setRange( range ); + if ( javaNode instanceof Statement ) { + javaNode.setData( BOX_NODE_DATA_KEY, boxNode ); + } } return javaNode; } - } diff --git a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/indexer/BoxNodeKey.java b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/indexer/BoxNodeKey.java new file mode 100644 index 000000000..e36208ab9 --- /dev/null +++ b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/indexer/BoxNodeKey.java @@ -0,0 +1,27 @@ +/** + * [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 ourtus.boxlang.transpiler.transformer.indexer; + +import com.github.javaparser.ast.DataKey; +import ourtus.boxlang.ast.BoxNode; + +/** + * Class representing a key in the Java Parser Node data collection + */ +public class BoxNodeKey extends DataKey { + + public static final DataKey BOX_NODE_DATA_KEY = new DataKey() { + }; +} diff --git a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/indexer/IndexPrettyPrinterVisitor.java b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/indexer/IndexPrettyPrinterVisitor.java new file mode 100644 index 000000000..f09c11266 --- /dev/null +++ b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/indexer/IndexPrettyPrinterVisitor.java @@ -0,0 +1,107 @@ +/** + * [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 ourtus.boxlang.transpiler.transformer.indexer; + +import com.github.javaparser.Position; +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.expr.MethodCallExpr; +import com.github.javaparser.ast.expr.StringLiteralExpr; +import com.github.javaparser.ast.stmt.DoStmt; +import com.github.javaparser.ast.stmt.ExpressionStmt; +import com.github.javaparser.ast.stmt.IfStmt; +import com.github.javaparser.ast.stmt.Statement; +import com.github.javaparser.printer.DefaultPrettyPrinterVisitor; +import com.github.javaparser.printer.configuration.PrinterConfiguration; +import ourtus.boxlang.ast.BoxNode; + +import static ourtus.boxlang.transpiler.transformer.indexer.BoxNodeKey.BOX_NODE_DATA_KEY; + +/** + * Visitor to walk the tree and resolve the cross-reference + */ +public class IndexPrettyPrinterVisitor extends DefaultPrettyPrinterVisitor { + + public IndexPrettyPrinterVisitor( PrinterConfiguration configuration ) { + super( configuration ); + } + + /** + * Detects if a Node has an BOX_NODE_DATA_KEY with a BoxNode + * @param node a Java Parser Node + * @return the associated BoxNode (if any) or null + */ + private Object hasBoxNodeKey( Node node ) { + + if ( !node.getDataKeys().isEmpty() ) { + for ( Object key : node.getDataKeys() ) { + if ( key.equals( BOX_NODE_DATA_KEY ) ) { + return node.getData( BOX_NODE_DATA_KEY ); + } + + } + } + return null; + } + + /** + * Visits an ExpressionStmt Node + * @param node a Java Parser ExpressionStmt + * @param arg void + */ + @Override + public void visit( ExpressionStmt node, Void arg ) { + BoxNode boxNode = ( BoxNode ) hasBoxNodeKey( node ); + if ( boxNode != null ) { + Position start = printer.getCursor(); + super.visit( node, arg ); + Position end = printer.getCursor(); + } else { + super.visit( node, arg ); + } + } + /** + * Visits an IfStmt Node + * @param node a Java Parser IfStmt + * @param arg void + */ + @Override + public void visit( IfStmt node, Void arg ) { + BoxNode boxNode = ( BoxNode ) hasBoxNodeKey( node ); + if ( boxNode != null ) { + Position start = printer.getCursor(); + super.visit( node, arg ); + Position end = printer.getCursor(); + } else { + super.visit( node, arg ); + } + } + /** + * Visits an DoStmt Node + * @param node a Java Parser DoStmt + * @param arg void + */ + @Override + public void visit( DoStmt node, Void arg ) { + BoxNode boxNode = ( BoxNode ) hasBoxNodeKey( node ); + if ( boxNode != null ) { + Position start = printer.getCursor(); + super.visit( node, arg ); + Position end = printer.getCursor(); + } else { + super.visit( node, arg ); + } + } + +} diff --git a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/statement/BoxExpressionTransformer.java b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/statement/BoxExpressionTransformer.java index 1e2966d95..4987e6c83 100644 --- a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/statement/BoxExpressionTransformer.java +++ b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/statement/BoxExpressionTransformer.java @@ -29,6 +29,6 @@ public class BoxExpressionTransformer extends AbstractTransformer { public Node transform( BoxNode node, TransformerContext context ) throws IllegalStateException { BoxExpression exprStmt = ( BoxExpression ) node; Expression expr = ( Expression ) BoxLangTranspiler.transform( exprStmt.getExpression() ); - return new ExpressionStmt( expr ); + return addIndex( new ExpressionStmt( expr ), node ); } } diff --git a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/statement/BoxIfElseTransformer.java b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/statement/BoxIfElseTransformer.java index 74d2828e1..b305faebb 100644 --- a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/statement/BoxIfElseTransformer.java +++ b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/statement/BoxIfElseTransformer.java @@ -69,6 +69,7 @@ public Node transform( BoxNode node, TransformerContext context ) throws Illegal else javaIfStmt.setElseStmt( elseBlock.getStatement( 0 ) ); } + addIndex( javaIfStmt, node ); return javaIfStmt; } diff --git a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/statement/BoxSwitchTransformer.java b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/statement/BoxSwitchTransformer.java index dcd2a92ae..5620fe411 100644 --- a/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/statement/BoxSwitchTransformer.java +++ b/compiler/src/main/java/ourtus/boxlang/transpiler/transformer/statement/BoxSwitchTransformer.java @@ -1,3 +1,17 @@ +/** + * [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 ourtus.boxlang.transpiler.transformer.statement; import com.github.javaparser.ast.Node; @@ -18,10 +32,23 @@ import java.util.HashMap; import java.util.Map; +/** + * Transform a SwitchStatement Node the equivalent Java Parser AST nodes + */ public class BoxSwitchTransformer extends AbstractTransformer { Logger logger = LoggerFactory.getLogger( BoxParenthesisTransformer.class ); + /** + * Transform a collection for statement + * + * @param node a BoxForIn instance + * @param context transformation context + * + * @return a Java Parser Block statement with an iterator and a while loop + * + * @throws IllegalStateException + */ @Override public Node transform( BoxNode node, TransformerContext context ) throws IllegalStateException { BoxSwitch boxSwitch = ( BoxSwitch ) node; @@ -54,6 +81,7 @@ public Node transform( BoxNode node, TransformerContext context ) throws Illegal } ); javaIfStmt.setThenStmt( thenBlock ); body.addStatement( javaIfStmt ); + addIndex( javaIfStmt, c ); } } ); boxSwitch.getCases().forEach( c -> { @@ -64,18 +92,8 @@ public Node transform( BoxNode node, TransformerContext context ) throws Illegal } } ); javaSwitch.setBody( body ); - // if(requiresBooleanCaster(boxSwitch.getCondition())) { - // template = "while( BooleanCaster.cast( ${condition} ) ) {}"; - // } - // Map values = new HashMap<>() {{ - // put("condition", condition.toString()); - // }}; - // WhileStmt javaWhile = (WhileStmt) parseStatement(template,values); - // BlockStmt body = new BlockStmt(); - // for(BoxStatement statement : boxSwitch.getBody()) { - // body.getStatements().add((Statement) BoxLangTranspiler.transform(statement)); - // } - // javaWhile.setBody(body); + logger.info( node.getSourceText() + " -> " + javaSwitch ); + addIndex( javaSwitch, node ); return javaSwitch; } }