Skip to content

Commit

Permalink
Added preprocessor replacement, should fix #8.
Browse files Browse the repository at this point in the history
  • Loading branch information
SkaceKamen committed Apr 15, 2017
1 parent 7498440 commit f6f24ac
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 12 deletions.
20 changes: 19 additions & 1 deletion src/cz/zipek/sqflint/preprocessor/SQFMacro.java
Expand Up @@ -33,12 +33,16 @@
*/
public class SQFMacro {
private final String name;
private final String arguments;
private final List<SQFMacroDefinition> definitions = new ArrayList<>();
private final String source;
private final int line;

public SQFMacro(String name, String source) {
public SQFMacro(String name, String arguments, String source, int line) {
this.name = name;
this.arguments = arguments;
this.source = source;
this.line = line;
}

public void addDefinition(String filename, Token token, String value) {
Expand All @@ -65,4 +69,18 @@ public List<SQFMacroDefinition> getDefinitions() {
public String getSource() {
return source;
}

/**
* @return the arguments
*/
public String getArguments() {
return arguments;
}

/**
* @return the line
*/
public int getLine() {
return line;
}
}
184 changes: 173 additions & 11 deletions src/cz/zipek/sqflint/preprocessor/SQFPreprocessor.java
Expand Up @@ -23,6 +23,7 @@
*/
package cz.zipek.sqflint.preprocessor;

import cz.zipek.sqflint.SQFLint;
import cz.zipek.sqflint.linter.Linter;
import cz.zipek.sqflint.parser.Token;
import java.io.BufferedReader;
Expand All @@ -33,9 +34,13 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

Expand All @@ -47,6 +52,8 @@ public class SQFPreprocessor {
private final Map<String, SQFMacro> macros = new HashMap<>();
private final List<SQFInclude> includes = new ArrayList<>();

private final List<SQFMacro> sortedMacros = new ArrayList<>();

public SQFPreprocessor() {

}
Expand All @@ -65,35 +72,56 @@ public String process(String input, String source, boolean include_filename) thr

Pattern whitespaceAtStart = Pattern.compile("^\\s*");
Pattern doubleWhitespace = Pattern.compile("\\s{1,}");
Pattern comments = Pattern.compile("(\\/\\*[^*]*\\*\\/)|(\\/\\/.*)");

for(String line : lines) {
for (String line : lines) {
// Remove whitespaces at beginning
line = whitespaceAtStart.matcher(line).replaceAll("");
if (line.length() > 0 && line.charAt(0) == '#') {
String lineUpdated = whitespaceAtStart
.matcher(line)
.replaceAll("");

// Remove comments
lineUpdated = comments
.matcher(lineUpdated)
.replaceAll("");

if (lineUpdated.length() > 0 && lineUpdated.charAt(0) == '#') {
// Parse the line
String word = readUntil(line, 1, ' ');
String values = readUntil(line, 2 + word.length(), '\n', true);
String word = readUntil(lineUpdated, 1, ' ');
String values = readUntil(lineUpdated, 2 + word.length(), '\n', true);

switch(word.toLowerCase()) {
case "define":
String ident = readUntil(values, 0, ' ');
String value = null;
String arguments = null;

// Only load value if there is any
if (values.length() > ident.length() + 1) {
value = values.substring(ident.length() + 1).trim();
}


// Parse argumented macro
if (ident.indexOf('(') >= 0) {
arguments = ident.substring(ident.indexOf('(') + 1);
if (arguments.indexOf(')') >= 0) {
arguments = arguments.substring(0, arguments.indexOf(')'));
}
ident = ident.substring(0, ident.indexOf('('));
}

Token token = new Token(Linter.STRING_LITERAL);
token.beginLine = lineIndex + 1;
token.endLine = lineIndex + 1;
token.beginColumn = 1;
token.endColumn = values.length() + 1;

if (!macros.containsKey(ident.toLowerCase())) {
macros.put(ident.toLowerCase(), new SQFMacro(ident, source));
if (!macros.containsKey(ident)) {
macros.put(ident, new SQFMacro(ident, arguments, source, lineIndex));
sortedMacros.add(macros.get(ident));
}

macros.get(ident.toLowerCase()).addDefinition(
macros.get(ident).addDefinition(
include_filename ? source : null,
token,
value
Expand All @@ -117,11 +145,145 @@ public String process(String input, String source, boolean include_filename) thr
case "undef": break;
case "else": break;
}
} else {
try {
sortedMacros.sort((a, b) -> b.getName().length() - a.getName().length());

while (true) {
boolean replaced = false;
for (SQFMacro macro : sortedMacros) {
if (line.contains(macro.getName())) {
line = replaceMacro(line, macro);
replaced = true;
break;
}
}

if (!replaced) break;
}
} catch (Exception ex) {
Logger.getLogger(SQFLint.class.getName()).log(Level.SEVERE, "Failed to parse line " + lineIndex + " of " + source, ex);
System.exit(1);
}

// System.out.println("#" + lineIndex + "\t" + line);
}

lines[lineIndex++] = line;
}

return String.join("\n", lines);
}

private int walkToEnd(String input) {
int index = 0;
int bracket = 0;

while (index < input.length()) {
if (input.charAt(index) == '(') {
bracket++;
} else if (input.charAt(index) == ')') {
bracket--;
}

if (bracket < 0) {
return index;
}

index++;
}
return -1;
}

private String replaceMacro(String line, SQFMacro macro) {
int index = line.indexOf(macro.getName());
String value = null;

if (!macro.getDefinitions().isEmpty()) {
value = macro.getDefinitions().get(macro.getDefinitions().size() - 1).getValue();
}

if (value == null) {
value = "";
}

if (macro.getArguments() == null) {
/*
System.out.println("At line: '" + line + "'");
System.out.println("MACRO: " + macro.getName());
System.out.println("VALUE: " + value);
*/

line = line.substring(0, index) + value + line.substring(index + macro.getName().length());
} else {
String[] arguments = macro.getArguments().split(",");
String values = line.substring(line.indexOf('(', index) + 1);

/*if (values.indexOf(')') >= 0) {
values = values.substring(0, values.indexOf(')'));
}*/
int endIndex = walkToEnd(values);
if (endIndex != -1) {
values = values.substring(0, endIndex);
} else {
values = "";
}

String[] args = values.split(",");

// This is completely wrong, but #YOLO
// (I actually don't want to spend much time on this, because #YOLO)
// This works somewhat, so deal with it
for (int i = 0; i < arguments.length && i < args.length; i++) {
String argName = arguments[i].trim();
String argValue = args[i].trim();
String noletter = "([^a-zA-Z#])";

// @TODO: There has to be other way :O
value = value.replaceAll("##" + argName + "##", argValue);

value = value.replaceAll("^" + argName + "##", argValue);
value = value.replaceAll(noletter + argName + "##", "$1" + argValue);

value = value.replaceAll("##" + argName + "$", argValue);
value = value.replaceAll("##" + argName + noletter, argValue + "$1");

value = value.replaceAll("#" + argName + "$", '"' + argValue + '"');
value = value.replaceAll("#" + argName + noletter, '"' + argValue + "\"$1");

value = value.replaceAll("^" + argName + "$", argValue);
value = value.replaceAll("^" + argName + noletter, argValue + "$1");
value = value.replaceAll(noletter + argName + "$", "$1" + argValue);
value = value.replaceAll(noletter + argName + noletter, "$1" + argValue + "$2");
}

value = value.replaceAll("##", "");

/*
System.out.println("At line: '" + line + "'");
System.out.println("MACRO: '" + macro.getName() + "'");
System.out.println("ARGS: '" + macro.getArguments() + "'");
System.out.println("VALS: '" + values + "'");
System.out.println("REPLACE: " + value);
*/

String left = line.substring(0, index);
String right = "";
int rightIndex = index + macro.getName().length() + values.length() + 2;

if (rightIndex < line.length()) {
right = line.substring(rightIndex);
}
lineIndex++;

/*
System.out.println("LEFT: " + left);
System.out.println("RIGHT: " + right);
*/

line = left + value + right;
}

return output;
return line;
}

private String readUntil(String input, int from, char exit) {
Expand Down

0 comments on commit f6f24ac

Please sign in to comment.