Add files via upload
This commit is contained in:
parent
c11f19c640
commit
2714d19d9e
@ -8,20 +8,18 @@
|
||||
"\n",
|
||||
"# BASIC Interpreter\n",
|
||||
"\n",
|
||||
"[Years ago](http://norvig.com/lispy.html), I showed how to write an Interpreter for a dialect of Lisp. Some readers appreciated it, and some asked about an interpreter for a language that isn't just a bunch of parentheses. In 2014 I saw a [celebration](http://time.com/69316/basic/) of the 50th anniversary of the 1964 [Dartmouth BASIC](http://web.archive.org/web/20120716185629/http://www.bitsavers.org/pdf/dartmouth/BASIC_Oct64.pdf) interpreter, and thought that I could show how to implement such an interpreter. I never quite finished in 2014, but in 2017 I rediscovered my unfinished file and completed it. For those of you unfamiliar with BASIC, here is a sample program:"
|
||||
"[Years ago](http://norvig.com/lispy.html), I showed how to write an Interpreter for a dialect of Lisp. Some readers appreciated it, and some asked about an interpreter for a language that isn't just a bunch of parentheses. In 2014 I saw a [celebration](http://time.com/69316/basic/) of the 50th anniversary of the 1964 [Dartmouth BASIC](http://web.archive.org/web/20120716185629/http://www.bitsavers.org/pdf/dartmouth/BASIC_Oct64.pdf) interpreter, and thought that I could show how to implement such an interpreter. I never quite finished in 2014, but now it is 2017, I rediscovered this unfinished file, and completed it. For those of you unfamiliar with BASIC, here is a sample program:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"program = '''\n",
|
||||
"10 REM POWER TABLE\n",
|
||||
"11 DATA 8, 4\n",
|
||||
"11 DATA 8, 4\n",
|
||||
"15 READ N0, P0\n",
|
||||
"20 PRINT \"N\",\n",
|
||||
"25 FOR P = 2 to P0\n",
|
||||
@ -47,8 +45,9 @@
|
||||
"source": [
|
||||
"Of course I don't have to build everything from scratch in assembly language, and I don't have to worry about every byte of storage, like [Kemeny](http://www.dartmouth.edu/basicfifty/basic.html), [Gates](http://www.pagetable.com/?p=774), and [Woz](http://www.retrothing.com/2008/07/restoring-wozs.html) did, so my job is much easier. The interpreter consists of three phases: \n",
|
||||
"* **Tokenization**: breaking a text into a list of tokens, for example: `\"10 READ N\"` becomes `['10', 'READ', 'N']`.\n",
|
||||
"* **Parsing**: building an executable representation from the tokens, so this statement becomes: `Stmt(num=10, typ='READ', args=['N'])`.\n",
|
||||
"* **Execution**: follow the flow of the program and do what each statement says; in this case an assignment: `variables['N'] = data.popleft()`.\n",
|
||||
"* **Parsing**: building a representation from the tokens: `Stmt(num=10, typ='READ', args=['N'])`.\n",
|
||||
"* **Execution**: follow the flow of the program and do what each statement says; in this case the `READ` statement\n",
|
||||
"has the effect of an assignment: `variables['N'] = data.popleft()`.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@ -60,9 +59,7 @@
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import re \n",
|
||||
@ -73,7 +70,7 @@
|
||||
" LET|READ|DATA|PRINT|GOTO|IF|FOR|NEXT|END|STOP | # keywords\n",
|
||||
" DEF|GOSUB|RETURN|DIM|REM|TO|THEN|STEP | # more keywords\n",
|
||||
" [A-Z]\\d? | # variable names (letter optionally followed by a digit)\n",
|
||||
" \" .*? \" | # labels (strings in double quotes)\n",
|
||||
" \".*? \" | # labels (strings in double quotes)\n",
|
||||
" <>|>=|<= | # multi-character relational operators\n",
|
||||
" \\S # any non-space single character ''', \n",
|
||||
" re.VERBOSE).findall"
|
||||
@ -90,9 +87,7 @@
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
@ -112,9 +107,7 @@
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
@ -133,9 +126,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"That looks good. Note that my tokens are just strings; it will be the parser's job, not the tokenizer's, to recognize that `'2'` is a number and `'X'` is the name of a variable. (In some interpreters, the tokenizer makes distinctions like these.)\n",
|
||||
"\n",
|
||||
@ -151,9 +142,7 @@
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
@ -180,15 +169,13 @@
|
||||
"* `peek()`: returns the next token in `tokens` (without changing `tokens`), or `None` if there are no more tokens.\n",
|
||||
"* `pop()`: removes and returns the next token. \n",
|
||||
"* `pop(`*string*`)`: removes and returns the next token if it is equal to the string; else return `None` and leave `tokens` unchanged.\n",
|
||||
"* `pop(`*predicate*`)`: removes and returns the next token if *predicate*(*token*) is true; else return `None` and leave `tokens` unchanged."
|
||||
"* `pop(`*predicate*`)`: remove and return the next token if *predicate*(*token*) is true; else return `None`, leave `tokens` alone."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"tokens = [] # Global variable to hold a list of tokens\n",
|
||||
@ -202,9 +189,10 @@
|
||||
" return (tokens[0] if tokens else None)\n",
|
||||
"\n",
|
||||
"def pop(constraint=None):\n",
|
||||
" \"\"\"Remove and return the first token in `tokens`, or return None if first token fails a constraint.\n",
|
||||
" `constraint` can be None, a literal string (e.g. pop('=')), or a predicate (e.g. pop(is_varname)).\"\"\"\n",
|
||||
" if constraint is None or (peek() == constraint) or (callable(constraint) and constraint(peek())):\n",
|
||||
" \"\"\"Remove and return the first token in `tokens`, or return None if token fails constraint.\n",
|
||||
" constraint can be None, a literal (e.g. pop('=')), or a predicate (e.g. pop(is_varname)).\"\"\"\n",
|
||||
" top = peek()\n",
|
||||
" if constraint is None or (top == constraint) or (callable(constraint) and constraint(top)):\n",
|
||||
" return tokens.pop(0)\n",
|
||||
" \n",
|
||||
"def remove_spaces(line): \n",
|
||||
@ -228,9 +216,7 @@
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
@ -255,11 +241,12 @@
|
||||
" ['100', 'IF', 'X1', '+', '123.4', '+', 'E1', '-', '12.3E4', '<>', \n",
|
||||
" '1.2E-34', '*', '-', '12E34', '+', '1', '+', '\"HI\"', 'THEN', '99'])\n",
|
||||
" assert remove_spaces('10 GO TO 99') == '10GOTO99'\n",
|
||||
" assert remove_spaces('100 PRINT \"HELLO WORLD\", SIN(X) ^ 2') == '100PRINT\"HELLO WORLD\",SIN(X)^2'\n",
|
||||
" assert (remove_spaces('100 PRINT \"HELLO WORLD\", SIN(X) ^ 2')\n",
|
||||
" == '100PRINT\"HELLO WORLD\",SIN(X)^2')\n",
|
||||
" assert lines('one line') == ['one line']\n",
|
||||
" assert lines(program) == [\n",
|
||||
" '10 REM POWER TABLE',\n",
|
||||
" '11 DATA 8, 4',\n",
|
||||
" '11 DATA 8, 4',\n",
|
||||
" '15 READ N0, P0',\n",
|
||||
" '20 PRINT \"N\",',\n",
|
||||
" '25 FOR P = 2 to P0',\n",
|
||||
@ -313,6 +300,43 @@
|
||||
"test_tokenizer()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"['10 REM POWER TABLE',\n",
|
||||
" '11 DATA 8, 4',\n",
|
||||
" '15 READ N0, P0',\n",
|
||||
" '20 PRINT \"N\",',\n",
|
||||
" '25 FOR P = 2 to P0',\n",
|
||||
" '30 PRINT \"N ^\" P,',\n",
|
||||
" '35 NEXT P',\n",
|
||||
" '40 PRINT \"SUM\"',\n",
|
||||
" '45 LET S = 0',\n",
|
||||
" '50 FOR N = 2 TO N0',\n",
|
||||
" '55 PRINT N,',\n",
|
||||
" '60 FOR P = 2 TO P0',\n",
|
||||
" '65 LET S = S + N ^ P',\n",
|
||||
" '70 PRINT N ^ P,',\n",
|
||||
" '75 NEXT P',\n",
|
||||
" '80 PRINT S',\n",
|
||||
" '85 NEXT N',\n",
|
||||
" '99 END']"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"lines(program)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
@ -347,10 +371,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def Grammar(): \n",
|
||||
@ -408,7 +430,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"execution_count": 10,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
@ -419,11 +441,11 @@
|
||||
" num = linenumber()\n",
|
||||
" typ = pop(is_stmt_type) or fail('unknown statement type')\n",
|
||||
" args = []\n",
|
||||
" for c in grammar[typ]: # For each constituent of rule, call if callable or match if literal string\n",
|
||||
" if callable(c):\n",
|
||||
" args.append(c())\n",
|
||||
" for p in grammar[typ]: # For each part of rule, call if callable or match if literal string\n",
|
||||
" if callable(p):\n",
|
||||
" args.append(p())\n",
|
||||
" else:\n",
|
||||
" pop(c) or fail('expected ' + repr(c))\n",
|
||||
" pop(p) or fail('expected ' + repr(p))\n",
|
||||
" return Stmt(num, typ, args)"
|
||||
]
|
||||
},
|
||||
@ -436,19 +458,17 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def linenumber(): return (int(pop()) if peek().isnumeric() else fail('missing line number'))\n",
|
||||
"def number(): return (-1 if pop('-') else +1) * float(pop()) # Optional minus sign before number\n",
|
||||
"def number(): return (-1 if pop('-') else +1) * float(pop()) # Optional minus sign\n",
|
||||
"def step(): return (expression() if pop('STEP') else 1) # 1 is the default step\n",
|
||||
"def relational(): return pop(is_relational) or fail('expected a relational operator')\n",
|
||||
"def varname(): return pop(is_varname) or fail('expected a variable name')\n",
|
||||
"def funcname(): return pop(is_funcname) or fail('expected a function name')\n",
|
||||
"def anycharacters(): tokens.clear() # The tokens in a REM statement don't matter, so just clear them"
|
||||
"def anycharacters(): tokens.clear() # Ignore tokens in a REM statement"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -460,18 +480,20 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"execution_count": 12,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def is_stmt_type(x): return isinstance(x, str) and x in grammar # LET, READ, ...\n",
|
||||
"def is_funcname(x): return isinstance(x, str) and len(x) == 3 and x.isalpha() # SIN, COS, FNA, FNB, ...\n",
|
||||
"def is_varname(x): return isinstance(x, str) and len(x) in (1, 2) and x[0].isalpha() # A, A1, A2, B, ...\n",
|
||||
"def is_label(x): return isinstance(x, str) and x.startswith('\"') # \"HELLO WORLD\", ...\n",
|
||||
"def is_relational(x): return isinstance(x, str) and x in ('<', '=', '>', '<=', '<>', '>=')\n",
|
||||
"def is_number(x): return isinstance(x, str) and x and x[0] in '.0123456789' # '3', '.14', ..."
|
||||
"def is_stmt_type(x): return is_str(x) and x in grammar # LET, READ, ...\n",
|
||||
"def is_funcname(x): return is_str(x) and len(x) == 3 and x.isalpha() # SIN, COS, FNA, FNB, ...\n",
|
||||
"def is_varname(x): return is_str(x) and len(x) in (1, 2) and x[0].isalpha() # A, A1, A2, B, ...\n",
|
||||
"def is_label(x): return is_str(x) and x.startswith('\"') # \"HELLO WORLD\", ...\n",
|
||||
"def is_relational(x): return is_str(x) and x in ('<', '=', '>', '<=', '<>', '>=')\n",
|
||||
"def is_number(x): return is_str(x) and x and x[0] in '.0123456789' # '3', '.14', ...\n",
|
||||
"\n",
|
||||
"def is_str(x): return isinstance(x, str)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -483,10 +505,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def variable(): \n",
|
||||
@ -509,7 +529,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"execution_count": 14,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
@ -536,7 +556,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"execution_count": 15,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
@ -556,10 +576,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
"execution_count": 16,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def parse_line(line):\n",
|
||||
@ -572,8 +590,8 @@
|
||||
" return stmt\n",
|
||||
" except SyntaxError as err:\n",
|
||||
" print(\"Error in line '{}' at '{}': {}\".format(line, ' '.join(tokens), err))\n",
|
||||
" return Stmt(0, 'REM', []) # Have to return something: a dummy statement\n",
|
||||
" \n",
|
||||
" return Stmt(0, 'REM', []) # Return dummy statement\n",
|
||||
" \n",
|
||||
"def fail(message): raise SyntaxError(message)"
|
||||
]
|
||||
},
|
||||
@ -588,7 +606,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"execution_count": 17,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
@ -596,14 +614,14 @@
|
||||
"source": [
|
||||
"from collections import namedtuple, defaultdict, deque\n",
|
||||
"\n",
|
||||
"Stmt = namedtuple('Stmt', 'num, typ, args') # Statement: '20 GOTO 99' => Stmt(20, 'GOTO', 99)\n",
|
||||
"Subscript = namedtuple('Subscript', 'var, indexes') # Subscripted reference: 'A(I)' => Subscript('A', ['I'])\n",
|
||||
"Funcall = namedtuple('Funcall', 'f, x') # Function call: 'SQR(X)' => Funcall('SQR', 'X')\n",
|
||||
"Opcall = namedtuple('Opcall', 'x, op, y') # Infix operation: 'X + 1' => Opcall('X', '+', 1)\n",
|
||||
"ForState = namedtuple('ForState', 'continu, end, step') # Data used to control a FOR loop variable\n",
|
||||
"Stmt = namedtuple('Stmt', 'num, typ, args') # '1 GOTO 9' => Stmt(1, 'GOTO', 9)\n",
|
||||
"Subscript = namedtuple('Subscript', 'var, indexes') # 'A(I)' => Subscript('A', ['I'])\n",
|
||||
"Funcall = namedtuple('Funcall', 'f, x') # 'SQR(X)' => Funcall('SQR', 'X')\n",
|
||||
"Opcall = namedtuple('Opcall', 'x, op, y') # 'X + 1' => Opcall('X', '+', 1)\n",
|
||||
"ForState = namedtuple('ForState', 'continu, end, step') # Data for FOR loop \n",
|
||||
"\n",
|
||||
"class Function(namedtuple('_', 'parm, body')):\n",
|
||||
" \"User-defined callable function; 'DEF FNC(X) = X ^ 3' => functions['FNC'] = Function('X', Opcall('X', '^', 3))\"\n",
|
||||
" \"User-defined function; 'DEF FNC(X) = X ^ 3' => Function('X', Opcall('X', '^', 3))\"\n",
|
||||
" def __call__(self, value): \n",
|
||||
" variables[self.parm] = value # Global assignment to the parameter\n",
|
||||
" return evalu(self.body)"
|
||||
@ -651,7 +669,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"execution_count": 18,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
@ -692,10 +710,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 19,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def expression(prec=1): \n",
|
||||
@ -724,9 +740,11 @@
|
||||
" else:\n",
|
||||
" return fail('unknown expression')\n",
|
||||
"\n",
|
||||
"def precedence(op): return (3 if op == '^' else 2 if op in ('*', '/') else 1 if op in ('+', '-') else 0)\n",
|
||||
"def precedence(op): \n",
|
||||
" return (3 if op == '^' else 2 if op in ('*', '/') else 1 if op in ('+', '-') else 0)\n",
|
||||
"\n",
|
||||
"def associativity(op): return (0 if op == '^' else 1)"
|
||||
"def associativity(op): \n",
|
||||
" return (0 if op == '^' else 1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -740,10 +758,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 20,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
@ -768,7 +784,7 @@
|
||||
" Stmt(num=99, typ='END', args=[])]"
|
||||
]
|
||||
},
|
||||
"execution_count": 19,
|
||||
"execution_count": 20,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@ -788,10 +804,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 21,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
@ -799,17 +813,17 @@
|
||||
"'ok'"
|
||||
]
|
||||
},
|
||||
"execution_count": 20,
|
||||
"execution_count": 21,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def test_parse(text, result, category=expression):\n",
|
||||
" \"Test that text can be parsed as a category to yield the semantic result, with no tokens left over.\"\n",
|
||||
"def test_exp(text, repr):\n",
|
||||
" \"Test that text can be parsed as an expression to yield repr, with no tokens left over.\"\n",
|
||||
" global tokens\n",
|
||||
" tokens = tokenizer(text)\n",
|
||||
" return category() == result and not tokens\n",
|
||||
" return (expression() == repr) and not tokens\n",
|
||||
" \n",
|
||||
"def test_parser():\n",
|
||||
" assert is_funcname('SIN') and is_funcname('FNZ') # Function names are three letters\n",
|
||||
@ -818,16 +832,17 @@
|
||||
" assert not is_varname('FNZ') and not is_varname('A10') and not is_varname('')\n",
|
||||
" assert is_relational('>') and is_relational('>=') and not is_relational('+')\n",
|
||||
" \n",
|
||||
" assert test_parse('A + B * X + C', Opcall(Opcall('A', '+', Opcall('B', '*', 'X')), '+', 'C'))\n",
|
||||
" assert test_parse('A + B + X + C', Opcall(Opcall(Opcall('A', '+', 'B'), '+', 'X'), '+', 'C'))\n",
|
||||
" assert test_parse('SIN(X)^2', Opcall(Funcall('SIN', 'X'), '^', 2))\n",
|
||||
" assert test_parse('10 ^ 2 ^ 3', Opcall(10, '^', Opcall(2, '^', 3))) # right associative\n",
|
||||
" assert test_parse('10 - 2 - 3', Opcall(Opcall(10, '-', 2), '-', 3)) # left associative\n",
|
||||
" assert test_parse('A(I)+M(I, J)', Opcall(Subscript(var='A', indexes=['I']), '+', \n",
|
||||
" Subscript(var='M', indexes=['I', 'J'])))\n",
|
||||
" assert test_parse('X * -1', Opcall('X', '*', Funcall('NEG', 1.0)))\n",
|
||||
" assert test_parse('X--Y--Z', Opcall(Opcall('X', '-', Funcall('NEG', 'Y')), '-', Funcall('NEG', 'Z')))\n",
|
||||
" assert test_parse('((((X))))', 'X')\n",
|
||||
" assert test_exp('A + B * X + C', Opcall(Opcall('A', '+', Opcall('B', '*', 'X')), '+', 'C'))\n",
|
||||
" assert test_exp('A + B + X + C', Opcall(Opcall(Opcall('A', '+', 'B'), '+', 'X'), '+', 'C'))\n",
|
||||
" assert test_exp('SIN(X)^2', Opcall(Funcall('SIN', 'X'), '^', 2))\n",
|
||||
" assert test_exp('10 ^ 2 ^ 3', Opcall(10, '^', Opcall(2, '^', 3))) # right associative\n",
|
||||
" assert test_exp('10 - 2 - 3', Opcall(Opcall(10, '-', 2), '-', 3)) # left associative\n",
|
||||
" assert test_exp('A(I)+M(I, J)', Opcall(Subscript(var='A', indexes=['I']), '+', \n",
|
||||
" Subscript(var='M', indexes=['I', 'J'])))\n",
|
||||
" assert test_exp('X * -1', Opcall('X', '*', Funcall('NEG', 1.0)))\n",
|
||||
" assert test_exp('X--Y--Z', Opcall(Opcall('X', '-', Funcall('NEG', 'Y')), \n",
|
||||
" '-', Funcall('NEG', 'Z')))\n",
|
||||
" assert test_exp('((((X))))', 'X')\n",
|
||||
" return 'ok'\n",
|
||||
"\n",
|
||||
"test_parser()"
|
||||
@ -844,7 +859,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"execution_count": 22,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
@ -881,10 +896,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 23,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def execute(stmts): \n",
|
||||
@ -944,10 +957,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 23,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 24,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import math\n",
|
||||
@ -1040,10 +1051,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 24,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 25,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def basic_print(items): \n",
|
||||
@ -1082,10 +1091,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 25,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 26,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
@ -1093,7 +1100,7 @@
|
||||
"text": [
|
||||
"\n",
|
||||
"10 REM POWER TABLE\n",
|
||||
"11 DATA 8, 4\n",
|
||||
"11 DATA 8, 4\n",
|
||||
"15 READ N0, P0\n",
|
||||
"20 PRINT \"N\",\n",
|
||||
"25 FOR P = 2 to P0\n",
|
||||
@ -1120,9 +1127,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 26,
|
||||
"execution_count": 27,
|
||||
"metadata": {
|
||||
"collapsed": false,
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [
|
||||
@ -1149,7 +1155,7 @@
|
||||
" Stmt(num=99, typ='END', args=[])]"
|
||||
]
|
||||
},
|
||||
"execution_count": 26,
|
||||
"execution_count": 27,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@ -1160,10 +1166,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 27,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 28,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
@ -1197,10 +1201,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 28,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 29,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
@ -1237,10 +1239,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 29,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 30,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
@ -1277,10 +1277,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 30,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 31,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
@ -1306,10 +1304,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 31,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 32,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
@ -1369,10 +1365,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 32,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 33,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
@ -1401,10 +1395,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 33,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 34,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
@ -1455,39 +1447,37 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 34,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 35,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"defaultdict(float,\n",
|
||||
" {'J': 5.0,\n",
|
||||
" ('S', (1.0, 1.0)): 40.0,\n",
|
||||
" ('S', (2.0, 4.0)): 21.0,\n",
|
||||
" ('P', (1.0,)): 1.25,\n",
|
||||
" ('S', (3.0, 2.0)): 47.0,\n",
|
||||
" ('S', (2.0, 2.0)): 16.0,\n",
|
||||
" ('S', (3.0, 3.0)): 29.0,\n",
|
||||
" {('P', (1.0,)): 1.25,\n",
|
||||
" ('S', (3.0, 5.0)): 33.0,\n",
|
||||
" ('S', (1.0, 5.0)): 42.0,\n",
|
||||
" ('S', (3.0, 4.0)): 16.0,\n",
|
||||
" 'I': 3.0,\n",
|
||||
" ('S', (1.0, 5.0)): 42.0,\n",
|
||||
" ('S', (2.0, 1.0)): 10.0,\n",
|
||||
" ('P', (3.0,)): 2.5,\n",
|
||||
" ('S', (1.0, 4.0)): 29.0,\n",
|
||||
" ('S', (2.0, 3.0)): 3.0,\n",
|
||||
" ('S', (1.0, 3.0)): 37.0,\n",
|
||||
" ('S', (2.0, 1.0)): 10.0,\n",
|
||||
" ('S', (1.0, 2.0)): 20.0,\n",
|
||||
" ('S', (2.0, 5.0)): 8.0,\n",
|
||||
" ('S', (3.0, 1.0)): 35.0,\n",
|
||||
" 'I': 3.0,\n",
|
||||
" ('S', (2.0, 4.0)): 21.0,\n",
|
||||
" ('P', (2.0,)): 4.3,\n",
|
||||
" ('S', (2.0, 5.0)): 8.0,\n",
|
||||
" ('S', (1.0, 1.0)): 40.0,\n",
|
||||
" ('S', (3.0, 3.0)): 29.0,\n",
|
||||
" 'J': 5.0,\n",
|
||||
" ('S', (3.0, 2.0)): 47.0,\n",
|
||||
" 'S': 169.4,\n",
|
||||
" ('P', (2.0,)): 4.3})"
|
||||
" ('S', (2.0, 2.0)): 16.0,\n",
|
||||
" ('S', (2.0, 3.0)): 3.0,\n",
|
||||
" ('S', (1.0, 3.0)): 37.0})"
|
||||
]
|
||||
},
|
||||
"execution_count": 34,
|
||||
"execution_count": 35,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@ -1498,19 +1488,17 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 35,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 36,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"6 3 2 6 9 0 0 3 3 6 9 2 7 9 1 2 1 4 4 1 2 7 3 7 8 \n",
|
||||
"5 2 5 4 4 9 7 7 0 5 4 9 7 7 5 5 4 9 3 8 6 2 6 4 5 \n",
|
||||
"8 7 6 0 3 4 1 4 2 2 8 1 1 9 2 4 0 1 0 2 1 3 4 0 2 \n",
|
||||
"9 0 2 7 4 1 8 4 3 2 8 3 9 3 9 3 2 4 6 8 9 0 9 5 9 \n"
|
||||
"7 2 8 4 9 1 9 8 0 8 3 0 3 4 5 6 4 2 1 6 9 9 9 7 5 \n",
|
||||
"8 4 1 5 9 9 7 5 5 7 2 0 3 2 1 2 1 8 3 9 9 3 0 0 2 \n",
|
||||
"2 8 9 7 5 8 9 0 2 7 8 6 7 4 9 9 4 2 5 4 0 9 6 5 3 \n",
|
||||
"3 3 5 1 7 1 6 1 0 7 3 5 8 1 9 0 4 7 1 7 4 5 1 7 6 \n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@ -1527,10 +1515,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 36,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 37,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
@ -1564,10 +1550,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 37,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 38,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
@ -1602,10 +1586,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 38,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 39,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
@ -1632,10 +1614,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 39,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 40,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
@ -1668,10 +1648,8 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 40,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 41,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
@ -1730,22 +1708,18 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Final Program\n",
|
||||
"\n",
|
||||
"Now for a final, longer example, Conway's Game of [Life](https://en.wikipedia.org/wiki/Conway's_Game_of_Life),\n",
|
||||
"which shows that BASIC is capable of handling a non-trivial problem—but I wouldn't want to rely on it for anything much bigger. This should give us some added confidence in the validity of the interpreter, but I would say the interpreter needs more work before I would trust it. I hope you found working through the interpreter informative, and maybe you have ideas for how to improve it, or to develop an interpreter for another language."
|
||||
"which shows that BASIC is capable of handling a non-trivial problem—but I wouldn't want to rely on it for anything much bigger. This should give us some added confidence in the validity of the interpreter, but I would say the interpreter needs more work before I would trust it. I hope you found working through the interpreter infortative, and maybe you have ideas for how to improve it, or to develop an interpreter for another language."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 41,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"execution_count": 42,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
@ -1931,6 +1905,15 @@
|
||||
"999 END \n",
|
||||
"''')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
@ -1949,9 +1932,9 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.5.1"
|
||||
"version": "3.5.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
"nbformat_minor": 1
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user