Skip to content

Commit

Permalink
Record line numbers in AST nodes.
Browse files Browse the repository at this point in the history
  • Loading branch information
cpressey committed Feb 23, 2022
1 parent d5c70bc commit 204b6db
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 60 deletions.
3 changes: 2 additions & 1 deletion src/castile/ast.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class AST(object):
def __init__(self, tag, children=None, value=None, type=None, aux=None):
def __init__(self, tag, children=None, value=None, type=None, aux=None, line=None):
self.tag = tag
self.line = line
self.value = value
# typechecker may populate this. parser will not.
self.type = type
Expand Down
110 changes: 57 additions & 53 deletions src/castile/parser.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import re

from castile.ast import AST
from castile.scanner import Scanner, CastileSyntaxError

Expand Down Expand Up @@ -43,14 +41,20 @@ def on_any(self, *args, **kwargs):
def on_type(self, *args, **kwargs):
return self.scanner.on_type(*args, **kwargs)

### Delegate to AST

def ast(self, *args, **kwargs):
kwargs['line'] = self.scanner.line
return AST(*args, **kwargs)

### Parser proper

def program(self):
defns = []
while not self.on_type('EOF'):
defns.append(self.defn())
self.consume(';')
return AST('Program', defns)
return self.ast('Program', defns)

def defn(self):
if self.consume('fun'):
Expand All @@ -63,8 +67,8 @@ def defn(self):
args.append(self.arg())
self.expect(")")
body = self.body()
funlit = AST('FunLit', [AST('Args', args), body])
return AST('Defn', [funlit], value=id)
funlit = self.ast('FunLit', [self.ast('Args', args), body])
return self.ast('Defn', [funlit], value=id)
elif self.consume('struct'):
id = self.expect_type('identifier')
self.expect("{")
Expand All @@ -73,49 +77,49 @@ def defn(self):
name = self.expect_type('identifier')
self.expect(':')
texpr = self.texpr0()
components.append(AST('FieldDefn', [texpr], value=name))
components.append(self.ast('FieldDefn', [texpr], value=name))
self.consume(';')
self.expect("}")
scope_children = []
if self.consume("for"):
self.expect("(")
idents = []
if not self.on(")"):
idents.append(AST('Ident', value=self.expect_type('identifier')))
idents.append(self.ast('Ident', value=self.expect_type('identifier')))
while self.consume(","):
idents.append(AST('Ident', value=self.expect_type('identifier')))
idents.append(self.ast('Ident', value=self.expect_type('identifier')))
self.expect(")")
scope_children.append(AST('Idents', idents))
return AST('StructDefn', [AST('FieldDefns', components)] + scope_children, value=id)
scope_children.append(self.ast('Idents', idents))
return self.ast('StructDefn', [self.ast('FieldDefns', components)] + scope_children, value=id)
else:
id = self.expect_type('identifier')
if self.consume('='):
e = self.literal()
return AST('Defn', [e], value=id)
return self.ast('Defn', [e], value=id)
else:
self.expect(':')
e = self.texpr0()
return AST('Forward', [e], value=id)
return self.ast('Forward', [e], value=id)

def arg(self):
id = self.expect_type('identifier')
te = AST('Type', value='integer')
te = self.ast('Type', value='integer')
if self.consume(':'):
te = self.texpr1()
return AST('Arg', [te], value=id)
return self.ast('Arg', [te], value=id)

def texpr0(self):
ast = self.texpr1()
if self.consume('->'):
r = self.texpr1()
return AST('FunType', [r, ast])
return self.ast('FunType', [r, ast])
if self.on(','):
args = [ast]
while self.consume(','):
args.append(self.texpr1())
self.expect('->')
r = self.texpr1()
return AST('FunType', [r] + args)
return self.ast('FunType', [r] + args)
return ast

def texpr1(self):
Expand All @@ -124,7 +128,7 @@ def texpr1(self):
args = [ast]
while self.consume('|'):
args.append(self.texpr2())
ast = AST('UnionType', args)
ast = self.ast('UnionType', args)
return ast

def texpr2(self):
Expand All @@ -134,9 +138,9 @@ def texpr2(self):
return ast
elif self.on_type('identifier'):
id = self.consume_type('identifier')
return AST('StructType', [], value=id)
return self.ast('StructType', [], value=id)
tname = self.expect_type('type name')
return AST('Type', value=tname)
return self.ast('Type', value=tname)

def block(self):
self.expect('{')
Expand All @@ -145,7 +149,7 @@ def block(self):
stmts.append(self.stmt())
self.consume(';')
self.expect('}')
return AST('Block', stmts)
return self.ast('Block', stmts)

STMT_TAGS = ('If', 'While', 'TypeCase', 'Return', 'Break')

Expand All @@ -163,36 +167,36 @@ def body(self):
stmts.append(last)
self.consume(';')
if len(stmts) == 0:
stmts = [AST('Return', [AST('None')])]
stmts = [self.ast('Return', [self.ast('None')])]
elif last is not None and last.tag not in self.STMT_TAGS:
stmts[-1] = AST('Return', [stmts[-1]])
stmts[-1] = self.ast('Return', [stmts[-1]])
self.expect('}')
vardecls = AST(
vardecls = self.ast(
'VarDecls',
[AST('VarDecl', value=name) for name in self.locals]
[self.ast('VarDecl', value=name) for name in self.locals]
)
stmts = AST('Block', stmts)
stmts = self.ast('Block', stmts)
self.locals = save_locals
return AST('Body', [vardecls, stmts])
return self.ast('Body', [vardecls, stmts])

def stmt(self):
if self.on('if'):
return self.ifstmt()
elif self.consume('while'):
t = self.expr0()
b = self.block()
return AST('While', [t, b])
return self.ast('While', [t, b])
elif self.consume('typecase'):
id = self.expect_type('identifier')
e = AST('VarRef', value=id)
e = self.ast('VarRef', value=id)
self.expect('is')
te = self.texpr0()
b = self.block()
return AST('TypeCase', [e, te, b], value=te.minirepr())
return self.ast('TypeCase', [e, te, b], value=te.minirepr())
elif self.consume('return'):
return AST('Return', [self.expr0()])
return self.ast('Return', [self.expr0()])
elif self.consume('break'):
return AST('Break')
return self.ast('Break')
else:
return self.expr0()

Expand All @@ -206,42 +210,42 @@ def ifstmt(self):
b2 = self.ifstmt()
else:
b2 = self.block()
return AST('If', [t, b1, b2])
return AST('If', [t, b1])
return self.ast('If', [t, b1, b2])
return self.ast('If', [t, b1])

def expr0(self):
e = self.expr1()
while self.on_type('boolean operator'):
op = self.expect_type('boolean operator')
e2 = self.expr1()
e = AST('Op', [e, e2], value=op)
e = self.ast('Op', [e, e2], value=op)
if self.consume('as'):
union_te = self.texpr0()
e = AST('TypeCast', [e, union_te])
e = self.ast('TypeCast', [e, union_te])
return e

def expr1(self):
e = self.expr2()
while self.on_type('relational operator'):
op = self.expect_type('relational operator')
e2 = self.expr2()
e = AST('Op', [e, e2], value=op)
e = self.ast('Op', [e, e2], value=op)
return e

def expr2(self):
e = self.expr3()
while self.on_type('additive operator'):
op = self.expect_type('additive operator')
e2 = self.expr3()
e = AST('Op', [e, e2], value=op)
e = self.ast('Op', [e, e2], value=op)
return e

def expr3(self):
e = self.expr4()
while self.on_type('multiplicative operator'):
op = self.expect_type('multiplicative operator')
e2 = self.expr4()
e = AST('Op', [e, e2], value=op)
e = self.ast('Op', [e, e2], value=op)
return e

def expr4(self):
Expand All @@ -255,10 +259,10 @@ def expr4(self):
while self.consume(","):
args.append(self.expr0())
self.expect(")")
e = AST('FunCall', [e] + args)
e = self.ast('FunCall', [e] + args)
elif self.consume('.'):
id = self.expect_type('identifier')
e = AST('Index', [e], value=id)
e = self.ast('Index', [e], value=id)
else:
done = True
return e
Expand All @@ -269,7 +273,7 @@ def expr5(self):
elif self.on_any(('-', 'fun', 'true', 'false', 'null')):
return self.literal()
elif self.consume('not'):
return AST('Not', [self.expr1()])
return self.ast('Not', [self.expr1()])
elif self.consume('make'):
# TODO I just accidentally any type. Is that bad?
texpr = self.texpr0()
Expand All @@ -279,47 +283,47 @@ def expr5(self):
id = self.expect_type('identifier')
self.expect(':')
e = self.expr0()
args.append(AST('FieldInit', [e], value=id))
args.append(self.ast('FieldInit', [e], value=id))
while self.consume(","):
id = self.expect_type('identifier')
self.expect(':')
e = self.expr0()
args.append(AST('FieldInit', [e], value=id))
args.append(self.ast('FieldInit', [e], value=id))
self.expect(")")
return AST('Make', [texpr] + args, value=texpr.minirepr())
return self.ast('Make', [texpr] + args, value=texpr.minirepr())

elif self.consume('('):
e = self.expr0()
self.expect(')')
return e
else:
id = self.expect_type('identifier')
ast = AST('VarRef', value=id)
ast = self.ast('VarRef', value=id)
if self.consume('='):
e = self.expr0()
aux = None
if id not in self.locals:
self.locals.add(id)
aux = 'defining instance'
ast = AST('Assignment', [ast, e], aux=aux)
ast = self.ast('Assignment', [ast, e], aux=aux)
return ast

def literal(self):
if self.on_type('string literal'):
v = self.consume_type('string literal')
return AST('StrLit', value=v)
return self.ast('StrLit', value=v)
elif self.on_type('integer literal'):
v = int(self.consume_type('integer literal'))
return AST('IntLit', value=v)
return self.ast('IntLit', value=v)
elif self.consume('-'):
v = 0 - int(self.expect_type('integer literal'))
return AST('IntLit', value=v)
return self.ast('IntLit', value=v)
elif self.consume('true'):
return AST('BoolLit', value=True)
return self.ast('BoolLit', value=True)
elif self.consume('false'):
return AST('BoolLit', value=False)
return self.ast('BoolLit', value=False)
elif self.consume('null'):
return AST('None')
return self.ast('None')
else:
self.expect('fun')
self.expect("(")
Expand All @@ -330,4 +334,4 @@ def literal(self):
args.append(self.arg())
self.expect(")")
body = self.body()
return AST('FunLit', [AST('Args', args), body])
return self.ast('FunLit', [self.ast('Args', args), body])
12 changes: 6 additions & 6 deletions src/castile/scanner.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import re

from castile.ast import AST


class CastileSyntaxError(ValueError):
pass
Expand All @@ -14,6 +12,7 @@ def __init__(self, text):
self.token = None
self.type = None
self.pos = 0
self.line = 1
self.scan()

def near_text(self, length=10):
Expand All @@ -29,6 +28,7 @@ def scan_pattern(self, pattern, type, token_group=1, rest_group=2):
self.type = type
self.token = match.group(token_group)
self.pos += len(match.group(0))
self.line += self.token.count('\n')
return True

def scan(self):
Expand Down Expand Up @@ -77,8 +77,8 @@ def expect(self, token):
self.scan()
else:
raise CastileSyntaxError(
"Expected '%s', but found '%s' (near '%s')" % (
token, self.token, self.near_text()
"Expected '%s', but found '%s' (line %s, near '%s')" % (
token, self.token, self.line, self.near_text()
)
)

Expand All @@ -100,8 +100,8 @@ def on_type(self, type):
def check_type(self, type):
if not self.type == type:
raise CastileSyntaxError(
"Expected %s, but found %s ('%s') (near '%s')" % (
type, self.type, self.token, self.near_text()
"Expected %s, but found %s ('%s') (line %s, near '%s')" % (
type, self.type, self.token, self.line, self.near_text()
)
)

Expand Down

0 comments on commit 204b6db

Please sign in to comment.