Last active
June 2, 2024 22:51
-
-
Save osa1/3de1dd44a3656ebabead0de0d9e6f60c to your computer and use it in GitHub Desktop.
Syntax question
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- An evaluator for the language "Untyped Arithmetic Expressions" described in | |
-- Types and Programming Langauges section 3. | |
-- Below I have a few different syntax for starting indentation blocks. | |
-- | |
-- I'm looking for the syntax that is (in priority order) | |
-- | |
-- (1) Most readable (subjective, I know) | |
-- (2) Easiest to implement tooling for (e.g. indentatation plugin in text | |
-- editors) | |
-- | |
-- Error recorvery and incremental parsing is not a concern: in an indentation | |
-- sensitive language you can easily split files into top-level definitions | |
-- based on indentation. A syntax error in one top-level definition, no matter | |
-- how bad, can't affect other definitions. In the worst case you skip the | |
-- whole top-level definitions and parse the rest. | |
-- | |
-- Not using any token for starting indentation blocks is a possibility, but I | |
-- think that would make tooling difficult. | |
-- | |
-- Ideally I also want one token that starts all indentation blocks. E.g. we | |
-- can't have 'do', 'with', 'of', 'begin' etc. ideally there should be only one | |
-- that works for all kinds of blocks. | |
-- | |
-- Option (1): without any tokens | |
-- | |
-- I think this looks good, but implementing indentation support in editors may | |
-- be tricky? | |
-- | |
type Term | |
True | |
False | |
Zero | |
If | |
cond: Term | |
then: Term | |
else_: Term | |
Succ(Term) | |
Pred(Term) | |
IsZero(Term) | |
type Value | |
Bool(Bool) | |
Num(Int) | |
fn eval(term: Term): Value | |
match term | |
Term.True | |
Value.Bool(Bool.True) | |
Term.False | |
Value.Bool(Bool.False) | |
Term.Zero | |
Value.Num(0) | |
Term.If(cond, then, else_) | |
let cond = match eval(cond) | |
Value.Bool(bool) | |
bool | |
Value.Num(num) | |
panic("If expression condition evaluated to number") | |
if cond | |
eval(then) | |
else | |
eval(else_) | |
Term.Succ(term) | |
match eval(term) | |
Value.Bool(_) | |
panic("Succ argument evaluated to bool") | |
Value.Num(value) | |
Value.Num(value + 1) | |
Term.Pred(term) | |
match eval(term) | |
Value.Bool(_) | |
panic("Pred argument evaluated to bool") | |
Value.Num(value) | |
Value.Num(value - 1) | |
Term.IsZero(term) | |
match eval(term) | |
Value.Bool(_) | |
panic("IsZero argument evaluated to bool") | |
Value.Num(value) | |
Value.Bool(value == 0) | |
-- | |
-- Option (2): ':'. | |
-- | |
-- This is the most familiar syntax (Python), but I feel like it overloads ':' | |
-- a bit too much. It's used for both starting an indentation and for type | |
-- annotations. | |
-- | |
-- In the example below there are only two type annotations, but you can | |
-- imagine more complicated top-level functions with lots of arguments.. | |
-- | |
-- Also, with this syntax we probably want '=' for starting function bodies. | |
-- Example: | |
-- | |
-- fn test() : <type> : <expr> -- confusing | |
-- fn test() : <type> = <expr> -- better | |
-- | |
type Term: | |
True | |
False | |
Zero | |
If: | |
cond: Term | |
then: Term | |
else_: Term | |
Succ(Term) | |
Pred(Term) | |
IsZero(Term) | |
type Value: | |
Bool(Bool) | |
Num(Int) | |
fn eval(term: Term): Value = | |
match term: | |
Term.True: | |
Value.Bool(Bool.True) | |
Term.False: | |
Value.Bool(Bool.False) | |
Term.Zero: | |
Value.Num(0) | |
Term.If(cond, then, else_): | |
let cond = match eval(cond): | |
Value.Bool(bool): | |
bool | |
Value.Num(num): | |
panic("If expression condition evaluated to number") | |
if cond: | |
eval(then) | |
else: | |
eval(else_) | |
Term.Succ(term): | |
match eval(term): | |
Value.Bool(_): | |
panic("Succ argument evaluated to bool") | |
Value.Num(value): | |
Value.Num(value + 1) | |
Term.Pred(term): | |
match eval(term): | |
Value.Bool(_): | |
panic("Pred argument evaluated to bool") | |
Value.Num(value): | |
Value.Num(value - 1) | |
Term.IsZero(term): | |
match eval(term): | |
Value.Bool(_): | |
panic("IsZero argument evaluated to bool") | |
Value.Num(value): | |
Value.Bool(value == 0) | |
-- | |
-- Option (3): '>' | |
-- | |
-- I think this looks ugly.. | |
-- | |
type Term > | |
True | |
False | |
Zero | |
If > | |
cond: Term | |
then: Term | |
else_: Term | |
Succ(Term) | |
Pred(Term) | |
IsZero(Term) | |
type Value > | |
Bool(Bool) | |
Num(Int) | |
fn eval(term: Term): Value > | |
match term > | |
Term.True > | |
Value.Bool(Bool.True) | |
Term.False > | |
Value.Bool(Bool.False) | |
Term.Zero > | |
Value.Num(0) | |
Term.If(cond, then, else_) > | |
let cond = match eval(cond) > | |
Value.Bool(bool) > | |
bool | |
Value.Num(num) > | |
panic("If expression condition evaluated to number") | |
if cond > | |
eval(then) | |
else > | |
eval(else_) | |
Term.Succ(term) > | |
match eval(term) > | |
Value.Bool(_) > | |
panic("Succ argument evaluated to bool") | |
Value.Num(value) > | |
Value.Num(value + 1) | |
Term.Pred(term) > | |
match eval(term) > | |
Value.Bool(_) > | |
panic("Pred argument evaluated to bool") | |
Value.Num(value) > | |
Value.Num(value - 1) | |
Term.IsZero(term) > | |
match eval(term) > | |
Value.Bool(_) > | |
panic("IsZero argument evaluated to bool") | |
Value.Num(value) > | |
Value.Bool(value == 0) | |
-- | |
-- Option (4): '::' | |
-- | |
-- Somehow this looks better than (3) to me, but it's still noisy | |
-- | |
type Term :: | |
True | |
False | |
Zero | |
If :: | |
cond: Term | |
then: Term | |
else_: Term | |
Succ(Term) | |
Pred(Term) | |
IsZero(Term) | |
type Value :: | |
Bool(Bool) | |
Num(Int) | |
fn eval(term: Term): Value :: | |
match term :: | |
Term.True :: | |
Value.Bool(Bool.True) | |
Term.False :: | |
Value.Bool(Bool.False) | |
Term.Zero :: | |
Value.Num(0) | |
Term.If(cond, then, else_) :: | |
let cond = match eval(cond) :: | |
Value.Bool(bool) :: | |
bool | |
Value.Num(num) :: | |
panic("If expression condition evaluated to number") | |
if cond :: | |
eval(then) | |
else :: | |
eval(else_) | |
Term.Succ(term) :: | |
match eval(term) :: | |
Value.Bool(_) :: | |
panic("Succ argument evaluated to bool") | |
Value.Num(value) :: | |
Value.Num(value + 1) | |
Term.Pred(term) :: | |
match eval(term) :: | |
Value.Bool(_) :: | |
panic("Pred argument evaluated to bool") | |
Value.Num(value) :: | |
Value.Num(value - 1) | |
Term.IsZero(term) :: | |
match eval(term) :: | |
Value.Bool(_) :: | |
panic("IsZero argument evaluated to bool") | |
Value.Num(value) :: | |
Value.Bool(value == 0) | |
-- | |
-- Option (5): ??? | |
-- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment