#!/usr/bin/env python3 # SPDX-License-Identifier: MIT # Copyright 2021 Jookia <contact@jookia.org> def tokenizer(code): tokens = [] token = "" text = "" mode = "normal" # normal/note/text for symbol in code: if symbol == " " or symbol == "\t" or symbol == "\n": lowered = token.lower() if lowered == "": pass elif lowered == "beginnote": mode = "note" elif lowered == "endnote": mode = "normal" elif lowered == "begintext": mode = "text" elif lowered == "endtext": tokens.append(("text", text[1:-8])) mode = "normal" text = "" elif token != "": if mode == "normal": keywords = ["newlang", "done", "set", "to", "endset", "if", "then", "else", "endif"] if lowered in keywords: type = "keyword" else: type = "symbol" tokens.append((type, lowered)) token = "" else: token += symbol if mode == "text": text += symbol return tokens parser_tokens = None parser_pos = 0 def parser_reset(tokens): global parser_pos global parser_tokens parser_tokens = tokens parser_pos = 0 def parser_eof(): global parser_pos global parser_tokens return parser_pos >= len(parser_tokens) def parser_next(): global parser_pos global parser_tokens if parser_eof(): print("Reached end of file early") sys.exit(1) (type, value) = parser_tokens[parser_pos] parser_pos += 1 #print("Read %s %s" % (type, value)) return (type, value) def parser_peek(): global parser_pos global parser_tokens (type, value) = parser_tokens[parser_pos] #print("Peeked %s %s" % (type, value)) return (type, value) def parser_skip(): global parser_pos global parser_tokens parser_pos += 1 def parse_version(): (type, value) = parser_next() if type != "keyword" or value != "newlang": print("Expected NewLang keyword") return None (type, value) = parser_next() print("Parsed language version %s" % (value)) return value def parse_subject(): (type, value) = parser_next() if type != "symbol": print("Expected symbol, got %s" % (type)) return None return value def parse_verb(): (type, value) = parser_next() if type != "symbol": print("Expected symbol, got %s" % (type)) return None return value def parse_arguments(terminator): args = [] while True: (type, value) = parser_next() if type == "keyword": if value == terminator: return args else: print("Unexpected keyword %s" % (value)) return None elif type == "text" or type == "symbol": args.append((type, value)) else: print("Unexpected type %s" % (type)) return None def parse_statement(terminator): subject = parse_subject() if not subject: print("While parsing subject") return None verb = parse_verb() if not verb: print("While parsing verb") return None arguments = parse_arguments(terminator) if arguments is None: print("While parsing arguments") return None print("Parsed statement: subject %s verb %s args %s" % (subject, verb, arguments)) return ('Statement', subject, verb, arguments) def parse_set(): subject = parse_subject() if not subject: print("While parsing subject") return None (type, value) = parser_next() if type != "keyword" or value != "to": print("Expect to, got %s %s" % (type, value)) return None print("Parsing set value...") ast = parse_statement("endset") if not ast: print("While parsing statement") return None print("Parsed set for %s" % (subject)) return ('Set', subject, ast) def parse_if(): print("Parsing if test condition...") test = parse_statement("then") if not test: print("While parsing test condition") return None print("Parsing if success statement...") success = parse_statement("else") if not success: print("While parsing success statement") return None print("Parsing if failure statement...") failure = parse_statement("endif") if not failure: print("While parsing failure statement") return None print("Parsed conditional") return ('If', test, success, failure) def parse_directive(): (type, value) = parser_peek() if type != "keyword" and type != "symbol": print("Expected keyword or symbol here, got %s" % (type)) return None if type == "keyword": parser_skip() if value == "set": ast = parse_set() if not ast: print("While parsing set directive") return None return ast elif value == "if": ast = parse_if() if not ast: print("While parsing set directive") return None return ast else: print("Unexpected keyword %s" % (value)) return None else: ast = parse_statement("done") if not ast: print("While parsing statement") return None return ast def parse_file(): print("Parsing file...") ast = [] version = parse_version() if not version: print("While parsing version identifier at start of file") return None if version != "0": print("Invalid version identifier %s" % (version)) return None while not parser_eof(): directive = parse_directive() if directive == None: print("While parsing directive in file") return None else: ast.append(directive) print("Parsed file") return ast def main(args): if len(args) != 2: print("Usage: main.py FILENAME") return 1 filename = args[1] code = open(filename).read() if code[0:2] == '#!': next_line = code.find('\n') + 1 code = code[next_line:] tokens = tokenizer(code) parser_reset(tokens) ast = parse_file() if not ast: return 1 return 0 if __name__ == "__main__": import sys sys.exit(main(sys.argv))