Skip to content

Commit

Permalink
Merge pull request #3 from catseye/develop-0.5
Browse files Browse the repository at this point in the history
Develop 0.5
  • Loading branch information
cpressey authored Mar 13, 2022
2 parents 9a6bd7c + 1985ccf commit d756a32
Show file tree
Hide file tree
Showing 24 changed files with 944 additions and 455 deletions.
93 changes: 93 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
History of Castile
==================

Castile 0.5
-----------

### Distribution

* Added HISTORY.md file.

### Language

* Scoped structs can be declared with the `for (...)` clause
after the struct. A value of a scope struct can only be
`make`d, and the fields of such a value can only be accessed,
lexically inside one of the definitions named in the `for`.
* Structs cannot be tested for equality with the `==` and `!=`
operators. Instead the programmer should write a function
that compares structs for equality, if desired.
* Values of union type can be tested for equality, but only if
none of the types involved in the union are structs.

### Implementation

* Lexical scanner has been split off from parser code, into
its own module. A performance bug (using O(n^2) space
instead of O(n)) during scanning has also been fixed.
* Line numbers are recorded in the AST when parsing, and
reported on type errors when type errors occur.
* Requesting the AST be dumped, will also dump the AST with
type assignments, if an error occurs during type checking.
* Established an abstract base class for compiler backends.
* Fixed a bug where tagged values were being tagged again
during a cast from a union type to another union type.
* ArgumentParser is used instead of OptionParser to parse
command-line arguments. `--version` added, `--test` (and
remaining doctests in source modules) removed.

Castile 0.4
-----------

### Distribution

* Re-focused project: Castile is a simple imperative language
with union types.
* Released under a 3-clause BSD license.

### Language

* `struct`s cannot be compared for order, it is a static error.
* A union type is allowed to be promoted to a bigger union type,
or to itself.

### Implementation

* Completed the C-generating backend of the compiler: it passes all tests now.
* Implemented `str` builtin, equality testing of `struct`s in all backends.
* Improved pretty-printing of code in C and Ruby backends.
* Implemented `ne` in stackmac implementation.

Castile 0.3 revision 2021.0625
------------------------------

* Updated implementation to run under both Python 2 and Python 3.
* Refactored test suite, making it more idiomatic Falderal.

Castile 0.3 revision 2016.0331
------------------------------

* Fixed generated Ruby code that worked in Ruby 1.8 but fails in Ruby 1.9.

Castile 0.3 revision 2015.0101
------------------------------

* Stopped using deprecated Falderal variable names in test suite.

Castile 0.3
-----------

* Treatment of local variables became more Python-like.
* Beginnings of a C backend in compiler.

Castile 0.2
-----------

* Heavy development of the language, with many changes.
* Added JavaScript, Ruby, and stackmac (stack-machine) backends to compiler.

Castile 0.1
-----------

Initial release of Castile, an unremarkable language with an unremarkable
compiler/interpreter in Python.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Castile
=======

Version 0.4 | _Entry_ [@ catseye.tc](https://catseye.tc/node/Castile)
Version 0.5 | _Entry_ [@ catseye.tc](https://catseye.tc/node/Castile)
| _See also:_ [Eightebed](https://github.com/catseye/Eightebed#readme)
[Dieter](https://github.com/catseye/Dieter#readme)

Expand Down
19 changes: 10 additions & 9 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ TODO

### Implementation ###

Line number reporting for Context errors and Syntax errors.

Name mangling for compilers (prepend with `_` most likely.)

And literal characters in strings, especially `'` and `"`.
Expand All @@ -11,8 +13,6 @@ Figure out a way to do `input`, `read`, and `write` with node.js backend.

Implement `int`, `chr`, `ord` for Ruby, JavaScript, stackmac, C.

Struct equality is not properly deep in JavaScript or C.

Better indentation in the JavaScript backend.

TaggedValue -> just a tuple.
Expand All @@ -21,23 +21,24 @@ stackmac: store tagged values as two values on the stack.
and void types in unions of (void, X) should only be one value.
(structs are still boxed though)

AST nodes should have source line numbers, it would be really nice.

Implement garbage collection of some sort in the C backend. Either that
or implement some kind of resource-awareness in the language itself.

Other backends (Python? Java? CIL? Scheme?)

Test framework: collect the backend-independent tests into a single
file, and only test it once. Run all the *other* tests on every
backend.

### Design ###

Don't output final value. Command-line arguments passed to `main`. (`sysmain`?)

Convenience:
Automatic type promotion (upcasting), e.g. using an integer where
integer|string is expected (as e.g. a function argument) is fine,
an `as integer|string` should be automatically inserted.

* Should we have automatic promotion (value tagging?)
Since it causes an operation, I think it should be explicit, but the
explicit syntax could be more lightweight.
* Lua-esque `:` operator: `a:b(c)` -> `a.b(a, c)`
Lua-esque `:` operator: `a:b(c)` -> `a.b(a, c)`

Type promotion with higher precedence? So that it can be used at toplevel.

Expand Down
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
70 changes: 70 additions & 0 deletions eg/assoc.castile
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Implementation of an associative map in Castile.
*
* The map is implemented as an association list,
* but this fact is hidden from clients, as only
* the operations have access to the internals
* of the struct.
*/

struct assoc {
key: string;
value: string;
next: assoc|void;
} for (update, lookup, remove, render)

fun empty() {
return null as assoc|void
}

fun update(k: string, v: string, a: assoc|void) {
make assoc(key:k, value:v, next:a as assoc|void)
}

lookup : assoc|void, string -> string|void
fun lookup(a: assoc|void, k: string) {
typecase a is void {
return null as string|void
}
typecase a is assoc {
if a.key == k {
return a.value as string|void
}
return lookup(a.next, k)
}
}

remove : assoc|void, string -> assoc|void
fun remove(a: assoc|void, k: string) {
typecase a is void {
return a as assoc|void
}
typecase a is assoc {
if a.key == k {
return remove(a.next, k)
}
return make assoc(key:a.key, value:a.value, next:remove(a.next, k)) as assoc|void
}
}

render : assoc|void -> string
fun render(a: assoc|void) {
typecase a is void {
return ""
}
typecase a is assoc {
return concat(a.value, concat(",", render(a.next)))
}
}

fun main() {
a = update("3", "third", empty());
a = update("2", "second", a as assoc|void);
a = update("1", "first", a as assoc|void);
print(render(a as assoc|void))
b = remove((a as assoc|void), "2");
print(render(b))
r = lookup(b, "2");
typecase r is void { print("NOT FOUND"); }
typecase r is string { print(r); }
}
45 changes: 45 additions & 0 deletions eg/linkedlist-ops.castile
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
struct list {
value: string;
next: list|void;
}

fun empty() {
return null as list|void
}

fun cons(v: string, l: list|void) {
make list(value:v, next:l) as list|void
}

equal_list : list|void, list|void -> boolean
fun equal_list(a: list|void, b: list|void) {
typecase a is void {
typecase b is void {
return true
}
}
typecase a is list {
typecase b is list {
return a.value == b.value and equal_list(a.next, b.next)
}
}
return false
}

length : list|void -> integer
fun length(l: list|void) {
typecase l is void { return 0 }
typecase l is list { return 1 + length(l.next) }
}

main = fun() {
l1 = cons("first", cons("second", cons("third", empty())));
l2 = cons("first", cons("second", cons("third", empty())));
l3 = cons("first", cons("second", empty()));

print(str(length(l1 as list|void)));

if (equal_list(l1, l2) and not equal_list(l2, l3)) {
print("Yep, story checks out")
}
}
17 changes: 17 additions & 0 deletions eg/typecase-error.castile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
struct person { name: string };
fun foo(a, b: integer|string) {
r = a;
typecase b is integer {
r = r + b;
};
typecase b is person {
r = r + len(b);
};
r
}
main = fun() {
a = 0;
a = foo(a, 333 as integer|string);
a = foo(a, "hiya" as integer|string);
a /* should output 337 */
}
1 change: 1 addition & 0 deletions eg/typecase.castile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ fun foo(a, b: integer|string) {
r
}
main = fun() {
a = 0;
a = foo(a, 333 as integer|string);
a = foo(a, "hiya" as integer|string);
a /* should output 337 */
Expand Down
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
21 changes: 21 additions & 0 deletions src/castile/backends/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""Abstract base class for Castile compiler backends,
especially source-to-source."""

class BaseCompiler(object):
def __init__(self, out):
self.out = out
self.indent = 0

def commas(self, asts, sep=','):
if asts:
for child in asts[:-1]:
self.compile(child)
self.out.write(sep)
self.compile(asts[-1])

def write(self, x):
self.out.write(x)

def write_indent(self, x):
self.out.write(' ' * self.indent)
self.out.write(x)
Loading

0 comments on commit d756a32

Please sign in to comment.