1,153 → 1,74 |
# Copyright Magomed Kostoev |
# Published under MIT license |
|
class Rule: |
pass |
block_beginner2finisher = { |
"(": ")", |
"{": "}", |
"\"": "\"", |
} |
|
def get_config(config_name): |
if config_name == "KPACK_CMD": |
# Just never pack the file for now |
return "" |
def get_to(src, ptr, c, backwards = False): |
while src[ptr] != c: |
if not backwards: |
ptr += 1 |
else: |
print(f"Unknown config name: {config_name}") |
exit(-1) |
ptr -= 1 |
return ptr |
|
def skip_whitespaces(src, ptr): |
while len(src) > ptr and src[ptr] == " ": |
# Assuming we are at beginning of some block (string, |
# parenthesed expression, {}-like thing), get to end |
# of the block (also handles the blocks openned in the |
# way to the block ending character. So it's not just |
# stupid searching for block closing character. |
def get_to_block_finisher(src, ptr): |
assert(src[ptr] in block_beginner2finisher) |
|
block_beginner = src[ptr] |
ptr += 1 |
while src[ptr] != block_beginner2finisher[block_beginner]: |
# If any block starts here - get to its end and then continue |
if src[ptr] in block_beginner2finisher: |
ptr = get_to_block_finisher(src, ptr) |
ptr += 1 |
return ptr |
|
# Returns True if @src has @string starting from @ptr index |
def match_string(src, ptr, string): |
if len(src) <= ptr + len(string): |
return False |
for i in range(len(string)): |
if src[ptr + i] != string[i]: |
return False |
return True |
def get_strnig(src, ptr): |
# Strings starts with "\"" |
assert(src[ptr] == "\"") |
|
def parse_tup_getconfig(src, ptr): |
# Skip get straight to the argument |
ptr += len("tup.getconfig(") |
ptr = skip_whitespaces(src, ptr) |
if src[ptr] != "\"": |
print("Expected \"config name\" as tup.getconfig parameter") |
exit() |
(config_name, ptr) = parse_string(src, ptr) |
ptr = skip_whitespaces(src, ptr) |
# Skip closing parenthese of the tup.getconfig call |
assert(src[ptr] == ")") |
result = "" |
# Skip first "\"" of the string |
ptr += 1 |
return (get_config(config_name), ptr) |
|
def parse_string(src, ptr): |
ptr += 1 |
string = "" |
while src[ptr] != "\"": |
string += src[ptr] |
result += src[ptr] |
ptr += 1 |
# Skip the closing "\"" |
ptr += 1 |
ptr = skip_whitespaces(src, ptr) |
# Check if we have concatination here |
if match_string(src, ptr, ".."): |
# Skip the ".." |
ptr += 2 |
# The expression parsing should result in a string |
(string_to_add, ptr) = parse_expression(src, ptr) |
# Concat our string to the resulting string |
string += string_to_add |
return (string, ptr) |
|
def parse_expression(src, ptr): |
ptr = skip_whitespaces(src, ptr) |
result = "WAT?!" |
if src[ptr] == "\"": |
(result, ptr) = parse_string(src, ptr) |
elif match_string(src, ptr, "tup.getconfig("): |
(result, ptr) = parse_tup_getconfig(src, ptr) |
else: |
print(f"Can't handle anything starting with '{src[ptr]}'") |
exit(-1) |
ptr = skip_whitespaces(src, ptr) |
return (result, ptr) |
|
def expect_comma(src, ptr): |
comma_skept = False |
ptr = skip_whitespaces(src, ptr) |
if src[ptr] == ",": |
ptr += 1 |
return (True, ptr) |
else: |
return (False, ptr) |
|
def parse_arguments(src, ptr): |
result = [] |
# Parse first argument |
(argument, ptr) = parse_expression(src, ptr) |
result.append(argument) |
(comma_encoutered, ptr) = expect_comma(src, ptr) |
# Parse the second argument if it's there |
if comma_encoutered: |
(argument, ptr) = parse_expression(src, ptr) |
result.append(argument) |
(comma_encoutered, ptr) = expect_comma(src, ptr) |
# Parse third argument if it's there |
if comma_encoutered: |
(argument, ptr) = parse_expression(src, ptr) |
result.append(argument) |
return result |
|
def parse_rule(src, ptr): |
def parse_rule_output(src, ptr): |
# Get straight to the first argument |
ptr += len("tup.rule(") |
# Parse the arguments |
args = parse_arguments(src, ptr) |
# Build the rule object |
result = Rule() |
if len(args) == 3: |
result.input = args[0] |
result.command = args[1] |
result.output = args[2] |
# Replace %f with input file in rule's command |
if type(result.input == str): |
result.command = result.command.replace("%f", result.input) |
else: |
print("Command building with non-string tup.rule's first argument" |
+ " isn't implemented") |
exit() |
# Replace %o with output file in rule's command |
if type(result.output == str): |
result.command = result.command.replace("%o", result.output) |
else: |
print("Command building with non-string tup.rule's first argument" |
+ " isn't implemented") |
exit() |
elif len(args) == 2: |
result.input = [] |
result.command = args[0] |
result.output = args[1] |
else: |
print(f"tup.rule can only take 2 or 3 arguments, not {len(args)}") |
exit(-1) |
# Unify the API - return arrays as input and output |
if type(result.input) == str: |
result.input = [ result.input ] |
else: |
assert(type(result.input) == list) |
if type(result.output) == str: |
result.output = [ result.output ] |
else: |
assert(type(result.output) == list) |
return result |
ptr += len("tup.rule") |
# Get to parenthese |
ptr = get_to(src, ptr, "(") |
# Get to the closing parenthese |
ptr = get_to_block_finisher(src, ptr) |
# We are at closing parenthese of argument list |
# And the last argument is always output file |
# Let's get to closing "\"" of the output file name |
ptr = get_to(src, ptr, "\"", backwards = True) |
# Get into the string |
ptr -= 1 |
# Then get to the beginning of the string |
ptr = get_to(src, ptr, "\"", backwards = True) |
# Now we can read the string |
return get_strnig(src, ptr) |
|
def parse(file_name): |
rules = [] |
def parse_tupfile_outputs(file_name): |
outputs = [] |
with open(file_name) as f: |
tupfile = f.read() |
rule_begin_index = tupfile.find("tup.rule(") |
while (rule_begin_index != -1): |
rules.append(parse_rule(tupfile, rule_begin_index)) |
outputs.append(parse_rule_output(tupfile, rule_begin_index)) |
# Find the next tup.rule call |
rule_begin_index = tupfile.find("tup.rule(", rule_begin_index + len("tup.rule(")) |
return rules |
return outputs |