Created
November 25, 2013 23:53
-
-
Save fowlmouth/7651030 to your computer and use it in GitHub Desktop.
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
import fowltek/parsetools/g_parse, fowltek/parsetools/g_lex | |
import strutils | |
const | |
tkCascade = tkComma | |
tkKeyword = tkUser1 | |
tkBlockStart = tkBracketOpen | |
tkBlockEnd = tkBracketClose | |
type | |
PNode = ref object{.inheritable.} | |
PStmts = ref object of PNode | |
stmts: seq[PNode] | |
PIdentNode = ref object of PNode | |
name: string | |
PMessage = ref object of PNode | |
recv: PNode | |
msg: string | |
args: seq[PNode] | |
PBlock = ref object of PNode | |
args: seq[string] | |
code: PStmts | |
template litNode (name, ty): stmt = | |
type name = ref object of PNode | |
val: ty | |
litNode PIntLit, int | |
litNode PStrLit, string | |
litNode PFltLit, float | |
type | |
PParser = var TParser[PNode] | |
proc parseStatements (P: PParser): PStmts | |
proc parseExpr (P: PParser): PNode | |
proc parse (input: string): PNode = | |
var P = newParser(parseStatements) | |
P.lex.add_posthook do(L: var TLexer; tok: var TToken)->bool: | |
if tok.kind == tkIdent and L.hasCharacters(0) and L.nextChar(0) == ':': | |
tok.kind = tkKeyword | |
result = true | |
inc L | |
tok.sval.add ':' | |
P.parse input | |
# unary > binary > keyword | |
# x f1 f2 | |
# ((x f1) f2) | |
proc parseBlock (P: PParser): PBlock = | |
P.consumeTok tkBlockStart | |
result = PBlock(args: @[]) | |
if P.currentTok.isOperator("|"): | |
P.consumeTok | |
while P.present(tkIdent): | |
result.args.add P.consumeTok.sval | |
P.consumeStrTok tkOperator, "|" | |
result.code = P.parseStatements | |
P.consumeTok tkBlockEnd | |
proc parseUnaryMessage (P: PParser; recv: PNode): PNode = | |
result = recv | |
while P.present(tkIdent): | |
result = PMessage(recv: result, msg: P.consumeTok.sval, args: @[]) | |
proc parseUnaryExpr (P: PParser): PNode = | |
var recv: PNode | |
case P.currentTok.kind | |
of tkInt: | |
recv = PIntLit(val: P.consumeTok.ival) | |
of tkFloat: | |
recv = PFltLit(val: P.consumeTok.fval) | |
of tkDqString, tkSqString: | |
recv = PStrLit(val: P.consumeTok.sval) | |
of tkIdent: | |
recv = PIdentNode(name: P.consumeTok.sval) | |
of tkParenOpen: | |
P.consumeTok | |
P.skipNewlines | |
recv = P.parseExpr | |
P.skipNewlines | |
P.consumeTok tkParenClose | |
of tkBlockStart: | |
recv = P.parseBlock | |
else: | |
return | |
result = P.parseUnaryMessage(recv) | |
proc parseBinaryMessage (P: PParser; recv: PNode): PNode = | |
result = recv | |
while P.present(tkOperator): | |
let | |
op = P.consumeTok.sval | |
right = P.parseUnaryExpr | |
result = PMessage(recv: result, msg: op, args: @[right]) | |
proc parseBinaryExpr (P: PParser): PNode = | |
result = P.parseUnaryExpr | |
result = P.parseBinaryMessage(result) | |
proc parseKeywordMessage (P: PParser; recv: PNode): PNode = | |
result = recv | |
var | |
msg = "" | |
args: seq[PNode] = @[] | |
while P.present(tkKeyword): | |
msg.add P.consumeTok.sval | |
args.add P.parseBinaryExpr | |
if args.len > 0: | |
result = PMessage( | |
recv: result, | |
msg: msg, | |
args: args) | |
proc parseKeywordExpr (P: PParser): PNode = | |
result = P.parseBinaryExpr | |
result = P.parseKeywordMessage(result) | |
proc parseCascadeExpr (P: PParser): PNode = | |
result = P.parseKeywordExpr | |
while not(result.isNil) and P.present(tkCascade): | |
P.consumeTok | |
if P.present(tkKeyword): | |
result = P.parseKeywordMessage(result) | |
elif P.present(tkOperator): | |
result = P.parseBinaryMessage(result) | |
elif P.present(tkIdent): | |
result = P.parseUnaryMessage(result) | |
proc parseExpr (P: PParser): PNode = | |
P.parseCascadeExpr | |
proc parseStatements (P: PParser): PStmts = | |
while(var s = P.parseExpr; not s.isNil): | |
if result.isNil: | |
result = PStmts(stmts: @[]) | |
result.stmts.add s | |
if P.currentTok.kind != tkEOF: | |
echo "Warning: Left over tokens!" | |
for i in P.tkIndex .. P.tokens.high: | |
echo P.tokens[i] | |
method render (N: PNode): string = "/* unk node */" | |
method render (N: PIntLit): string = | |
$N.val | |
method render (N: PStrLit): string = | |
N.val | |
method render (N: PFltLit): string = | |
formatFloat(N.val, ffDecimal, 5) | |
method render (N: PIdentNode): string = | |
N.name | |
method render (N: PMessage): string = | |
result = "(" | |
result.add N.recv.render | |
result.add ' ' | |
result.add N.msg | |
if N.args.len > 0: | |
result.add " [" | |
result.add N.args.map(proc(x: PNode): string = x.render).join(", ") | |
result.add ']' | |
result.add ')' | |
method render (N: PStmts): string = | |
result = "" | |
for S in N.stmts: | |
result.add S.render | |
result.add ".\L" | |
when isMainModule: | |
import rdstdin | |
var input: string | |
while readLineFromStdin(">> ", input): | |
input.parse.render.echo |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment