From 784e5e6095dab0f8910d3b936f45c8e13bc53b84 Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Mon, 14 Feb 2022 22:20:17 +0000 Subject: [PATCH] Checkpoint implementing scoped structs. --- doc/Grammar.md | 2 +- src/castile/backends/c.py | 22 +++++---- src/castile/backends/javascript.py | 5 +- src/castile/checker.py | 15 +++--- src/castile/parser.py | 12 ++++- test.sh | 1 + tests/Castile.md | 77 ++++++++++++++++++++++++++++++ 7 files changed, 114 insertions(+), 20 deletions(-) diff --git a/doc/Grammar.md b/doc/Grammar.md index eeae00e..f7b11f9 100644 --- a/doc/Grammar.md +++ b/doc/Grammar.md @@ -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 [";"]} "}". diff --git a/src/castile/backends/c.py b/src/castile/backends/c.py index b55ce97..82339f6 100644 --- a/src/castile/backends/c.py +++ b/src/castile/backends/c.py @@ -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') @@ -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)) diff --git a/src/castile/backends/javascript.py b/src/castile/backends/javascript.py index af789ae..277d213 100644 --- a/src/castile/backends/javascript.py +++ b/src/castile/backends/javascript.py @@ -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)) diff --git a/src/castile/checker.py b/src/castile/checker.py index bba8625..a216bba 100644 --- a/src/castile/checker.py +++ b/src/castile/checker.py @@ -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 = {} @@ -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): diff --git a/src/castile/parser.py b/src/castile/parser.py index d65c288..48fcf2a 100644 --- a/src/castile/parser.py +++ b/src/castile/parser.py @@ -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('='): diff --git a/test.sh b/test.sh index adbefc0..a5a9be6 100755 --- a/test.sh +++ b/test.sh @@ -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 diff --git a/tests/Castile.md b/tests/Castile.md index a15fb17..855f14b 100644 --- a/tests/Castile.md +++ b/tests/Castile.md @@ -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