Skip to content

Instantly share code, notes, and snippets.

@fowlmouth
Created November 25, 2013 23:53
Show Gist options
  • Save fowlmouth/7651030 to your computer and use it in GitHub Desktop.
Save fowlmouth/7651030 to your computer and use it in GitHub Desktop.
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