Simplify use statement to take variable WORD only arguments

This commit is contained in:
aNNiMON 2023-09-09 15:51:36 +03:00 committed by Victor Melnik
parent 589856fbf3
commit 59f8c4109e
12 changed files with 45 additions and 142 deletions

View File

@ -6,7 +6,6 @@ import com.annimon.ownlang.parser.ast.Statement;
import com.annimon.ownlang.parser.ast.Visitor; import com.annimon.ownlang.parser.ast.Visitor;
import com.annimon.ownlang.parser.linters.AssignValidator; import com.annimon.ownlang.parser.linters.AssignValidator;
import com.annimon.ownlang.parser.linters.DefaultFunctionsOverrideValidator; import com.annimon.ownlang.parser.linters.DefaultFunctionsOverrideValidator;
import com.annimon.ownlang.parser.linters.UseWithNonStringValueValidator;
public final class Linter { public final class Linter {
@ -22,7 +21,6 @@ public final class Linter {
public void execute() { public void execute() {
final Visitor[] validators = new Visitor[] { final Visitor[] validators = new Visitor[] {
new UseWithNonStringValueValidator(),
new AssignValidator(), new AssignValidator(),
new DefaultFunctionsOverrideValidator() new DefaultFunctionsOverrideValidator()
}; };

View File

@ -5,12 +5,7 @@ import com.annimon.ownlang.lib.NumberValue;
import com.annimon.ownlang.lib.StringValue; import com.annimon.ownlang.lib.StringValue;
import com.annimon.ownlang.lib.UserDefinedFunction; import com.annimon.ownlang.lib.UserDefinedFunction;
import com.annimon.ownlang.parser.ast.*; import com.annimon.ownlang.parser.ast.*;
import java.util.ArrayList; import java.util.*;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** /**
* *
@ -145,7 +140,7 @@ public final class Parser {
return new ReturnStatement(expression()); return new ReturnStatement(expression());
} }
if (match(TokenType.USE)) { if (match(TokenType.USE)) {
return new UseStatement(expression()); return useStatement();
} }
if (match(TokenType.INCLUDE)) { if (match(TokenType.INCLUDE)) {
return new IncludeStatement(expression()); return new IncludeStatement(expression());
@ -168,13 +163,21 @@ public final class Parser {
return assignmentStatement(); return assignmentStatement();
} }
private UseStatement useStatement() {
final var modules = new HashSet<String>();
do {
modules.add(consume(TokenType.WORD).text());
} while (match(TokenType.COMMA));
return new UseStatement(modules);
}
private Statement assignmentStatement() { private Statement assignmentStatement() {
if (match(TokenType.EXTRACT)) { if (match(TokenType.EXTRACT)) {
return destructuringAssignment(); return destructuringAssignment();
} }
final Expression expression = expression(); final Expression expression = expression();
if (expression instanceof Statement) { if (expression instanceof Statement statement) {
return (Statement) expression; return statement;
} }
throw new ParseException("Unknown statement: " + get(0)); throw new ParseException("Unknown statement: " + get(0));
} }

View File

@ -1,11 +1,8 @@
package com.annimon.ownlang.parser.ast; package com.annimon.ownlang.parser.ast;
import com.annimon.ownlang.exceptions.TypeException;
import com.annimon.ownlang.lib.ArrayValue;
import com.annimon.ownlang.lib.Types;
import com.annimon.ownlang.lib.Value;
import com.annimon.ownlang.modules.Module; import com.annimon.ownlang.modules.Module;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collection;
/** /**
* *
@ -16,24 +13,17 @@ public final class UseStatement extends InterruptableNode implements Statement {
private static final String PACKAGE = "com.annimon.ownlang.modules.%s.%s"; private static final String PACKAGE = "com.annimon.ownlang.modules.%s.%s";
private static final String INIT_CONSTANTS_METHOD = "initConstants"; private static final String INIT_CONSTANTS_METHOD = "initConstants";
public final Expression expression; public final Collection<String> modules;
public UseStatement(Expression expression) { public UseStatement(Collection<String> modules) {
this.expression = expression; this.modules = modules;
} }
@Override @Override
public void execute() { public void execute() {
super.interruptionCheck(); super.interruptionCheck();
final Value value = expression.eval(); for (String module : modules) {
switch (value.type()) { loadModule(module);
case Types.ARRAY -> {
for (Value module : ((ArrayValue) value)) {
loadModule(module.asString());
}
}
case Types.STRING -> loadModule(value.asString());
default -> throw typeException(value);
} }
} }
@ -49,20 +39,10 @@ public final class UseStatement extends InterruptableNode implements Statement {
} }
public void loadConstants() { public void loadConstants() {
if (expression instanceof ArrayExpression ae) { for (String module : modules) {
for (Expression expr : ae.elements) { loadConstants(module);
loadConstants(expr.eval().asString());
} }
} }
if (expression instanceof ValueExpression ve) {
loadConstants(ve.value.asString());
}
}
private TypeException typeException(Value value) {
return new TypeException("Array or string required in 'use' statement, " +
"got " + Types.typeToString(value.type()) + " " + value);
}
private void loadConstants(String moduleName) { private void loadConstants(String moduleName) {
try { try {
@ -86,6 +66,6 @@ public final class UseStatement extends InterruptableNode implements Statement {
@Override @Override
public String toString() { public String toString() {
return "use " + expression; return "use " + String.join(", ", modules);
} }
} }

View File

@ -1,53 +0,0 @@
package com.annimon.ownlang.parser.linters;
import com.annimon.ownlang.Console;
import com.annimon.ownlang.lib.Types;
import com.annimon.ownlang.lib.Value;
import com.annimon.ownlang.parser.ast.*;
public final class UseWithNonStringValueValidator extends LintVisitor {
@Override
public void visit(IncludeStatement st) {
super.visit(st);
applyVisitor(st, this);
}
@Override
public void visit(UseStatement st) {
super.visit(st);
if (st.expression instanceof ArrayExpression ae) {
for (Expression expr : ae.elements) {
if (!checkExpression(expr)) {
return;
}
}
} else {
if (!checkExpression(st.expression)) {
return;
}
}
}
private boolean checkExpression(Expression expr) {
if (expr instanceof ValueExpression valueExpr) {
final Value value = valueExpr.value;
if (value.type() != Types.STRING) {
warnWrongType(value);
return false;
}
return true;
} else {
Console.error(String.format(
"Warning: `use` with %s, not ValueExpression", expr.getClass().getSimpleName()));
return false;
}
}
private void warnWrongType(Value value) {
Console.error(String.format(
"Warning: `use` with %s - %s, not string",
Types.typeToString(value.type()), value.asString()));
}
}

View File

@ -1,12 +1,7 @@
package com.annimon.ownlang.parser.optimization; package com.annimon.ownlang.parser.optimization;
import com.annimon.ownlang.exceptions.OperationIsNotSupportedException; import com.annimon.ownlang.exceptions.OperationIsNotSupportedException;
import com.annimon.ownlang.parser.ast.BinaryExpression; import com.annimon.ownlang.parser.ast.*;
import com.annimon.ownlang.parser.ast.ConditionalExpression;
import com.annimon.ownlang.parser.ast.FunctionDefineStatement;
import com.annimon.ownlang.parser.ast.Node;
import com.annimon.ownlang.parser.ast.UnaryExpression;
import com.annimon.ownlang.parser.ast.ValueExpression;
import com.annimon.ownlang.parser.visitors.VisitorUtils; import com.annimon.ownlang.parser.visitors.VisitorUtils;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -104,6 +99,11 @@ public class ConstantFolding extends OptimizationVisitor<Void> implements Optimi
return super.visit(s, t); return super.visit(s, t);
} }
@Override
public Node visit(UseStatement s, Void unused) {
return null;
}
@Override @Override
public Node visit(FunctionDefineStatement s, Void t) { public Node visit(FunctionDefineStatement s, Void t) {
if (OPERATORS.contains(s.name)) { if (OPERATORS.contains(s.name)) {

View File

@ -412,10 +412,6 @@ public abstract class OptimizationVisitor<T> implements ResultVisitor<Node, T> {
@Override @Override
public Node visit(UseStatement s, T t) { public Node visit(UseStatement s, T t) {
final Node expression = s.expression.accept(this, t);
if (expression != s.expression) {
return new UseStatement((Expression) expression);
}
return s; return s;
} }
@ -450,8 +446,8 @@ public abstract class OptimizationVisitor<T> implements ResultVisitor<Node, T> {
} }
protected Statement consumeStatement(Node node) { protected Statement consumeStatement(Node node) {
if (node instanceof Statement) { if (node instanceof Statement statement) {
return (Statement) node; return statement;
} }
return new ExprStatement((Expression) node); return new ExprStatement((Expression) node);
} }

View File

@ -1,5 +1,6 @@
package com.annimon.ownlang.parser.optimization; package com.annimon.ownlang.parser.optimization;
import com.annimon.ownlang.lib.ScopeHandler;
import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Value;
import com.annimon.ownlang.lib.Variables; import com.annimon.ownlang.lib.Variables;
import com.annimon.ownlang.parser.ast.*; import com.annimon.ownlang.parser.ast.*;
@ -99,36 +100,22 @@ public class VariablesGrabber extends OptimizationVisitor<Map<String, VariableIn
@Override @Override
public Node visit(UseStatement s, Map<String, VariableInfo> t) { public Node visit(UseStatement s, Map<String, VariableInfo> t) {
if (grabModuleConstants) { if (grabModuleConstants) {
// To get module variables we need to store current variables, clear all, then load module. // To get module constants we need to store current constants, clear all, then load module.
final Map<String, Value> currentVariables = new HashMap<>(Variables.variables()); final Map<String, Value> currentConstants = new HashMap<>(ScopeHandler.constants());
Variables.variables().clear(); ScopeHandler.constants().clear();
if (canLoadConstants(s.expression)) {
s.loadConstants(); s.loadConstants();
} // Grab module constants
// Grab module variables for (Map.Entry<String, Value> entry : ScopeHandler.constants().entrySet()) {
for (Map.Entry<String, Value> entry : Variables.variables().entrySet()) {
final VariableInfo var = variableInfo(t, entry.getKey()); final VariableInfo var = variableInfo(t, entry.getKey());
var.value = entry.getValue(); var.value = entry.getValue();
t.put(entry.getKey(), var); t.put(entry.getKey(), var);
} }
// Restore previous variables // Restore previous constants
Variables.variables().putAll(currentVariables); ScopeHandler.constants().putAll(currentConstants);
} }
return super.visit(s, t); return super.visit(s, t);
} }
private boolean canLoadConstants(Expression expression) {
if (expression instanceof ArrayExpression ae) {
for (Expression expr : ae.elements) {
if (!isValue(expr)) {
return false;
}
}
return true;
}
return isValue(expression);
}
@Override @Override
protected boolean visit(Arguments in, Arguments out, Map<String, VariableInfo> t) { protected boolean visit(Arguments in, Arguments out, Map<String, VariableInfo> t) {
for (Argument argument : in) { for (Argument argument : in) {

View File

@ -192,6 +192,6 @@ public abstract class AbstractVisitor implements Visitor {
@Override @Override
public void visit(UseStatement st) { public void visit(UseStatement st) {
st.expression.accept(this);
} }
} }

View File

@ -23,14 +23,7 @@ public class ModuleDetector extends AbstractVisitor {
@Override @Override
public void visit(UseStatement st) { public void visit(UseStatement st) {
if (st.expression instanceof ArrayExpression ae) { modules.addAll(st.modules);
for (Expression expr : ae.elements) {
modules.add(expr.eval().asString());
}
}
if (st.expression instanceof ValueExpression ve) {
modules.add(ve.value.asString());
}
super.visit(st); super.visit(st);
} }
} }

View File

@ -358,7 +358,7 @@ public class PrintVisitor implements ResultVisitor<StringBuilder, StringBuilder>
@Override @Override
public StringBuilder visit(UseStatement s, StringBuilder t) { public StringBuilder visit(UseStatement s, StringBuilder t) {
t.append("use "); t.append("use ");
s.expression.accept(this, t); t.append(String.join(", ", s.modules));
return t; return t;
} }

View File

@ -30,14 +30,13 @@ public final class ModulesInfoCreator {
for (String moduleName : moduleNames) { for (String moduleName : moduleNames) {
final String moduleClassPath = String.format("com.annimon.ownlang.modules.%s.%s", moduleName, moduleName); final String moduleClassPath = String.format("com.annimon.ownlang.modules.%s.%s", moduleName, moduleName);
Class<?> moduleClass = Class.forName(moduleClassPath); Class<?> moduleClass = Class.forName(moduleClassPath);
Functions.getFunctions().clear(); ScopeHandler.resetScope();
Variables.variables().clear();
final Module module = (Module) moduleClass.getDeclaredConstructor().newInstance(); final Module module = (Module) moduleClass.getDeclaredConstructor().newInstance();
module.init(); module.init();
final ModuleInfo moduleInfo = new ModuleInfo(moduleName); final ModuleInfo moduleInfo = new ModuleInfo(moduleName);
moduleInfo.functions.addAll(Functions.getFunctions().keySet()); moduleInfo.functions.addAll(ScopeHandler.functions().keySet());
moduleInfo.constants.putAll(Variables.variables()); moduleInfo.constants.putAll(ScopeHandler.constants());
moduleInfo.types.addAll(listValues(moduleClass)); moduleInfo.types.addAll(listValues(moduleClass));
moduleInfos.add(moduleInfo); moduleInfos.add(moduleInfo);
} }

View File

@ -135,7 +135,7 @@ public final class Repl {
final int maxCols = 2; final int maxCols = 2;
final int size = commands.size(); final int size = commands.size();
for (int i = 0; i < size; i += maxCols) { for (int i = 0; i < size; i += maxCols) {
// Pad to max length and print in tab-separatex maxCols columns // Pad to max length and print in tab-separated maxCols columns
System.out.println(commands System.out.println(commands
.subList(i, Math.min(size, i + maxCols)) .subList(i, Math.min(size, i + maxCols))
.stream() .stream()