Skip to content

Commit

Permalink
Checkpoint implementing scoped structs.
Browse files Browse the repository at this point in the history
  • Loading branch information
cpressey committed Feb 14, 2022
1 parent 605081b commit 784e5e6
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 20 deletions.
2 changes: 1 addition & 1 deletion doc/Grammar.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ This is an EBNF grammar for Castile.

Program ::= {Defn [";"]}.
Defn ::= "fun" ident "(" [Arg {"," Arg}] ")" Body
| "struct" ident "{" {ident ":" TExpr [";"]} "}"
| "struct" ident "{" {ident ":" TExpr [";"]} "}" ["for" "(" [ident {"," ident}] ")"]
| ident (":" TExpr0 | "=" Literal).
Arg ::= ident [":" TExpr1].
Body ::= "{" {Stmt [";"]} "}".
Expand Down
22 changes: 12 additions & 10 deletions src/castile/backends/c.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,28 +183,30 @@ def compile(self, ast):
elif ast.tag == 'Forward':
self.write_indent('extern %s;\n' % self.c_decl(ast.children[0].type, ast.value))
elif ast.tag == 'StructDefn':
field_defns = ast.children[0].children
self.write_indent('struct %s {\n' % ast.value)
self.indent += 1
for child in ast.children:
for child in field_defns:
assert child.tag == 'FieldDefn', child.tag
self.compile(child)
self.indent -= 1
self.write_indent('};\n\n')
self.write_indent('struct %s * make_%s(' % (ast.value, ast.value))

if ast.children:
for child in ast.children[:-1]:
assert child.tag == 'FieldDefn'
if field_defns:
for child in field_defns[:-1]:
assert child.tag == 'FieldDefn', child.tag
self.write('%s, ' % self.c_decl(child.children[0].type, child.value))
child = ast.children[-1]
assert child.tag == 'FieldDefn'
child = field_defns[-1]
assert child.tag == 'FieldDefn', child.tag
self.write('%s' % self.c_decl(child.children[0].type, child.value))

self.write(') {\n')
self.indent += 1
self.write_indent('struct %s *x = malloc(sizeof(struct %s));\n' % (ast.value, ast.value))

for child in ast.children:
assert child.tag == 'FieldDefn'
for child in field_defns:
assert child.tag == 'FieldDefn', child.tag
self.write_indent('x->%s = %s;\n' % (child.value, child.value))

self.write_indent('return x;\n')
Expand All @@ -214,8 +216,8 @@ def compile(self, ast):
self.write_indent('int equal_%s(struct %s * a, struct %s * b) {\n' % (ast.value, ast.value, ast.value))

self.indent += 1
for child in ast.children:
assert child.tag == 'FieldDefn'
for child in field_defns:
assert child.tag == 'FieldDefn', child.tag
struct_type = child.children[0].value if child.children[0].tag == 'StructType' else None
if struct_type:
self.write_indent('if (!equal_%s(a->%s, b->%s)) return 0;\n' % (struct_type, child.value, child.value))
Expand Down
5 changes: 3 additions & 2 deletions src/castile/backends/javascript.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,10 @@ def compile(self, ast):
elif ast.tag == 'Forward':
pass
elif ast.tag == 'StructDefn':
field_defns = ast.children[0].children
self.out.write('function equal_%s(a, b) {\n' % ast.value)
for child in ast.children:
assert child.tag == 'FieldDefn'
for child in field_defns:
assert child.tag == 'FieldDefn', child.tag
struct_type = child.children[0].value if child.children[0].tag == 'StructType' else None
if struct_type:
self.out.write('if (!equal_%s(a.%s, b.%s)) return false;\n' % (struct_type, child.value, child.value))
Expand Down
15 changes: 9 additions & 6 deletions src/castile/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ class CastileTypeError(ValueError):


class StructDefinition(object):
def __init__(self, name, field_names, content_types):
def __init__(self, name, field_names, content_types, scope_idents):
self.name = name
self.field_names = field_names # dict of name -> position
self.content_types = content_types # list of types in order
self.scope_idents = scope_idents # list of identifiers, or None

def field_names_in_order(self):
m = {}
Expand Down Expand Up @@ -62,17 +63,19 @@ def collect_struct(self, ast):
if name in self.structs:
raise CastileTypeError('duplicate struct %s' % name)
struct_fields = {}
te = []
type_exprs = []
i = 0
for child in ast.children:
assert child.tag == 'FieldDefn'
field_defns = ast.children[0].children
scope_idents = ast.children[1].children if len(ast.children) > 1 else None
for child in field_defns:
assert child.tag == 'FieldDefn', child.tag
field_name = child.value
if field_name in struct_fields:
raise CastileTypeError('already-defined field %s' % field_name)
struct_fields[field_name] = i
i += 1
te.append(self.type_of(child.children[0]))
self.structs[name] = StructDefinition(ast.value, struct_fields, te)
type_exprs.append(self.type_of(child.children[0]))
self.structs[name] = StructDefinition(ast.value, struct_fields, type_exprs, scope_idents)

def resolve_structs(self, ast):
if isinstance(ast.type, Struct):
Expand Down
12 changes: 11 additions & 1 deletion src/castile/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,17 @@ def defn(self):
components.append(AST('FieldDefn', [texpr], value=name))
self.consume(';')
self.expect("}")
return AST('StructDefn', components, value=id)
scope_children = []
if self.consume("for"):
self.expect("(")
idents = []
if not self.on(")"):
idents.append(AST('Ident', value=self.expect_type('identifier')))
while self.consume(","):
idents.append(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)
else:
id = self.expect_type('identifier')
if self.consume('='):
Expand Down
1 change: 1 addition & 0 deletions test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ if [ ! x`command -v gcc` = x ]; then
APPLIANCES="$APPLIANCES tests/appliances/castile-c-c.md"
fi

APPLIANCES="tests/appliances/castile-c-c.md"
#APPLIANCES="tests/appliances/castile-c-javascript.md"

falderal $APPLIANCES tests/Castile.md
Expand Down
77 changes: 77 additions & 0 deletions tests/Castile.md
Original file line number Diff line number Diff line change
Expand Up @@ -1250,3 +1250,80 @@ In combination with unions, this lets us create "typed enums".
| }
= red
= blue

### Scoped Structs ###

When a `struct` is declared, it may be associated with a set of identifiers.
Functions with these global names are the only function definitions which
can `make` such a struct, or see that it has fields; to all other functions,
these operations will not be available. It is in this way that encapsulation
is accomplished.

| struct list {
| value: string;
| next: list|void;
| } for (cons, singleton, empty)
|
| fun cons(v: string, l: list) {
| make list(value:v, next:l as list|void)
| }
|
| fun singleton(v: string) {
| make list(value:v, next:null as list|void)
| }
|
| fun empty(l: list|void) {
| a = "no";
| typecase l is void { a = "yes"; }
| return a;
| }
|
| fun main() {
| l = cons("first", cons("second", singleton("third")));
| print(empty(l));
| }
= no

| struct list {
| value: string;
| next: list|void;
| } for (cons, singleton, empty)
|
| fun cons(v: string, l: list) {
| make list(value:v, next:l as list|void)
| }
|
| fun singleton(v: string) {
| make list(value:v, next:null as list|void)
| }
|
| fun empty(l: list|void) {
| a = "no";
| typecase l is void { a = "yes"; }
| return a;
| }
|
| fun main() {
| l = make list(value:"first", next:null);
| print(empty(l));
| }
? make

| struct list {
| value: string;
| next: list|void;
| } for (cons, singleton, empty)
|
| fun cons(v: string, l: list) {
| make list(value:v, next:l as list|void)
| }
|
| fun singleton(v: string) {
| make list(value:v, next:null as list|void)
| }
|
| fun main() {
| l = cons("first", cons("second", singleton("third")));
| print(l.value);
| }
? value

0 comments on commit 784e5e6

Please sign in to comment.