Skip to content

Instantly share code, notes, and snippets.

@darkf
Last active December 10, 2015 16:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save darkf/4459282 to your computer and use it in GitHub Desktop.
Save darkf/4459282 to your computer and use it in GitHub Desktop.
Possum macro proposal
loop for i to 10
  print i
end

In the parser we encounter loop. It's not a function or a special form, but it is a macro. We pass it off to the macro transformer function, which spits out an AST node, and we add that, instead of the original (illegal) code.

The loop macro can be defined like:

defmacro loop is
	atom is for
	atom var
	atom is to
	expr end-i
	expr-body rest

	body
		begin
			print concat concat atom->str " is: " quote var
			defun iter n is
				if != n 0 then
					begin
						set! var end-i
						rest (* substitute in the loop body *)
					end
					iter - n 1
				end
			end
			iter end-i
		end
end

Macros can use other defined macros, such as here with begin.

Internally, macros will be transformer functions of a form:

val macroTransformer (toks : Consumable) : expr

That is, take a stream of tokens as input, and return an AST node. However, this transformer will need to make the required calls to parse in order to ensure macro uses in their bodies are properly parsed.

In the interpreter, we store ASTs instead of token streams, so this works out. The parse function will take in streams of tokens and output an AST node. If we parse the body of the macro above, we'll evaluate any macros it uses and splice it into the resulting AST, which will then be stored internally for the macro.

type macro = {name : string; transformer : (Consumable -> expr) }

where transformer is a closure that handles the body AST.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment