import Spa.Language.Base

namespace Spa

/-!
Scoped quotation syntax for writing object-language programs.

`[obj_expr| … ]` builds an `Expr`, `[obj_stmt| … ]` builds a `Stmt`.

Example:
```
[obj_stmt|
  zero := 0;
  pos  := zero + 1;
  if pos { x := 1 } else { noop };
  while x { x := x - 1 }
]
```
-/

/-- Expressions of the object language. -/
declare_syntax_cat obj_expr

syntax num                              : obj_expr
syntax ident                           : obj_expr
syntax:65 obj_expr:65 " + " obj_expr:66 : obj_expr
syntax:65 obj_expr:65 " - " obj_expr:66 : obj_expr
syntax "(" obj_expr ")"                 : obj_expr

/-- Statements of the object language. -/
declare_syntax_cat obj_stmt

syntax "noop"                                          : obj_stmt
syntax ident " := " obj_expr                           : obj_stmt
syntax "if " obj_expr " { " obj_stmt " } " "else" " { " obj_stmt " } " : obj_stmt
syntax "while " obj_expr " { " obj_stmt " } "          : obj_stmt
syntax:50 obj_stmt:51 "; " obj_stmt:50                 : obj_stmt
syntax "(" obj_stmt ")"                                : obj_stmt

scoped syntax "[obj_expr| " obj_expr " ]" : term
scoped syntax "[obj_stmt| " obj_stmt " ]" : term

scoped macro_rules
  | `([obj_expr| $n:num])        => `(Expr.num $n)
  | `([obj_expr| $x:ident])      => `(Expr.var $(Lean.quote x.getId.toString))
  | `([obj_expr| $a + $b])       => `(Expr.add [obj_expr| $a] [obj_expr| $b])
  | `([obj_expr| $a - $b])       => `(Expr.sub [obj_expr| $a] [obj_expr| $b])
  | `([obj_expr| ($e:obj_expr)]) => `([obj_expr| $e])

scoped macro_rules
  | `([obj_stmt| noop])           => `(Stmt.basic .noop)
  | `([obj_stmt| $x:ident := $e]) =>
      `(Stmt.basic (.assign $(Lean.quote x.getId.toString) [obj_expr| $e]))
  | `([obj_stmt| $s₁ ; $s₂])      => `(Stmt.andThen [obj_stmt| $s₁] [obj_stmt| $s₂])
  | `([obj_stmt| if $e { $s₁ } else { $s₂ }]) =>
      `(Stmt.ifElse [obj_expr| $e] [obj_stmt| $s₁] [obj_stmt| $s₂])
  | `([obj_stmt| while $e { $s }]) => `(Stmt.whileLoop [obj_expr| $e] [obj_stmt| $s])
  | `([obj_stmt| ($s:obj_stmt)])  => `([obj_stmt| $s])

end Spa
