Compare commits
2 Commits
8f0e8f68bb
...
45829ecc7e
Author | SHA1 | Date |
---|---|---|
|
45829ecc7e | |
|
0133a237ac |
|
@ -0,0 +1,2 @@
|
|||
.class
|
||||
target/
|
|
@ -5,8 +5,8 @@
|
|||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.yourname.scripts</groupId>
|
||||
<artifactId>cparser-tests</artifactId>
|
||||
<groupId>cparser</groupId>
|
||||
<artifactId>cparser</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<dependencies>
|
||||
|
@ -20,13 +20,8 @@
|
|||
</dependencies>
|
||||
|
||||
<build>
|
||||
<!--
|
||||
Configure Maven to recognize the main source directory outside the default
|
||||
src/main/java by setting the sourceDirectory to ../../cparser relative to pom.xml.
|
||||
Similarly, set the testSourceDirectory to the current directory where ParserTests.java resides.
|
||||
-->
|
||||
<sourceDirectory>../../cparser</sourceDirectory>
|
||||
<testSourceDirectory>.</testSourceDirectory>
|
||||
<!-- Specify the custom test source directory -->
|
||||
<testSourceDirectory>./src/test/java</testSourceDirectory>
|
||||
|
||||
<plugins>
|
||||
<!-- Compiler Plugin to specify Java version -->
|
|
@ -0,0 +1,124 @@
|
|||
package cparser;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class AST {
|
||||
|
||||
public static class Error {
|
||||
public final Span span;
|
||||
public final String message;
|
||||
|
||||
public Error(Span span, String message) {
|
||||
this.span = span;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Span {
|
||||
public final int startOffset;
|
||||
public final int endOffset;
|
||||
|
||||
public Span(int startOffset, int endOffset) {
|
||||
this.startOffset = startOffset;
|
||||
this.endOffset = endOffset;
|
||||
}
|
||||
|
||||
public Span() {
|
||||
this.startOffset = -1;
|
||||
this.endOffset = -1;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return endOffset > startOffset;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PreprocessorExpression {
|
||||
public final Span span;
|
||||
|
||||
public PreprocessorExpression(Span span) {
|
||||
this.span = span;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Type {
|
||||
// The entire type definition, e.g. "byte**"
|
||||
public final Span span;
|
||||
// the base type name, e.g. byte in "byte* (&value)[20]"
|
||||
public final Span baseSpan;
|
||||
|
||||
public Type(Span span, Span baseSpan) {
|
||||
this.span = span;
|
||||
this.baseSpan = baseSpan;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Identifier {
|
||||
public final Span span;
|
||||
|
||||
public Identifier(Span span) {
|
||||
this.span = span;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ArgumentList {
|
||||
public final List<Object> arguments;
|
||||
|
||||
public ArgumentList(List<Object> arguments) {
|
||||
this.arguments = arguments;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Expr {
|
||||
public final Span span;
|
||||
|
||||
public Expr(Span span) {
|
||||
this.span = span;
|
||||
}
|
||||
};
|
||||
|
||||
public static class VariableDeclaration {
|
||||
public final String type;
|
||||
public final String identifier;
|
||||
public final boolean isPointer;
|
||||
public final boolean isReference;
|
||||
public final boolean isArray;
|
||||
public final int arraySize;
|
||||
public final boolean isFunction;
|
||||
public final Span span;
|
||||
|
||||
public VariableDeclaration(String type, String identifier, boolean isPointer, boolean isReference,
|
||||
boolean isArray, int arraySize, boolean isFunction, Span span) {
|
||||
this.type = type;
|
||||
this.identifier = identifier;
|
||||
this.isPointer = isPointer;
|
||||
this.isReference = isReference;
|
||||
this.isArray = isArray;
|
||||
this.arraySize = arraySize;
|
||||
this.isFunction = isFunction;
|
||||
this.span = span;
|
||||
}
|
||||
}
|
||||
|
||||
public static class FunctionDecl {
|
||||
public final Identifier name;
|
||||
public final Type returnValue;
|
||||
public final ArgumentList args;
|
||||
|
||||
public FunctionDecl(Identifier name, Type returnValue, ArgumentList args) {
|
||||
this.name = name;
|
||||
this.returnValue = returnValue;
|
||||
this.args = args;
|
||||
}
|
||||
}
|
||||
|
||||
public static class FunctionCall {
|
||||
public final Identifier name;
|
||||
public final ArgumentList args;
|
||||
|
||||
public FunctionCall(Identifier name, ArgumentList args) {
|
||||
this.name = name;
|
||||
this.args = args;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,358 @@
|
|||
package cparser;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import cparser.Tokenizer.Token;
|
||||
import cparser.Log;
|
||||
import cparser.AST.VariableDeclaration;
|
||||
|
||||
public class Parser {
|
||||
private Tokenizer.TokenSet tokenSet;
|
||||
private Log log;
|
||||
private Tokenizer.Token[] tokens;
|
||||
|
||||
private List<Object> statements = new ArrayList<>();
|
||||
private List<VariableDeclaration> variableDeclarations = new ArrayList<>();
|
||||
private List<AST.Span> commentStack = new ArrayList<>();
|
||||
private List<AST.Error> errors = new ArrayList<>();
|
||||
|
||||
public Parser(Tokenizer.TokenSet tokenSet, Log log) {
|
||||
this.tokenSet = tokenSet;
|
||||
this.statements = new ArrayList<>();
|
||||
this.tokens = tokenSet.getTokens();
|
||||
}
|
||||
|
||||
void log(String msg) {
|
||||
if (log != null) {
|
||||
log.log(msg);
|
||||
}
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
|
||||
public List<VariableDeclaration> getVariableDeclarations() {
|
||||
return variableDeclarations;
|
||||
}
|
||||
|
||||
private static class Result<T> {
|
||||
public final T value;
|
||||
public final AST.Span span;
|
||||
|
||||
public Result(T value, AST.Span span) {
|
||||
this.value = value;
|
||||
this.span = span;
|
||||
}
|
||||
|
||||
public Result() {
|
||||
this.value = null;
|
||||
this.span = new AST.Span();
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return span.isValid();
|
||||
}
|
||||
|
||||
public static <T> Result<T> none() {
|
||||
return new Result<T>();
|
||||
}
|
||||
|
||||
public Result<Object> toGeneric() {
|
||||
return new Result<Object>(value, span);
|
||||
}
|
||||
}
|
||||
|
||||
boolean applyResult(Result<?> result) {
|
||||
if (result.span.isValid()) {
|
||||
index = result.span.endOffset + 1;
|
||||
|
||||
if (result.value instanceof VariableDeclaration) {
|
||||
variableDeclarations.add((VariableDeclaration) result.value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void addError(AST.Span span, String message) {
|
||||
errors.add(new AST.Error(span, message));
|
||||
}
|
||||
|
||||
private void addError(Tokenizer.Token token, String message) {
|
||||
addError(token.getSpan(), message);
|
||||
}
|
||||
|
||||
public void parse() {
|
||||
for (index = 0; index < tokens.length;) {
|
||||
Tokenizer.Token token = tokens[index];
|
||||
if (token.type == Tokenizer.TokenType.BLOCK_COMMENT || token.type == Tokenizer.TokenType.COMMENT) {
|
||||
commentStack.add(token.getSpan());
|
||||
index++;
|
||||
continue;
|
||||
} else if (token.type == Tokenizer.TokenType.HASH) {
|
||||
Result<AST.PreprocessorExpression> result = parsePreprocessorExpression();
|
||||
if (result.span.isValid()) {
|
||||
index = result.span.endOffset + 1;
|
||||
} else {
|
||||
addError(token, "Invalid preprocessor expression");
|
||||
index++;
|
||||
}
|
||||
} else {
|
||||
Result<?> stmt = parseStmt();
|
||||
if (applyResult(stmt))
|
||||
continue;
|
||||
addError(token, "Invalid statement");
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Result<VariableDeclaration> parseVarDecl() {
|
||||
int startIndex = index;
|
||||
StringBuilder type = new StringBuilder();
|
||||
String identifier = null;
|
||||
boolean isPointer = false;
|
||||
boolean isReference = false;
|
||||
boolean isArray = false;
|
||||
boolean isFunction = false;
|
||||
int arraySize = -1;
|
||||
|
||||
boolean haveTypeIdentifier = false;
|
||||
boolean haveIdentifier = false;
|
||||
|
||||
int parenDepth = 0;
|
||||
int idxStart = 0;
|
||||
int idxDepth = 0;
|
||||
|
||||
// Parse type
|
||||
while (index < tokens.length) {
|
||||
Token token = tokens[index];
|
||||
if (token.type == Tokenizer.TokenType.IDENTIFIER || token.type == Tokenizer.TokenType.OTHER) {
|
||||
// Already have a type identifier, so this is most likely a variable name
|
||||
if (!haveTypeIdentifier) {
|
||||
type.append(tokenSet.getTextNoNewlines(token));
|
||||
haveTypeIdentifier = true;
|
||||
} else if (!haveIdentifier) {
|
||||
identifier = tokenSet.getTextNoNewlines(token);
|
||||
haveIdentifier = true;
|
||||
if (parenDepth == 0)
|
||||
break;
|
||||
} else {
|
||||
// Unknown
|
||||
addError(token, "Unknown token after identifier");
|
||||
}
|
||||
index++;
|
||||
} else if (token.type == Tokenizer.TokenType.STAR) {
|
||||
isPointer = true;
|
||||
type.append("*");
|
||||
index++;
|
||||
} else if (token.type == Tokenizer.TokenType.AMPERSAND) {
|
||||
isReference = true;
|
||||
type.append("&");
|
||||
index++;
|
||||
} else if (token.type == Tokenizer.TokenType.L_PAREN) {
|
||||
parenDepth++;
|
||||
index++;
|
||||
} else if (token.type == Tokenizer.TokenType.R_PAREN) {
|
||||
parenDepth--;
|
||||
index++;
|
||||
} else if (token.type == Tokenizer.TokenType.L_IDX) {
|
||||
idxDepth++;
|
||||
idxStart = token.ofs + 1;
|
||||
if (haveIdentifier) {
|
||||
// Parse function parameters?
|
||||
}
|
||||
break;
|
||||
} else if (token.type == Tokenizer.TokenType.R_IDX) {
|
||||
idxDepth--;
|
||||
if (idxDepth == 0) {
|
||||
String idxVal = tokenSet.getTextNoNewlines(idxStart, (token.ofs - 1) - idxStart);
|
||||
type.append("[" + idxVal + "]");
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AST.Span span = new AST.Span(tokens[startIndex].ofs, tokens[index].getEnd());
|
||||
VariableDeclaration varDecl = new VariableDeclaration(
|
||||
type.toString().trim(),
|
||||
identifier,
|
||||
isPointer,
|
||||
isReference,
|
||||
isArray,
|
||||
arraySize,
|
||||
isFunction,
|
||||
span);
|
||||
|
||||
return new Result<>(varDecl, span);
|
||||
}
|
||||
|
||||
private Result<?> parseStmt() {
|
||||
int startIndex = index;
|
||||
for (int i = startIndex; i < tokens.length; i++) {
|
||||
Tokenizer.Token token = tokens[i];
|
||||
|
||||
Result<VariableDeclaration> varDeclResult = parseVarDecl();
|
||||
if (varDeclResult.isValid())
|
||||
return varDeclResult;
|
||||
|
||||
// if (token.type == Tokenizer.TokenType.L_PAREN && idStack.size() > 0) {
|
||||
// // Function call?
|
||||
// } else if (token.type == Tokenizer.TokenType.SEMICOLON) {
|
||||
// boolean isVarAssign = false;
|
||||
// for (int j = startIndex; j < i; j++) {
|
||||
// if (tokens[j].type == Tokenizer.TokenType.EQUALS) {
|
||||
// isVarAssign = true;
|
||||
// }
|
||||
// }
|
||||
// } else if (token.type == Tokenizer.TokenType.L_BRACE) {
|
||||
// boolean isVarAssign = false;
|
||||
// for (int j = startIndex; j < i; j++) {
|
||||
// if (tokens[j].type == Tokenizer.TokenType.L_PAREN) {
|
||||
// int endIndex = findClosingParenthesis(j);
|
||||
// if (endIndex != -1) {
|
||||
// index = endIndex;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
return Result.none();
|
||||
// if (index + 1 < tokens.length && tokens[index + 1].type ==
|
||||
// Tokenizer.TokenType.L_PAREN) {
|
||||
// // Function call or declaration/definition
|
||||
// if (index > 0 && (tokens[index - 1].type == Tokenizer.TokenType.IDENTIFIER ||
|
||||
// tokens[index - 1].type == Tokenizer.TokenType.OTHER)) {
|
||||
// // Function declaration or definition
|
||||
// index = parseFunctionDeclaration();
|
||||
// } else {
|
||||
// // Function call
|
||||
// index = parseFunctionCall();
|
||||
// }
|
||||
// } else {
|
||||
// // Variable reference
|
||||
// index = parseVariableReference();
|
||||
// }
|
||||
}
|
||||
|
||||
private int parseVarDecl(int startIndex, int endIndex) {
|
||||
if (tokens[startIndex].type == Tokenizer.TokenType.R_PAREN) {
|
||||
return startIndex;
|
||||
}
|
||||
return startIndex + 1;
|
||||
}
|
||||
|
||||
// Try to parse prep expression
|
||||
private Result<AST.PreprocessorExpression> parsePreprocessorExpression() {
|
||||
int startIndex = index;
|
||||
int index = startIndex;
|
||||
if (tokenSet.tokens[index].type == Tokenizer.TokenType.HASH) {
|
||||
int startLine = tokenSet.getLine(index);
|
||||
while (index < tokenSet.tokens.length) {
|
||||
if (tokenSet.getLine(index) > startLine) {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
// Find first next line token
|
||||
index--;
|
||||
}
|
||||
|
||||
AST.Span span = new AST.Span(startIndex, index);
|
||||
return new Result(new AST.PreprocessorExpression(span), span);
|
||||
}
|
||||
|
||||
// private ArgumentList parseArgumentList(int startIndex, int endIndex) {
|
||||
// List<Object> arguments = new ArrayList<>();
|
||||
// for (int i = startIndex; i < endIndex; i++) {
|
||||
// if (tokens[i].type == Tokenizer.TokenType.COMMA) {
|
||||
|
||||
// }
|
||||
// }
|
||||
// return new ArgumentList(arguments);
|
||||
// }
|
||||
|
||||
// // Try to parse function declaration and return the ending token index
|
||||
// private int parseFunctionDeclaration() {
|
||||
// Tokenizer.Token[] tokens = tokenSet.getTokens();
|
||||
// String name = tokenSet.getTextNoNewlines(tokens[index]);
|
||||
// int endIndex = findClosingParenthesis(index + 1);
|
||||
|
||||
// if (endIndex == -1)
|
||||
// return index;
|
||||
|
||||
// boolean isDefinition = false;
|
||||
// if (endIndex + 1 < tokens.length && tokens[endIndex + 1].type ==
|
||||
// Tokenizer.TokenType.L_BRACE) {
|
||||
// isDefinition = true;
|
||||
// endIndex = findClosingBrace(endIndex + 1);
|
||||
// }
|
||||
|
||||
// if (endIndex == -1)
|
||||
// return index;
|
||||
|
||||
// Function function = new Function(name, tokens[index].ofs,
|
||||
// tokens[endIndex].ofs + tokens[endIndex].len,
|
||||
// isDefinition);
|
||||
// functions.add(function);
|
||||
// return endIndex - 1;
|
||||
// }
|
||||
|
||||
// // Try to parse function call and return the ending token index
|
||||
// private int parseFunctionCall() {
|
||||
// Tokenizer.Token[] tokens = tokenSet.getTokens();
|
||||
// String name = tokenSet.getTextNoNewlines(tokens[index]);
|
||||
// int endIndex = findClosingParenthesis(index + 1);
|
||||
// if (endIndex == -1)
|
||||
// return index;
|
||||
|
||||
// FunctionCall functionCall = new FunctionCall(name, tokens[index].ofs,
|
||||
// tokens[endIndex].ofs + tokens[endIndex].len);
|
||||
// functionCalls.add(functionCall);
|
||||
// return endIndex - 1;
|
||||
// }
|
||||
|
||||
// // Try to parse variable reference and add it to the list
|
||||
// private int parseVariableReference() {
|
||||
// Tokenizer.Token token = tokenSet.getTokens()[index];
|
||||
// String name = tokenSet.getTextNoNewlines(token);
|
||||
// Variable variable = new Variable(name, token.ofs, token.ofs + token.len);
|
||||
// variables.add(variable);
|
||||
// return index + 1;
|
||||
// }
|
||||
|
||||
private int findClosingParenthesis(int startIndex) {
|
||||
Tokenizer.Token[] tokens = tokenSet.getTokens();
|
||||
int parenCount = 1;
|
||||
for (int i = startIndex + 1; i < tokens.length; i++) {
|
||||
if (tokens[i].type == Tokenizer.TokenType.L_PAREN) {
|
||||
parenCount++;
|
||||
} else if (tokens[i].type == Tokenizer.TokenType.R_PAREN) {
|
||||
parenCount--;
|
||||
if (parenCount == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int findClosingBrace(int startIndex) {
|
||||
Tokenizer.Token[] tokens = tokenSet.getTokens();
|
||||
int braceCount = 1;
|
||||
for (int i = startIndex + 1; i < tokens.length; i++) {
|
||||
if (tokens[i].type == Tokenizer.TokenType.L_BRACE) {
|
||||
braceCount++;
|
||||
} else if (tokens[i].type == Tokenizer.TokenType.R_BRACE) {
|
||||
braceCount--;
|
||||
if (braceCount == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
|
@ -13,7 +13,13 @@ public class Tokenizer {
|
|||
R_PAREN,
|
||||
L_BRACE,
|
||||
R_BRACE,
|
||||
L_IDX,
|
||||
R_IDX,
|
||||
SEMICOLON,
|
||||
EQUALS,
|
||||
ARROW,
|
||||
STAR,
|
||||
AMPERSAND,
|
||||
COMMA,
|
||||
COMMENT,
|
||||
BLOCK_COMMENT,
|
||||
|
@ -29,9 +35,17 @@ public class Tokenizer {
|
|||
public int ofs;
|
||||
public int len;
|
||||
public TokenType type;
|
||||
|
||||
public AST.Span getSpan() {
|
||||
return new AST.Span(ofs, getEnd());
|
||||
}
|
||||
|
||||
public int getEnd() {
|
||||
return ofs + len;
|
||||
}
|
||||
}
|
||||
|
||||
public class TokenSet {
|
||||
public static class TokenSet {
|
||||
public final Token[] tokens;
|
||||
public final String text;
|
||||
private final TreeMap<Integer, Integer> lineNumberTable;
|
||||
|
@ -51,10 +65,24 @@ public class Tokenizer {
|
|||
return entry != null ? entry.getValue() : -1;
|
||||
}
|
||||
|
||||
public String getText(Token token) {
|
||||
return getText(token.ofs, token.len);
|
||||
}
|
||||
|
||||
public String getText(int ofs, int len) {
|
||||
return text.substring(ofs, ofs + len); // Fixed recursion issue
|
||||
}
|
||||
|
||||
public String getTextNoNewlines(Token token) {
|
||||
String text = getText(token);
|
||||
return text.replace("\n", "");
|
||||
}
|
||||
|
||||
|
||||
public String getTextNoNewlines(int ofs, int len) {
|
||||
String text = getText(ofs, len);
|
||||
return text.replace("\n", "");
|
||||
}
|
||||
};
|
||||
|
||||
private final String text;
|
||||
|
@ -325,6 +353,20 @@ public class Tokenizer {
|
|||
newType = TokenType.COMMA;
|
||||
} else if (currentChar == '#') {
|
||||
newType = TokenType.HASH;
|
||||
} else if (currentChar == '&') {
|
||||
newType = TokenType.AMPERSAND;
|
||||
} else if (currentChar == '[') {
|
||||
newType = TokenType.L_IDX;
|
||||
} else if (currentChar == ']') {
|
||||
newType = TokenType.R_IDX;
|
||||
} else if (currentChar == '=') {
|
||||
newType = TokenType.EQUALS;
|
||||
} else if (currentChar == '>' && index > 0 && text.charAt(index - 1) == '-') {
|
||||
newType = TokenType.ARROW;
|
||||
currentType = TokenType.ARROW;
|
||||
tokenStart = index - 1;
|
||||
} else if (currentChar == '*') {
|
||||
newType = TokenType.STAR;
|
||||
}
|
||||
// Handle other characters
|
||||
else {
|
|
@ -0,0 +1,150 @@
|
|||
package cparser_tests;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import java.lang.String;
|
||||
|
||||
import cparser.Parser;
|
||||
import cparser.Tokenizer;
|
||||
import cparser.AST;
|
||||
import cparser.Log;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ParserTests {
|
||||
private Parser parser;
|
||||
private Tokenizer.TokenSet tokenSet;
|
||||
private Log testLog;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
testLog = new Log() {
|
||||
@Override
|
||||
public void log(String msg) {
|
||||
System.out.println(msg);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseVariableDecl() {
|
||||
String[] testCases = {
|
||||
"byte RVar1;",
|
||||
"tdstLastErrorInfo *pdVar2;",
|
||||
"undefined4 *puVar2;",
|
||||
"CHAR pathToUbi_ini[260];",
|
||||
"undefined& DAT_005a9ed4;",
|
||||
"char(&s_Identifier_005b6420)[16];",
|
||||
"void (*fnType)(int j, char, bool);",
|
||||
"void** (*fnType)(int j, char, bool);"
|
||||
};
|
||||
|
||||
for (String code : testCases) {
|
||||
tokenSet = new Tokenizer(code).parse();
|
||||
parser = new Parser(tokenSet, testLog);
|
||||
parser.parse();
|
||||
|
||||
List<AST.VariableDeclaration> declarations = parser.getVariableDeclarations();
|
||||
assertEquals("Failed for case: " + code, 1, declarations.size());
|
||||
AST.VariableDeclaration decl = declarations.get(0);
|
||||
assertNotNull(decl);
|
||||
System.out.println("Parsed: " + code);
|
||||
System.out.println(" Type: " + decl.type);
|
||||
System.out.println(" Identifier: " + decl.identifier);
|
||||
System.out.println(" Is Pointer: " + decl.isPointer);
|
||||
System.out.println(" Is Reference: " + decl.isReference);
|
||||
System.out.println(" Is Array: " + decl.isArray);
|
||||
System.out.println(" Array Size: " + decl.arraySize);
|
||||
System.out.println(" Is Function: " + decl.isFunction);
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Test
|
||||
* public void testParseFunctionDeclaration() {
|
||||
* String code = "void foo(int a, int b);";
|
||||
* tokenSet = new Tokenizer(code).parse();
|
||||
* parser = new Parser(tokenSet, testLog);
|
||||
* parser.parse();
|
||||
*
|
||||
* List<Parser.Function> functions = parser.getFunctions();
|
||||
* assertEquals(1, functions.size());
|
||||
* assertEquals("foo", functions.get(0).name);
|
||||
* assertFalse(functions.get(0).isDefinition);
|
||||
* }
|
||||
*
|
||||
* @Test
|
||||
* public void testParseFunctionDefinition() {
|
||||
* String code = "int bar(int x) { return x + 1; }";
|
||||
* tokenSet = new Tokenizer(code).parse();
|
||||
* parser = new Parser(tokenSet, testLog);
|
||||
* parser.parse();
|
||||
*
|
||||
* List<Parser.Function> functions = parser.getFunctions();
|
||||
* assertEquals(1, functions.size());
|
||||
* assertEquals("bar", functions.get(0).name);
|
||||
* assertTrue(functions.get(0).isDefinition);
|
||||
* }
|
||||
*
|
||||
* @Test
|
||||
* public void testParseFunctionCall() {
|
||||
* String code = "result = calculate(5, 10);";
|
||||
* tokenSet = new Tokenizer(code).parse();
|
||||
* parser = new Parser(tokenSet, testLog);
|
||||
* parser.parse();
|
||||
*
|
||||
* List<Parser.FunctionCall> functionCalls = parser.getFunctionCalls();
|
||||
* assertEquals(1, functionCalls.size());
|
||||
* assertEquals("calculate", functionCalls.get(0).name);
|
||||
* }
|
||||
*
|
||||
* @Test
|
||||
* public void testParsePreprocessorDirective() {
|
||||
* String code = "#include <stdio.h>\nint main() { return 0; }";
|
||||
* tokenSet = new Tokenizer(code).parse();
|
||||
* parser = new Parser(tokenSet, testLog);
|
||||
* parser.parse();
|
||||
*
|
||||
* List<Parser.Function> functions = parser.getFunctions();
|
||||
* assertEquals(1, functions.size());
|
||||
* assertEquals("main", functions.get(0).name);
|
||||
* }
|
||||
*
|
||||
* @Test
|
||||
* public void testParseComplexCode() {
|
||||
* String code =
|
||||
* "#include <stdio.h>\n" +
|
||||
* "int globalVar = 10;\n" +
|
||||
* "void helper(int x);\n" +
|
||||
* "int main() {\n" +
|
||||
* " int localVar = 5;\n" +
|
||||
* " helper(localVar);\n" +
|
||||
* " return 0;\n" +
|
||||
* "}\n" +
|
||||
* "void helper(int x) {\n" +
|
||||
* " printf(\"%d\", x);\n" +
|
||||
* "}";
|
||||
*
|
||||
* tokenSet = new Tokenizer(code).parse();
|
||||
* parser = new Parser(tokenSet, testLog);
|
||||
* parser.parse();
|
||||
*
|
||||
* List<Parser.Variable> variables = parser.getVariables();
|
||||
* List<Parser.Function> functions = parser.getFunctions();
|
||||
* List<Parser.FunctionCall> functionCalls = parser.getFunctionCalls();
|
||||
*
|
||||
* assertEquals(2, variables.size());
|
||||
* assertEquals(2, functions.size());
|
||||
* assertEquals(2, functionCalls.size());
|
||||
*
|
||||
* assertTrue(variables.stream().anyMatch(v -> v.name.equals("globalVar")));
|
||||
* assertTrue(variables.stream().anyMatch(v -> v.name.equals("localVar")));
|
||||
* assertTrue(functions.stream().anyMatch(f -> f.name.equals("main")));
|
||||
* assertTrue(functions.stream().anyMatch(f -> f.name.equals("helper")));
|
||||
* assertTrue(functionCalls.stream().anyMatch(fc -> fc.name.equals("helper")));
|
||||
* assertTrue(functionCalls.stream().anyMatch(fc -> fc.name.equals("printf")));
|
||||
* }
|
||||
*/
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
// Script to categorize Stuff
|
||||
// @menupath File.Run.ScriptName
|
||||
// @category _Reman3
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import re3lib.*;
|
||||
|
||||
public class NewScript extends GhidraScript {
|
||||
|
||||
@Override
|
||||
protected void run() throws Exception {
|
||||
// TODO: Add your script logic here
|
||||
println("Hello from NewScript!");
|
||||
}
|
||||
}
|
|
@ -1,202 +0,0 @@
|
|||
package cparser;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import cparser.Tokenizer.Token;
|
||||
import cparser.Log;
|
||||
|
||||
public class Parser {
|
||||
private Tokenizer.TokenSet tokenSet;
|
||||
private List<Variable> variables;
|
||||
private List<Function> functions;
|
||||
private List<FunctionCall> functionCalls;
|
||||
private Log log;
|
||||
|
||||
public Parser(Tokenizer.TokenSet tokenSet, Log log) {
|
||||
this.tokenSet = tokenSet;
|
||||
this.variables = new ArrayList<>();
|
||||
this.functions = new ArrayList<>();
|
||||
this.functionCalls = new ArrayList<>();
|
||||
}
|
||||
|
||||
void log(String msg) {
|
||||
if (log != null) {
|
||||
log.log(msg);
|
||||
}
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
|
||||
public void parse() {
|
||||
Tokenizer.Token[] tokens = tokenSet.getTokens();
|
||||
for (index = 0; index < tokens.length; index++) {
|
||||
Tokenizer.Token token = tokens[index];
|
||||
if (token.type == Tokenizer.TokenType.BLOCK_COMMENT || token.type == Tokenizer.TokenType.COMMENT) {
|
||||
continue;
|
||||
} else if (token.type == Tokenizer.TokenType.HASH) {
|
||||
index = parsePreprocessorExpression();
|
||||
} else if (tokens[index].type == Tokenizer.TokenType.IDENTIFIER) {
|
||||
if (index + 1 < tokens.length && tokens[index + 1].type == Tokenizer.TokenType.L_PAREN) {
|
||||
// Function call or declaration/definition
|
||||
if (index > 0 && (tokens[index - 1].type == Tokenizer.TokenType.IDENTIFIER ||
|
||||
tokens[index - 1].type == Tokenizer.TokenType.OTHER)) {
|
||||
// Function declaration or definition
|
||||
index = parseFunctionDeclaration();
|
||||
} else {
|
||||
// Function call
|
||||
index = parseFunctionCall();
|
||||
}
|
||||
} else {
|
||||
// Variable reference
|
||||
index = parseVariableReference();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to parse prep expression
|
||||
private int parsePreprocessorExpression() {
|
||||
int index = this.index;
|
||||
if (tokenSet.tokens[index].type == Tokenizer.TokenType.HASH) {
|
||||
int startLine = tokenSet.getLine(index);
|
||||
while (index < tokenSet.tokens.length) {
|
||||
if (tokenSet.getLine(index) > startLine) {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
// Find first next line token
|
||||
index--;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
// Try to parse function declaration and return the ending token index
|
||||
private int parseFunctionDeclaration() {
|
||||
Tokenizer.Token[] tokens = tokenSet.getTokens();
|
||||
String name = tokenSet.getTextNoNewlines(tokens[index]);
|
||||
int endIndex = findClosingParenthesis(index + 1);
|
||||
|
||||
if (endIndex == -1)
|
||||
return index;
|
||||
|
||||
boolean isDefinition = false;
|
||||
if (endIndex + 1 < tokens.length && tokens[endIndex + 1].type == Tokenizer.TokenType.L_BRACE) {
|
||||
isDefinition = true;
|
||||
endIndex = findClosingBrace(endIndex + 1);
|
||||
}
|
||||
|
||||
if (endIndex == -1)
|
||||
return index;
|
||||
|
||||
Function function = new Function(name, tokens[index].ofs, tokens[endIndex].ofs + tokens[endIndex].len,
|
||||
isDefinition);
|
||||
functions.add(function);
|
||||
return endIndex - 1;
|
||||
}
|
||||
|
||||
// Try to parse function call and return the ending token index
|
||||
private int parseFunctionCall() {
|
||||
Tokenizer.Token[] tokens = tokenSet.getTokens();
|
||||
String name = tokenSet.getTextNoNewlines(tokens[index]);
|
||||
int endIndex = findClosingParenthesis(index + 1);
|
||||
if (endIndex == -1)
|
||||
return index;
|
||||
|
||||
FunctionCall functionCall = new FunctionCall(name, tokens[index].ofs,
|
||||
tokens[endIndex].ofs + tokens[endIndex].len);
|
||||
functionCalls.add(functionCall);
|
||||
return endIndex - 1;
|
||||
}
|
||||
|
||||
// Try to parse variable reference and add it to the list
|
||||
private int parseVariableReference() {
|
||||
Tokenizer.Token token = tokenSet.getTokens()[index];
|
||||
String name = tokenSet.getTextNoNewlines(token);
|
||||
Variable variable = new Variable(name, token.ofs, token.ofs + token.len);
|
||||
variables.add(variable);
|
||||
return index + 1;
|
||||
}
|
||||
|
||||
private int findClosingParenthesis(int startIndex) {
|
||||
Tokenizer.Token[] tokens = tokenSet.getTokens();
|
||||
int parenCount = 1;
|
||||
for (int i = startIndex + 1; i < tokens.length; i++) {
|
||||
if (tokens[i].type == Tokenizer.TokenType.L_PAREN) {
|
||||
parenCount++;
|
||||
} else if (tokens[i].type == Tokenizer.TokenType.R_PAREN) {
|
||||
parenCount--;
|
||||
if (parenCount == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int findClosingBrace(int startIndex) {
|
||||
Tokenizer.Token[] tokens = tokenSet.getTokens();
|
||||
int braceCount = 1;
|
||||
for (int i = startIndex + 1; i < tokens.length; i++) {
|
||||
if (tokens[i].type == Tokenizer.TokenType.L_BRACE) {
|
||||
braceCount++;
|
||||
} else if (tokens[i].type == Tokenizer.TokenType.R_BRACE) {
|
||||
braceCount--;
|
||||
if (braceCount == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public List<Variable> getVariables() {
|
||||
return variables;
|
||||
}
|
||||
|
||||
public List<Function> getFunctions() {
|
||||
return functions;
|
||||
}
|
||||
|
||||
public List<FunctionCall> getFunctionCalls() {
|
||||
return functionCalls;
|
||||
}
|
||||
|
||||
public static class Variable {
|
||||
public final String name;
|
||||
public final int startOffset;
|
||||
public final int endOffset;
|
||||
|
||||
public Variable(String name, int startOffset, int endOffset) {
|
||||
this.name = name;
|
||||
this.startOffset = startOffset;
|
||||
this.endOffset = endOffset;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Function {
|
||||
public final String name;
|
||||
public final int startOffset;
|
||||
public final int endOffset;
|
||||
public final boolean isDefinition;
|
||||
|
||||
public Function(String name, int startOffset, int endOffset, boolean isDefinition) {
|
||||
this.name = name;
|
||||
this.startOffset = startOffset;
|
||||
this.endOffset = endOffset;
|
||||
this.isDefinition = isDefinition;
|
||||
}
|
||||
}
|
||||
|
||||
public static class FunctionCall {
|
||||
public final String name;
|
||||
public final int startOffset;
|
||||
public final int endOffset;
|
||||
|
||||
public FunctionCall(String name, int startOffset, int endOffset) {
|
||||
this.name = name;
|
||||
this.startOffset = startOffset;
|
||||
this.endOffset = endOffset;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
package cparser.tests;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import java.lang.String;
|
||||
|
||||
import cparser.Parser;
|
||||
import cparser.Tokenizer;
|
||||
import cparser.Log;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ParserTests {
|
||||
|
||||
private Parser parser;
|
||||
private Tokenizer.TokenSet tokenSet;
|
||||
private Log mockLog;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mockLog = new Log() {
|
||||
@Override
|
||||
public void log(String msg) {
|
||||
// Do nothing for tests
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseVariableReference() {
|
||||
String code = "int x = 5;";
|
||||
tokenSet = new Tokenizer(code).parse();
|
||||
parser = new Parser(tokenSet, mockLog);
|
||||
parser.parse();
|
||||
|
||||
List<Parser.Variable> variables = parser.getVariables();
|
||||
assertEquals(1, variables.size());
|
||||
assertEquals("x", variables.get(0).name);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseFunctionDeclaration() {
|
||||
String code = "void foo(int a, int b);";
|
||||
tokenSet = new Tokenizer(code).parse();
|
||||
parser = new Parser(tokenSet, mockLog);
|
||||
parser.parse();
|
||||
|
||||
List<Parser.Function> functions = parser.getFunctions();
|
||||
assertEquals(1, functions.size());
|
||||
assertEquals("foo", functions.get(0).name);
|
||||
assertFalse(functions.get(0).isDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseFunctionDefinition() {
|
||||
String code = "int bar(int x) { return x + 1; }";
|
||||
tokenSet = new Tokenizer(code).parse();
|
||||
parser = new Parser(tokenSet, mockLog);
|
||||
parser.parse();
|
||||
|
||||
List<Parser.Function> functions = parser.getFunctions();
|
||||
assertEquals(1, functions.size());
|
||||
assertEquals("bar", functions.get(0).name);
|
||||
assertTrue(functions.get(0).isDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseFunctionCall() {
|
||||
String code = "result = calculate(5, 10);";
|
||||
tokenSet = new Tokenizer(code).parse();
|
||||
parser = new Parser(tokenSet, mockLog);
|
||||
parser.parse();
|
||||
|
||||
List<Parser.FunctionCall> functionCalls = parser.getFunctionCalls();
|
||||
assertEquals(1, functionCalls.size());
|
||||
assertEquals("calculate", functionCalls.get(0).name);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParsePreprocessorDirective() {
|
||||
String code = "#include <stdio.h>\nint main() { return 0; }";
|
||||
tokenSet = new Tokenizer(code).parse();
|
||||
parser = new Parser(tokenSet, mockLog);
|
||||
parser.parse();
|
||||
|
||||
List<Parser.Function> functions = parser.getFunctions();
|
||||
assertEquals(1, functions.size());
|
||||
assertEquals("main", functions.get(0).name);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseComplexCode() {
|
||||
String code =
|
||||
"#include <stdio.h>\n" +
|
||||
"int globalVar = 10;\n" +
|
||||
"void helper(int x);\n" +
|
||||
"int main() {\n" +
|
||||
" int localVar = 5;\n" +
|
||||
" helper(localVar);\n" +
|
||||
" return 0;\n" +
|
||||
"}\n" +
|
||||
"void helper(int x) {\n" +
|
||||
" printf(\"%d\", x);\n" +
|
||||
"}";
|
||||
|
||||
tokenSet = new Tokenizer(code).parse();
|
||||
parser = new Parser(tokenSet, mockLog);
|
||||
parser.parse();
|
||||
|
||||
List<Parser.Variable> variables = parser.getVariables();
|
||||
List<Parser.Function> functions = parser.getFunctions();
|
||||
List<Parser.FunctionCall> functionCalls = parser.getFunctionCalls();
|
||||
|
||||
assertEquals(2, variables.size());
|
||||
assertEquals(2, functions.size());
|
||||
assertEquals(2, functionCalls.size());
|
||||
|
||||
assertTrue(variables.stream().anyMatch(v -> v.name.equals("globalVar")));
|
||||
assertTrue(variables.stream().anyMatch(v -> v.name.equals("localVar")));
|
||||
assertTrue(functions.stream().anyMatch(f -> f.name.equals("main")));
|
||||
assertTrue(functions.stream().anyMatch(f -> f.name.equals("helper")));
|
||||
assertTrue(functionCalls.stream().anyMatch(fc -> fc.name.equals("helper")));
|
||||
assertTrue(functionCalls.stream().anyMatch(fc -> fc.name.equals("printf")));
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>cparser-tests</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
|
@ -1,4 +0,0 @@
|
|||
activeProfiles=
|
||||
eclipse.preferences.version=1
|
||||
resolveWorkspaceProjects=true
|
||||
version=1
|
Binary file not shown.
|
@ -1,57 +0,0 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||
http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.yourname.scripts</groupId>
|
||||
<artifactId>cparser-tests</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<dependencies>
|
||||
<!-- JUnit for testing -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<!--
|
||||
Configure Maven to recognize the main source directory outside the default
|
||||
src/main/java by setting the sourceDirectory to ../../cparser relative to pom.xml.
|
||||
Similarly, set the testSourceDirectory to the current directory where ParserTests.java resides.
|
||||
-->
|
||||
<sourceDirectory>../../cparser</sourceDirectory>
|
||||
<testSourceDirectory>.</testSourceDirectory>
|
||||
|
||||
<plugins>
|
||||
<!-- Compiler Plugin to specify Java version -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- Surefire Plugin to run JUnit tests -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.22.2</version>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/ParserTests.java</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
Loading…
Reference in New Issue