// Utils
|
|
|
|
println = (x) {
|
|
if (!x) {
|
|
print("\n");
|
|
} else {
|
|
print(x, "\n", full: 1);
|
|
}
|
|
};
|
|
|
|
Object.subtype(name) { self.new(__name__: name) }
|
|
|
|
// Input stream
|
|
|
|
Stream = Object.subtype(#Stream);
|
|
|
|
newStream(string) {
|
|
self = Stream.new(
|
|
content: string,
|
|
position: 0,
|
|
limit: len(string)
|
|
);
|
|
print("Created new stream object: { position: ", self.position, ", limit: ", self.limit, ", !atEnd(): ", !self.atEnd(), " }\n");
|
|
self;
|
|
}
|
|
|
|
Stream.atEnd() { self.position >= self.limit }
|
|
Stream.peek() { !self.atEnd() && self.content[self.position] }
|
|
Stream.inc() { !self.atEnd() && { self.position = self.position + 1; } }
|
|
|
|
Stream.next() {
|
|
!self.atEnd() && {
|
|
c = self.content[self.position];
|
|
self.position = self.position + 1;
|
|
c;
|
|
}
|
|
}
|
|
|
|
Stream.setLastBegin = () { self.lastBegin = self.position; };
|
|
|
|
// Context
|
|
|
|
Context = Object.subtype(#Context);
|
|
Context.init() { self.variables = []; self; }
|
|
Context.declareVariable(var) { self.variables[var] = nil }
|
|
|
|
// ----- Grammar Constructs -----
|
|
|
|
// String Literal
|
|
|
|
StringLiteral = Object.subtype(#StringLiteral);
|
|
|
|
StringLiteral.match(stream, context, actions) {
|
|
|
|
n = len(self.string);
|
|
i = 0;
|
|
success = 1;
|
|
if (stream.atEnd()) { success = 0; }
|
|
startPosition = stream.position;
|
|
|
|
while(i < n && success == 1) {
|
|
if (self.string[i] != stream.peek()) {
|
|
success = 0;
|
|
stream.position = startPosition;
|
|
|
|
} else {
|
|
i = i + 1;
|
|
stream.inc();
|
|
}
|
|
}
|
|
success;
|
|
}
|
|
|
|
// Character Class
|
|
|
|
CharacterClass = Object.subtype(#CharacterClass);
|
|
|
|
CharacterClass.match(stream, context, actions) {
|
|
|
|
classLength = len(self.value);
|
|
i = 0;
|
|
prevChar = nil;
|
|
success = 0;
|
|
|
|
while (i < classLength && success == 0 && !stream.atEnd()) {
|
|
|
|
// [a] case
|
|
if (prevChar == nil) {
|
|
// println("[a] case");
|
|
prevChar = self.value[i];
|
|
|
|
if (stream.peek() == self.value[i]) {
|
|
success = 1;
|
|
}
|
|
|
|
} else if (prevChar != nil && self.value[i] == ord('-')) {
|
|
|
|
// [a-z] case
|
|
if (i+1 < classLength) {
|
|
// println("[a-z] case");
|
|
rangeStart = charValue(prevChar);
|
|
rangeEnd = charValue(self.value[i+1]);
|
|
// print("Range Start: ", rangeStart, " | ");
|
|
// print("Range End: ", rangeEnd, "\n");
|
|
if (charValue(stream.peek()) >= rangeStart
|
|
&& charValue(stream.peek()) <= rangeEnd
|
|
) {
|
|
success = 1;
|
|
}
|
|
prevChar = nil;
|
|
i = i + 1;
|
|
|
|
// [a-] case
|
|
} else {
|
|
// println("[a-] case");
|
|
if (stream.peek() == ord('-')) {
|
|
success = 1;
|
|
}
|
|
}
|
|
|
|
// [ab] case
|
|
} else if (prevChar != nil && self.value[i] != ord('-')) {
|
|
// println("[ab] case");
|
|
prevChar = self.value[i];
|
|
if (stream.peek() == self.value[i]) {
|
|
success = 1;
|
|
}
|
|
}
|
|
// print("prevChar: ", prevChar, "\n");
|
|
i = i + 1;
|
|
}
|
|
|
|
if (success == 1) {
|
|
stream.inc();
|
|
}
|
|
|
|
success;
|
|
}
|
|
|
|
// Dot
|
|
|
|
Dot = Object.subtype(#Dot);
|
|
|
|
Dot.match(stream, context, actions) {
|
|
if (!stream.atEnd()) {
|
|
stream.inc();
|
|
1;
|
|
} else {
|
|
0;
|
|
}
|
|
}
|
|
|
|
// Begin
|
|
|
|
Begin = Object.subtype(#Begin);
|
|
|
|
Begin.match(stream, context, actions) {
|
|
stream.setLastBegin();
|
|
1;
|
|
}
|
|
|
|
// End
|
|
|
|
End = Object.subtype(#End);
|
|
|
|
End.match(stream, context, actions) {
|
|
context.input = stream.string[stream.lastBegin..stream.position];
|
|
1;
|
|
}
|
|
|
|
// Optional (? postfix operator)
|
|
|
|
Optional = Object.subtype(#Optional);
|
|
|
|
Optional.match(stream, context, actions) {
|
|
self.expression.match(stream, context, actions);
|
|
1;
|
|
}
|
|
|
|
// Star
|
|
|
|
Star = Object.subtype(#Star);
|
|
|
|
Star.match(stream, context, actions) {
|
|
while (self.expression.match(stream, context, actions) == 1) {}
|
|
1;
|
|
}
|
|
|
|
// Plus
|
|
|
|
Plus = Object.subtype(#Plus);
|
|
|
|
Plus.match(stream, context, actions) {
|
|
if (self.expression.match(stream, context, actions) == 1) {
|
|
while (self.expression.match(stream, context, actions) == 1) {}
|
|
1;
|
|
} else {
|
|
0;
|
|
}
|
|
}
|
|
|
|
// And
|
|
|
|
And = Object.subtype(#And);
|
|
|
|
And.match(stream, context, actions) {
|
|
position = stream.position;
|
|
|
|
if (self.expression.match(stream, context, actions) == 1) {
|
|
stream.position = position;
|
|
1;
|
|
} else {
|
|
0;
|
|
}
|
|
}
|
|
|
|
// Not
|
|
|
|
Not = Object.subtype(#Not);
|
|
|
|
Not.match(stream, context, actions) {
|
|
position = stream.position;
|
|
|
|
if (self.expression.match(stream, context, actions) == 1) {
|
|
stream.position = position;
|
|
0;
|
|
} else {
|
|
1;
|
|
}
|
|
}
|
|
|
|
// Sequence
|
|
|
|
Sequence = Object.subtype(#Sequence);
|
|
|
|
Sequence.match(stream, context, actions) {
|
|
i = 0;
|
|
match = 1;
|
|
while (i < self.length() && match == 1) {
|
|
match = self[i].match(stream, context, actions);
|
|
i = i + 1;
|
|
}
|
|
match;
|
|
}
|
|
|
|
// Alternation
|
|
|
|
Alternation = Object.subtype(#Alternation);
|
|
|
|
Alternation.match(stream, context, actions) {
|
|
i = 0;
|
|
success = 0;
|
|
while (i < self.length() && success == 0) {
|
|
initialActionCount = actions.length();
|
|
startingPosition = stream.position;
|
|
success = self[i].match(stream, context, actions);
|
|
|
|
if (success == 0) {
|
|
while (actions.length() > initialActionCount) {
|
|
actions.pop();
|
|
}
|
|
stream.position = startingPosition;
|
|
}
|
|
|
|
i = i + 1;
|
|
print(i, " | ", success, " | ", self.length(), "\n");
|
|
}
|
|
success;
|
|
}
|
|
|
|
// Action
|
|
|
|
Action = Object.subtype(#Action);
|
|
|
|
Action.match(stream, context, actions) {
|
|
actions.push(self);
|
|
self.context = context;
|
|
1;
|
|
}
|
|
|
|
Action.execute() {
|
|
|
|
// Declare all variables that a value is set to in the context
|
|
for (statement in self.parseTree.body) {
|
|
if (statement.__name__ == "SetVar") {
|
|
self.context.declareVariable(statement.name);
|
|
}
|
|
}
|
|
|
|
// Evaluate the parse tree and return to outer context if needed
|
|
returnValue = eval(self.parseTree, env: self.context.variables);
|
|
if (self.context.outerContext != nil) {
|
|
self.context.outerContext.variables[self.context.returnValueName] = returnValue;
|
|
}
|
|
|
|
}
|
|
|
|
// Assignment
|
|
|
|
Assignment = Object.subtype(#Assignment);
|
|
|
|
Assignment.match(stream, context, actions) {
|
|
context.declareVariable(self.variableName);
|
|
innerContext = Context.new(outerContext: context, returnValueName: self.variableName).init();
|
|
self.rule.match(stream, innerContext, actions);
|
|
}
|
|
|
|
// ----- Grammar of Grammars -----
|
|
|
|
// Whitespace
|
|
|
|
// - = ( space | comment )*
|
|
// space = ' ' | '\t' | end-of-line
|
|
// comment = '#' ( !end-of-line . )* end-of-line
|
|
// end-of-line = '\r\n' | '\n' | '\r'
|
|
// end-of-file = !.
|
|
|
|
end_of_file = Not.new(expression: Dot.new());
|
|
end_of_line = Alternation.new()
|
|
.push(StringLiteral.new(string: "\r\n"))
|
|
.push(StringLiteral.new(string: "\n"))
|
|
.push(StringLiteral.new(string: "\r"));
|
|
|
|
comment = Sequence.new()
|
|
.push(StringLiteral.new(string: "#"))
|
|
.push(Star.new(expression:
|
|
Sequence.new()
|
|
.push(Not.new(expression: end_of_line))
|
|
.push(Dot.new())
|
|
))
|
|
.push(end_of_line);
|
|
|
|
space = Alternation.new()
|
|
.push(StringLiteral.new(string: " "))
|
|
.push(StringLiteral.new(string: "\t"))
|
|
.push(end_of_line);
|
|
|
|
ws = Star.new(expression: Alternation.new()
|
|
.push(space)
|
|
.push(comment)
|
|
);
|
|
|
|
|
|
// Literal Terminators
|
|
|
|
Terminators = [];
|
|
|
|
// BAR = '|' -
|
|
// NOT = '!' -
|
|
// QUERY = '?' -
|
|
// BEGIN = '<' -
|
|
// END = '>' -
|
|
// TILDE = '~' -
|
|
// RPERCENT = '%}' -
|
|
|
|
Terminators.bar = Sequence.new().push(StringLiteral.new(string: "|")).push(ws);
|
|
Terminators.not = Sequence.new().push(StringLiteral.new(string: "!")).push(ws);
|
|
Terminators.query = Sequence.new().push(StringLiteral.new(string: "?")).push(ws);
|
|
Terminators.begin = Sequence.new().push(StringLiteral.new(string: "<")).push(ws);
|
|
Terminators.end = Sequence.new().push(StringLiteral.new(string: ">")).push(ws);
|
|
Terminators.tilde = Sequence.new().push(StringLiteral.new(string: "~")).push(ws);
|
|
Terminators.rpercent = Sequence.new().push(StringLiteral.new(string: "%}")).push(ws);
|
|
|
|
// DIGIT = [0-9]
|
|
// LETTER = [A-Za-z_]
|
|
// ALNUM = LETTER | DIGIT
|
|
// NIL = "nil" !ALNUM -
|
|
|
|
digit = CharacterClass.new(value: "0-9");
|
|
letter = CharacterClass.new(value: "A-Za-z_");
|
|
alnum = Alternation.new().push(letter).push(digit);
|
|
nil = Sequence.new().push(StringLiteral.new(string: "nil")).push(Not.new(expression: alnum)).push(ws);
|
|
|
|
// SEMI = ";" -
|
|
// COMMA = "," -
|
|
// COLON = ":" -
|
|
// LPAREN = "(" -
|
|
// RPAREN = ")" -
|
|
// LBRAK = "[" -
|
|
// RBRAK = "]" -
|
|
// LBRACE = "{" -
|
|
// RBRACE = "}" -
|
|
// ASSIGN = "=" ![=] -
|
|
// AND = "&" ![&=] -
|
|
// PLUS = "+" ![+=] -
|
|
// STAR = "*" ![=] -
|
|
// DOT = "." ![.] -
|
|
|
|
Terminators.semi = Sequence.new().push(StringLiteral.new(string: ";")).push(ws);
|
|
Terminators.comma = Sequence.new().push(StringLiteral.new(string: ",")).push(ws);
|
|
Terminators.colon = Sequence.new().push(StringLiteral.new(string: ":")).push(ws);
|
|
Terminators.lparen = Sequence.new().push(StringLiteral.new(string: "(")).push(ws);
|
|
Terminators.rparen = Sequence.new().push(StringLiteral.new(string: ")")).push(ws);
|
|
Terminators.lbrak = Sequence.new().push(StringLiteral.new(string: "[")).push(ws);
|
|
Terminators.rbrak = Sequence.new().push(StringLiteral.new(string: "]")).push(ws);
|
|
Terminators.lbrace = Sequence.new().push(StringLiteral.new(string: "{")).push(ws);
|
|
Terminators.rbrace = Sequence.new().push(StringLiteral.new(string: "}")).push(ws);
|
|
|
|
Terminators.assign = Sequence.new()
|
|
.push(StringLiteral.new(string: "="))
|
|
.push(Not.new(expression: CharacterClass.new(value: "=")))
|
|
.push(ws);
|
|
Terminators.and = Sequence.new()
|
|
.push(StringLiteral.new(string: "&"))
|
|
.push(Not.new(expression: CharacterClass.new(value: "&=")))
|
|
.push(ws);
|
|
Terminators.plus = Sequence.new()
|
|
.push(StringLiteral.new(string: "+"))
|
|
.push(Not.new(expression: CharacterClass.new(value: "+=")))
|
|
.push(ws);
|
|
Terminators.star = Sequence.new()
|
|
.push(StringLiteral.new(string: "*"))
|
|
.push(Not.new(expression: CharacterClass.new(value: "=")))
|
|
.push(ws);
|
|
Terminators.dot = Sequence.new()
|
|
.push(StringLiteral.new(string: "."))
|
|
.push(Not.new(expression: CharacterClass.new(value: ".")))
|
|
.push(ws);
|
|
|
|
// ----- Main -----
|
|
|
|
// stream = newStream(readfile("input.txt"));
|
|
stream = newStream("baaababbaabaaa");
|
|
context = Context.new(outerContext: nil).init();
|
|
actions = [];
|
|
|
|
// s = StringLiteral.new(string: "ab");
|
|
// print("Success : ", s.match(stream), "\n");
|
|
|
|
// c = CharacterClass.new(value: " \n\t");
|
|
// print("Parsing Character: ", stream.peek(), " | CharacterClass [", c.value,"] : ", c.match(stream), "\n");
|
|
|
|
// d = Dot.new();
|
|
// print("Parsing Character: ", stream.peek(), " | Dot : ", d.match(stream, context), "\n");
|
|
|
|
println("\n--- Action Test ---\n");
|
|
|
|
actionParseTree = `{
|
|
innerVar = 151;
|
|
};
|
|
|
|
act = Action.new(parseTree: actionParseTree);
|
|
|
|
assign = Assignment.new(variableName: #outerVar, rule: act);
|
|
assign.match(stream, context, actions);
|
|
|
|
actionParseTree2 = `{
|
|
x = 69;
|
|
};
|
|
|
|
act2 = Action.new(parseTree: actionParseTree2);
|
|
act2.match(stream, context, actions);
|
|
|
|
for (action in actions) {
|
|
action.execute();
|
|
}
|
|
|
|
print("global variable named innerVar => ", innerVar, " should be nil\n");
|
|
print("global variable named x => ", x, " should be nil\n");
|
|
|
|
print(context, "\n", full: 1);
|
|
|
|
|
|
|
|
|
|
print("\nMatching : ", Terminators.bar.match(stream, context, actions), "\n");
|
|
println(stream);
|
|
|
|
print("\nMatching : ", Star.new(expression: Alternation.new()
|
|
.push(StringLiteral.new(string: "a"))
|
|
.push(StringLiteral.new(string: "b"))
|
|
).match(stream, context, actions), "\n");
|
|
println(stream);
|
|
|
|
// print("\nMatching : ", ws.match(stream, context, actions), "\n");
|
|
// println(stream);
|
|
|
|
// c = CharacterClass.new(value: "a");
|
|
// u = Not.new(expression: c);
|
|
// println(u.match(stream, context));
|
|
// println(stream);
|
|
//
|
|
// println();
|
|
// println();
|
|
// println();
|
|
//
|
|
// gensokyo = [buses: 0, inhabitants: 1337];
|
|
//
|
|
// reimu = [location: gensokyo];
|
|
// marisa = [location: gensokyo];
|
|
//
|
|
// println(marisa.location);
|
|
// reimu.location.inhabitants = 42;
|
|
// println(marisa.location);
|
|
|