Skip to content

Instantly share code, notes, and snippets.

@tangentstorm
Created March 10, 2022 03:32
Show Gist options
  • Save tangentstorm/bd9c27c45aff04a43050091003e1f4be to your computer and use it in GitHub Desktop.
Save tangentstorm/bd9c27c45aff04a43050091003e1f4be to your computer and use it in GitHub Desktop.
simple command language parser for jprez in gdscript (godot).
# i wrote this, and it works, but then i decided to just use Godot's own Expression parser.
# https://docs.godotengine.org/en/latest/tutorials/scripting/evaluating_expressions.html
func _examples():
test('@title["the deck"]')
test('@show["jp-editor"; 0]')
test('@ed.xy[ 0 5]')
func test(cmd:String):
if cmd.begins_with('@'):
print(cmd)
cmd = cmd.right(1)
var e = EventParser.new()
var toks = e.scan(cmd)
print(toks)
var ast = e.parse(cmd)
print(ast)
else: print("bad command: %s" % cmd)
class EventParser:
enum TOK { SPACE, LBRACK, RBRACK, SEMI, NUMS, STR, NAME, END }
const RX = '(?<skip>\\s+)|\\[|\\]|;|(\\d+(\\s+\\d+))|"([^"]|[\\\\]\\")*"|(\\.|\\w)+'
var rxToken:RegEx
var tokens: Array # array of tokens
var ii: int # counter
var tt: int # current token type
func _init():
rxToken = RegEx.new(); rxToken.compile(RX)
func tok_type(tok:String):
match tok[0]:
' ': return TOK.SPACE
'[': return TOK.LBRACK
']': return TOK.RBRACK
';': return TOK.SEMI
'"': return TOK.STR
'0','1','2','3','4','5','6','7','8','9': return TOK.NUMS
_: return TOK.NAME
func scan(cmd:String):
var toks = []
for m in rxToken.search_all(cmd):
if m.get_string("skip"): continue # drop spaces
toks.push_back(m.get_string())
return toks
func parse(cmd:String):
ii = 0; tokens = scan(cmd)
return _parse_call()
func _tt():
if ii >= len(tokens): return TOK.END
tt=tok_type(tokens[ii])
return tt
func _expected_msg(kinds:Array)->String:
var res = ''
if kinds:
for k in kinds: res += ' | ' + TOK.keys()[k]
res = res.right(3)
return res
func _expect(kinds:Array):
if ii >= len(tokens):
printerr('unexpected end of input. expecting ', _expected_msg(kinds))
else:
var tt = tok_type(tokens[ii])
if tt in kinds: return tokens[ii]
else:
var tk = TOK.keys()
printerr('error: expecting %s but got %s (%s)' % [_expected_msg(kinds), tk[tt], tokens[ii]])
func _take(kinds:Array):
var res = _expect(kinds)
ii += 1
return res
func _parse_call():
var ident = _take([TOK.NAME])
_take([TOK.LBRACK])
var args = []
while _tt() != TOK.RBRACK:
match tt:
TOK.STR:
# !! be lazv and borrow godot's string parser
var e = Expression.new()
assert(OK==e.parse(tokens[ii]), "bad string: %s" % tokens[ii])
args.push_back(e.execute())
TOK.NUMS:
var nums = tokens[ii].split_floats(' ')
args.push_back(nums[0] if len(nums)==1 else nums)
_: assert(false, "unexpected %s inside a call!" % TOK.keys()[tt])
ii += 1
if _expect([TOK.RBRACK, TOK.SEMI]) == ';': ii+=1
return [ident, args]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment