Skip to content

Commit

Permalink
make macro a macro
Browse files Browse the repository at this point in the history
* macro definitions are no longer auto-evaluated during compilation
  * use let-macro for this
  • Loading branch information
vito committed Feb 23, 2012
1 parent fd2459e commit 559dc57
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 129 deletions.
16 changes: 8 additions & 8 deletions kernel/core.ay
@@ -1,21 +1,21 @@
infix("=", 10, "right")
infix("?", 30, "right")

-- a macro so we can actually, you know, send messages
macro(~_ ~(? to-word)): make-send(node)
-- a few macros so we can actually, you know, send messages
define-macro('(~_ ~(? to-word)), 'make-send(node))
define-macro('(~_ ~(? to-word)(~*_)), 'make-send(node))
define-macro('(~(? to-word)(~*_)), 'make-send(node))

macro(~_ ~(? to-word)(~*_)): make-send(node)

macro(~(? to-word)(~*_)): node to-send
-- a macro for defining macros!
define-macro('(macro(~pat) ~(body: Block)), '(Macro new(node line, pat, body body)))

-- pseudo-variables
macro(nil): Primitive new(node line, "nil" to-sym)

macro(self): Primitive new(node line, "self" to-sym)

macro(true): Primitive new(node line, "true" to-sym)

macro(false): Primitive new(node line, "false" to-sym)

-- assignment/pattern-matching
macro(~x = ~y): Assign new(node line, x to-pattern to-node, y)

-- [x, y] { x + y }
Expand Down
10 changes: 3 additions & 7 deletions kernel/meta.ay
Expand Up @@ -24,17 +24,13 @@ Atomy::AST open:
{ Atomy::CodeLoader module = mod
Atomy::CodeLoader context = bnd

@macros each [m]:
mod define-macro(m pattern, m body, Atomy::CodeLoader compiling)
@macros each [`(~pat = ~exp)]:
mod define-macro(pat, exp, Atomy::CodeLoader compiling)

body compile(g)
} ensuring:
Atomy::CodeLoader module = before-mod
Atomy::CodeLoader context = before-ctx

macro(let-macro(~*ms) ~(b: Block)):
LetMacro new(
node line
b body
ms collect [`(~p = ~b)]:
`(macro(~p): ~b))
LetMacro new(node line, b body, ms)
7 changes: 0 additions & 7 deletions lib/atomy/ast/macro.rb
Expand Up @@ -10,13 +10,6 @@ class Macro < Block
attr_writer :evaluated

def bytecode(g)
unless @evaluated
Atomy::CodeLoader.module.define_macro(
@pattern,
@body,
Atomy::CodeLoader.compiling)
end

pos(g)

g.state.scope.nest_scope self
Expand Down
4 changes: 0 additions & 4 deletions lib/atomy/atomy.kpeg
Expand Up @@ -127,7 +127,6 @@
level3 = compose
| level2
level4 = language
| macro
| binary
| level3

Expand All @@ -142,9 +141,6 @@
| line:line < /[\+\-]?\d+/ >
{ Atomy::AST::Primitive.new(line, text.to_i) }

macro = line:line "macro" "(" wsp expression:p wsp ")" wsp block:b
{ Atomy::AST::Macro.new(line, p, b.body) }

language = "#language" wsp identifier:n { set_lang(n) } %lang.root

quote = line:line "'" level2:e
Expand Down
73 changes: 2 additions & 71 deletions lib/atomy/atomy.kpeg.rb
Expand Up @@ -1358,17 +1358,14 @@ def _level3
return _tmp
end

# level4 = (language | macro | binary | level3)
# level4 = (language | binary | level3)
def _level4

_save = self.pos
while true # choice
_tmp = apply(:_language)
break if _tmp
self.pos = _save
_tmp = apply(:_macro)
break if _tmp
self.pos = _save
_tmp = apply(:_binary)
break if _tmp
self.pos = _save
Expand Down Expand Up @@ -1534,71 +1531,6 @@ def _number
return _tmp
end

# macro = line:line "macro" "(" wsp expression:p wsp ")" wsp block:b { Atomy::AST::Macro.new(line, p, b.body) }
def _macro

_save = self.pos
while true # sequence
_tmp = apply(:_line)
line = @result
unless _tmp
self.pos = _save
break
end
_tmp = match_string("macro")
unless _tmp
self.pos = _save
break
end
_tmp = match_string("(")
unless _tmp
self.pos = _save
break
end
_tmp = apply(:_wsp)
unless _tmp
self.pos = _save
break
end
_tmp = apply(:_expression)
p = @result
unless _tmp
self.pos = _save
break
end
_tmp = apply(:_wsp)
unless _tmp
self.pos = _save
break
end
_tmp = match_string(")")
unless _tmp
self.pos = _save
break
end
_tmp = apply(:_wsp)
unless _tmp
self.pos = _save
break
end
_tmp = apply(:_block)
b = @result
unless _tmp
self.pos = _save
break
end
@result = begin; Atomy::AST::Macro.new(line, p, b.body) ; end
_tmp = true
unless _tmp
self.pos = _save
end
break
end # end sequence

set_failed_rule :_macro unless _tmp
return _tmp
end

# language = "#language" wsp identifier:n { set_lang(n) } %lang.root
def _language

Expand Down Expand Up @@ -3963,9 +3895,8 @@ def _root
Rules[:_level1] = rule_info("level1", "(call | grouped | level0(true))")
Rules[:_level2] = rule_info("level2", "(scoped_constant | postfix | level1)")
Rules[:_level3] = rule_info("level3", "(compose | level2)")
Rules[:_level4] = rule_info("level4", "(language | macro | binary | level3)")
Rules[:_level4] = rule_info("level4", "(language | binary | level3)")
Rules[:_number] = rule_info("number", "(line:line < /[\\+\\-]?0[oO][0-7]+/ > { Atomy::AST::Primitive.new(line, text.to_i(8)) } | line:line < /[\\+\\-]?0[xX][\\da-fA-F]+/ > { Atomy::AST::Primitive.new(line, text.to_i(16)) } | line:line < /[\\+\\-]?\\d+(\\.\\d+)?[eE][\\+\\-]?\\d+/ > { Atomy::AST::Literal.new(line, text.to_f) } | line:line < /[\\+\\-]?\\d+\\.\\d+/ > { Atomy::AST::Literal.new(line, text.to_f) } | line:line < /[\\+\\-]?\\d+/ > { Atomy::AST::Primitive.new(line, text.to_i) })")
Rules[:_macro] = rule_info("macro", "line:line \"macro\" \"(\" wsp expression:p wsp \")\" wsp block:b { Atomy::AST::Macro.new(line, p, b.body) }")
Rules[:_language] = rule_info("language", "\"\#language\" wsp identifier:n { set_lang(n) } %lang.root")
Rules[:_quote] = rule_info("quote", "line:line \"'\" level2:e { Atomy::AST::Quote.new(line, e) }")
Rules[:_quasi_quote] = rule_info("quasi_quote", "line:line \"`\" level2:e { Atomy::AST::QuasiQuote.new(line, e) }")
Expand Down
1 change: 0 additions & 1 deletion lib/atomy/compiler/stages.rb
Expand Up @@ -66,7 +66,6 @@ def input(file, line = 1)

def parse
Atomy::Parser.parse_file(@file) do |x|
x.evaluated = true if x.is_a?(AST::Macro)
x.evaluate(CodeLoader.context, @file)
x
end
Expand Down
66 changes: 37 additions & 29 deletions test/suite/macros.ay
Expand Up @@ -4,31 +4,64 @@ use("meta")
use("control-flow")
use("therie")

macro(~x m-foo-1): names [x]: `'~x

macro(~x m-foo-2): names [x]: `'([~x]: 1)

macro(~x m-foo-3): names [x]: `'(foo(~x) := 1)

macro(~a m-foo-4): node line

macro(~Block m-foo-7): '1
macro(~Word m-foo-7): '2
macro(~Primitive m-foo-7): '3

macro((~x + 1) m-foo-8): `[~x, 1]

macro((~x + 1) m-foo-9): `1
macro(~Binary m-foo-9): `2

macro(~Binary m-foo-10): `2
macro((~x + 1) m-foo-10): `1

macro(~x m-foo-11): `(macro(~x): '42)
(1 m-foo-110) m-foo-11

macro([~x, ~y] m-foo-12): `(macro(~x): '(macro(~y): '42))
[1 m-foo-120, 1 m-foo-121] m-foo-12
1 m-foo-120

macro(m-foo-18(~_)): '1
macro(m-foo-18(~_, ~_)): '2

macro(m-foo-19(~_, ~_)): '2
macro(m-foo-19(~_)): '1

macro(m-foo-25): '1

macro(default-having(~(x = '42))): x

describe("macros"):
describe("unique name generation"):
it("creates names via #names"):
macro(~x m-foo-1): names [x]: `'~x
var-1 = 1 m-foo-1
var-2 = 2 m-foo-1
var-1 should-be(var-1)
var-1 should: != var-2

it("decorates through block patterns"):
macro(~x m-foo-2): names [x]: `'([~x]: 1)
var-1 = 1 m-foo-2
var-1 should-be(var-1)
1 m-foo-2 should: != 2 m-foo-2

it("decorates through definition patterns"):
macro(~x m-foo-3): names [x]: `'(foo(~x) := 1)
var-1 = 1 m-foo-3
var-1 should-be(var-1)
1 m-foo-3 should: != 2 m-foo-3

-- TODO: change these to spec #node instead
describe("line information"):
it("is provided during macroexpansion via #line"):
macro(~a m-foo-4): node line
1 m-foo-4 should-be(_LINE)
2 m-foo-4 should-be(_LINE)

Expand All @@ -40,57 +73,34 @@ describe("macros"):

describe("macro definition"):
it("can pattern-match node types via constants"):
macro(~Block m-foo-7): '1
macro(~Word m-foo-7): '2
macro(~Primitive m-foo-7): '3

: 1 ; m-foo-7 should-be(1)
x m-foo-7 should-be(2)
1 m-foo-7 should-be(3)

it("can destructurually pattern-match expressions"):
macro((~x + 1) m-foo-8): `[~x, 1]
(2 + 1) m-foo-8 should-be([2, 1])

it("matches destructuring before constants"):
macro((~x + 1) m-foo-9): `1
macro(~Binary m-foo-9): `2

macro(~Binary m-foo-10): `2
macro((~x + 1) m-foo-10): `1

(2 + 1) m-foo-9 should-be(1)
(2 + 1) m-foo-10 should-be(1)

it("can specify defaults for arguments"):
macro(default-having(~(x = '42))): x
default-having() should-be(42)
default-having(41) should-be(41)

it("can define macros in their expansion"):
macro(~x m-foo-11): `(macro(~x): '42)
(1 m-foo-110) m-foo-11
1 m-foo-110 should-be(42)
: 2 m-foo-110 ; should-raise(NoMethodError)

it("can define macros that define macros in their expansion in their expansion"):
macro([~x, ~y] m-foo-12): `(macro(~x): '(macro(~y): '42))
[1 m-foo-120, 1 m-foo-121] m-foo-12
1 m-foo-120
1 m-foo-121 should-be(42)
: 2 m-foo-121 ; should-raise(NoMethodError)

describe("varying argument lengths"):
it("tries higher required argument counts first"):
macro(m-foo-18(~_)): '1
macro(m-foo-18(~_, ~_)): '2

m-foo-18(1) should-be(1)
m-foo-18(1, 2) should-be(2)

macro(m-foo-19(~_, ~_)): '2
macro(m-foo-19(~_)): '1

m-foo-19(1) should-be(1)
m-foo-19(1, 2) should-be(2)

Expand Down Expand Up @@ -130,8 +140,6 @@ describe("macros"):
m-foo-24 should-be([0, 1])

it("overrides non-let'd macros"):
macro(m-foo-25): '1

let-macro(m-foo-25 = '2):
m-foo-25 should-be(2)

Expand Down
5 changes: 3 additions & 2 deletions test/suite/quotes.ay
Expand Up @@ -2,17 +2,18 @@ use("core")
use("define")
use("therie")

macro(q-foo-0): '42
macro(q-foo-1): '['2, '3]

describe("quasiquotation"):
it("constructs an expression"):
`1 should-be('1)
`(1 + 1) should-be('(1 + 1))

it("expands through unquotes"):
macro(q-foo-0): '42
`(1 + ~q-foo-0) should-be('(1 + 42))

it("expands through splice unquotes"):
macro(q-foo-1): '['2, '3]
`[1, ~*q-foo-1] should-be('[1, 2, 3])

describe("unquotes"):
Expand Down

0 comments on commit 559dc57

Please sign in to comment.