# *Acc!*, [Cracc'd](https://codegolf.stackexchange.com/a/62446/16766) by ppperry This language has one looping structure, basic integer math, character I/O, and an accumulator (thus the name). *Just one* accumulator. Thus, the name. ### Statements Commands are parsed line by line. There are three types of command: 1. `Count <var> while <cond>` Counts `<var>` up from 0 as long as `<cond>` is nonzero, equivalent to C-style `for(<var>=0; <cond>; <var>++)`. The loop counter can be any single lowercase letter. The condition can be any expression, not necessarily involving the loop variable. The loop halts when the condition's value becomes 0. Loops require K&R-style curly braces (in particular, [the Stroustrup variant](https://en.wikipedia.org/wiki/Indent_style#Variant:_Stroustrup)): Count i while i-5 { ... } 2. `Write <charcode>` Outputs a single character with the given ASCII/Unicode value to stdout. The charcode can be any expression. 3. Expression Any expression standing by itself is evaluated and assigned back to the accumulator (which is accessible as `_`). Thus, e.g., `3` is a statement that sets the accumulator to 3; `_ + 1` increments the accumulator; and `_ * N` reads a character and multiplies the accumulator by its charcode. **Note:** the accumulator is the only variable that can be directly assigned to; loop variables and `N` can be used in calculations but not modified. The accumulator is initially 0. ### Expressions An expression can include integer literals, loop variables (`a-z`), `_` for the accumulator, and the special value `N`, which reads a character and evaluates to its charcode each time it is used. **Note:** this means you only get one shot to read each character; the next time you use `N`, you'll be reading the next one. Operators are: - `+`, addition - `-`, subtraction; unary negation - `*`, multiplication - `/`, integer division - `%`, modulo - `^`, exponentiation Parentheses can be used to enforce precedence of operations. Any other character in an expression is a syntax error. ### Whitespace and comments Leading and trailing whitespace and empty lines are ignored. Whitespace in loop headers must be exactly as shown, with a single space between the loop header and opening curly brace as well. Whitespace inside expressions is optional. `#` begins a single-line comment. ### Input/Output *Acc!* expects a single line of characters as input. Each input character can be retrieved in sequence and its charcode processed using `N`. Trying to read past the last character of the line causes an error. A character can be output by passing its charcode to the `Write` statement. ### Interpreter The interpreter (written in Python 3) translates *Acc!* code into Python and `exec`s it. import re, sys def main(): if len(sys.argv) != 2: print("Please supply a filename on the command line.", file=sys.stderr) return codeFile = sys.argv[1] with open(codeFile) as f: code = f.readlines() code = translate(code) exec(code, {"inputStream": (ord(char) for char in input())}) def translate(accCode): indent = 0 loopVars = [] pyCode = ["_ = 0"] for lineNum, line in enumerate(accCode): if "#" in line: # Strip comments line = line[:line.index("#")] line = line.strip() if not line: continue lineNum += 1 if line == "}": if indent: loopVar = loopVars.pop() if loopVar is not None: pyCode.append(" "*indent + loopVar + " += 1") indent -= 1 else: raise SyntaxError("Line %d: unmatched }" % lineNum) else: m = re.fullmatch(r"Count ([a-z]) while (.+) \{", line) if m: expression = validateExpression(m.group(2)) if expression: loopVar = m.group(1) pyCode.append(" "*indent + loopVar + " = 0") pyCode.append(" "*indent + "while " + expression + ":") indent += 1 loopVars.append(loopVar) else: raise SyntaxError("Line %d: invalid expression " % lineNum + m.group(2)) else: m = re.fullmatch(r"Write (.+)", line) if m: expression = validateExpression(m.group(1)) if expression: pyCode.append(" "*indent + "print(chr(%s), end='')" % expression) else: raise SyntaxError("Line %d: invalid expression " % lineNum + m.group(1)) else: expression = validateExpression(line) if expression: pyCode.append(" "*indent + "_ = " + expression) else: raise SyntaxError("Line %d: invalid statement " % lineNum + line) return "\n".join(pyCode) def validateExpression(expr): "Translates expr to Python expression or returns None if invalid." expr = expr.strip() if re.search(r"[^ 0-9a-z_N()*/%^+-]", expr): # Expression contains invalid characters return None elif re.search(r"[a-zN_]\w+", expr): # Expression contains multiple letters or underscores in a row return None else: # Not going to check validity of all identifiers or nesting of parens-- # let the Python code throw an error if problems arise there # Replace short operators with their Python versions expr = expr.replace("^", "**") expr = expr.replace("/", "//") # Replace N with a call to get the next input character expr = expr.replace("N", "inputStream.send(None)") return expr if __name__ == "__main__": main()