• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Jass² - A Jass Parser written in vJass

Status
Not open for further replies.

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539

Jass² - A Jass Parser written in vJass


Hacked over the last few days so probably buggy. Equiped with an ad-hoc printer.
Documentation may come sometime. Some examples follow though.


How to parse Toplevel:
JASS:
local string j = "

type bla extends integer

globals
    constant integer x = 3
    integer y
    integer array z
endglobals

function foo takes integer x, bla foo returns real
set x = y
return lol
endfunction
"

local Jass_Parser p = Jass_Parser.create(j)
local Jass_List e = p.parseProgram()

loop
exitwhen e == Jass_Nil
    call BJDebugMsg(Jass_Toplevel(e.head).toString())
    set e = e.tail
endloop

Parsing a single expression:
JASS:
local string j = "a * b[3] + foo(bar, $ff)"
local Jass_Parser p = Jass_Parser.create(j)

call BJDebugMsg(p.parseExpr().toString())

Here's the code: GitHub - lep/jassjass: Jass parser written in vJass
 
Last edited:
Level 3
Joined
May 19, 2010
Messages
35
Very cool :thumbs_up:. Now we are one step closer to a full ingame jass compiler. The only piece that's really missing is the jumptable.

An ingame REPL loop or even a full editor would massively save time with testing/debugging. When I have time I'll start writing a bytecode generator.
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
I guess a REPL should be possible, although it seems that the parser currently doesn't recycle Tokens, i.e one should be careful of running out of struct instances.
I am not sure how an in-game editor would work though.

Very impressive indeed.
Yes, there is no memory management at the moment. destroying tokens should be rather easy though (e.g. done in nextsym).

And in general adding the appropiate destructors is very automatic work.
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
Pretty awesome as an experiment. :D

Is this a stepping stone to something more? Or for fun? It is really crazy that you got it working at all, especially in such short time. Cool stuff.
I don't have concrete plans. I hope that other people can use this. Using this should point out any flaws.
Maybe i'll write a jass-interpreter in jass for funs, since it should be rather easy with this.
 
Level 13
Joined
Nov 7, 2014
Messages
571
Here are two functions that should be able to deallocate the parse tree's nodes (modulo any bugs):
JASS:
library JassDestroy uses Jass

private function unimplemented takes string s returns nothing
    call BJDebugMsg("unimplemented: " + s)
    if 1 / 0 == 0 then
    endif
endfunction

globals
    private Jass_List array list_nodes
    private integer list_nodes_count

    private Jass_Ast array ast_nodes
    private integer ast_nodes_count

    private Jass_Ast array stack_nodes
    private integer stack_nodes_count
endglobals
private function destroy_ast_impl takes Jass_Ast ast_node returns nothing
    local Jass_List list_node
    local Jass_Expr expr_node
    local integer ty
    local integer i

    if ast_node == 0 then
        return
    endif

    set list_nodes_count = 0
    set ast_nodes_count = 0
    set stack_nodes_count = 0

    set stack_nodes_count = stack_nodes_count + 1
    set stack_nodes[stack_nodes_count] = ast_node

    loop
        exitwhen stack_nodes_count == 0
        set ast_node = stack_nodes[stack_nodes_count]
        set stack_nodes_count = stack_nodes_count - 1

        set ast_nodes_count = ast_nodes_count + 1
        set ast_nodes[ast_nodes_count] = ast_node

        set ty = ast_node.getType()

        if false then

        // elseif ty == Jass_Toplevel.typeid then
        //     base struct

        // elseif ty == Jass_VarDecl.typeid then
        //     base struct

        elseif ty == Jass_Globals.typeid then
            set list_node = Jass_Globals(ast_node).glbls

            loop
                exitwhen list_node == 0
                set list_nodes_count = list_nodes_count + 1
                set list_nodes[list_nodes_count] = list_node

                set ast_nodes_count = ast_nodes_count + 1
                set ast_nodes[ast_nodes_count] = Jass_VarDecl(list_node.head)

                set list_node = list_node.tail
            endloop

        elseif ty == Jass_TypeDef.typeid then
            // no struct references

        // elseif ty  == Jass_TypeAndName.typeid then
        //     unreachable

        elseif ty == Jass_Native.typeid then
            set list_node = Jass_Native(ast_node).params

            loop
                exitwhen list_node == 0
                set list_nodes_count = list_nodes_count + 1
                set list_nodes[list_nodes_count] = list_node

                set ast_nodes_count = ast_nodes_count + 1
                set ast_nodes[ast_nodes_count] = Jass_TypeAndName(list_node.head)

                set list_node = list_node.tail
            endloop

        elseif ty == Jass_Function.typeid then
            set list_node = Jass_Function(ast_node).params

            loop
                exitwhen list_node == 0
                set list_nodes_count = list_nodes_count + 1
                set list_nodes[list_nodes_count] = list_node

                set ast_nodes_count = ast_nodes_count + 1
                set ast_nodes[ast_nodes_count] = Jass_TypeAndName(list_node.head)

                set list_node = list_node.tail
            endloop

            set list_node = Jass_Function(ast_node).body

            loop
                exitwhen list_node == 0
                set list_nodes_count = list_nodes_count + 1
                set list_nodes[list_nodes_count] = list_node

                set stack_nodes_count = stack_nodes_count + 1
                set stack_nodes[stack_nodes_count] = Jass_Statement(list_node.head)

                set list_node = list_node.tail
            endloop

        elseif ty == Jass_ArrayDecl.typeid then
            // no struct references

        elseif ty == Jass_NormalDecl.typeid then
            set expr_node = Jass_NormalDecl(ast_node).init
            if expr_node != 0 then
                set stack_nodes_count = stack_nodes_count + 1
                set stack_nodes[stack_nodes_count] = expr_node
            endif

        // elseif ty == Jass_Statement.typeid then
        //     base struct

        // elseif ty == Jass_Var.typeid then
        //     base struct

        // elseif ty == Jass_Expr.typeid then
        //     base struct

        elseif ty == Jass_ArrayVar.typeid then
            set stack_nodes_count = stack_nodes_count + 1
            set stack_nodes[stack_nodes_count] = Jass_ArrayVar(ast_node).idx

        elseif ty == Jass_NormalVar.typeid then
            // no struct references

        elseif ty == Jass_Set.typeid then
            set stack_nodes_count = stack_nodes_count + 1
            set stack_nodes[stack_nodes_count] = Jass_Set(ast_node).var

            set stack_nodes_count = stack_nodes_count + 1
            set stack_nodes[stack_nodes_count] = Jass_Set(ast_node).value

        elseif ty == Jass_Call.typeid then
            set list_node = Jass_Call(ast_node).args

            loop
                exitwhen list_node == 0
                set list_nodes_count = list_nodes_count + 1
                set list_nodes[list_nodes_count] = list_node

                set stack_nodes_count = stack_nodes_count + 1
                set stack_nodes[stack_nodes_count] = Jass_Expr(list_node.head)

                set list_node = list_node.tail
            endloop

        elseif ty == Jass_CallStmt.typeid then
            set stack_nodes_count = stack_nodes_count + 1
            set stack_nodes[stack_nodes_count] = Jass_Call(Jass_CallStmt(ast_node).cll)

        elseif ty == Jass_Local.typeid then
            set stack_nodes_count = stack_nodes_count + 1
            set stack_nodes[stack_nodes_count] = Jass_VarDecl(Jass_Local(ast_node).vd)

        elseif ty == Jass_Loop.typeid then
            set list_node = Jass_Loop(ast_node).body

            loop
                exitwhen list_node == 0
                set list_nodes_count = list_nodes_count + 1
                set list_nodes[list_nodes_count] = list_node

                set stack_nodes_count = stack_nodes_count + 1
                set stack_nodes[stack_nodes_count] = Jass_Statement(list_node.head)

                set list_node = list_node.tail
            endloop

        elseif ty == Jass_If.typeid then
            set stack_nodes_count = stack_nodes_count + 1
            set stack_nodes[stack_nodes_count] = Jass_If(ast_node).cond

            set list_node = Jass_If(ast_node).thenBody

            loop
                exitwhen list_node == 0
                set list_nodes_count = list_nodes_count + 1
                set list_nodes[list_nodes_count] = list_node

                set stack_nodes_count = stack_nodes_count + 1
                set stack_nodes[stack_nodes_count] = Jass_Statement(list_node.head)

                set list_node = list_node.tail
            endloop

            set list_node = Jass_If(ast_node).elseBody

            loop
                exitwhen list_node == 0
                set list_nodes_count = list_nodes_count + 1
                set list_nodes[list_nodes_count] = list_node

                set stack_nodes_count = stack_nodes_count + 1
                set stack_nodes[stack_nodes_count] = Jass_Statement(list_node.head)

                set list_node = list_node.tail
            endloop

        elseif ty == Jass_Exitwhen.typeid then
            set stack_nodes_count = stack_nodes_count + 1
            set stack_nodes[stack_nodes_count] = Jass_Exitwhen(ast_node).cond

        elseif ty == Jass_Return.typeid then
            set stack_nodes_count = stack_nodes_count + 1
            set stack_nodes[stack_nodes_count] = Jass_Return(ast_node).value

        elseif ty == Jass_Integer.typeid then
            // no struct references

        elseif ty == Jass_Rawcode.typeid then
            // no struct references

        elseif ty == Jass_Real.typeid then
            // no struct references

        elseif ty == Jass_String.typeid then
            // no struct references

        elseif ty == Jass_Code.typeid then
            // no struct references

        elseif ty == Jass_Null.typeid then
            // no struct references

        elseif ty == Jass_Bool.typeid then
            // no struct references

        elseif ty == Jass_CallExpr.typeid then
            set stack_nodes_count = stack_nodes_count + 1
            set stack_nodes[stack_nodes_count] = Jass_CallExpr(ast_node).cll

        elseif ty == Jass_VarExpr.typeid then
            set stack_nodes_count = stack_nodes_count + 1
            set stack_nodes[stack_nodes_count] = Jass_VarExpr(ast_node).var

        else
            call unimplemented("ty: " + I2S(ty))

        endif
    endloop

    set i = 1
    loop
        exitwhen i > ast_nodes_count
        call ast_nodes[i].destroy()
        set i = i + 1
    endloop

    set i = 1
    loop
        exitwhen i > list_nodes_count
        call list_nodes[i].destroy()
        set i = i + 1
    endloop
endfunction

globals
    private Jass_Ast ast_node_ef
endglobals
private function destroy_ast_ef takes nothing returns nothing
    call destroy_ast_impl(ast_node_ef)
endfunction

// set expr|stmt|decl = Jass_Parser.create(script).parseExpr|Statement|Decl()
// call JassDestroy_destroy_ast(expr|stmt|decl)
//
public function destroy_ast takes Jass_Ast ast_node returns nothing
    set ast_node_ef = ast_node
    call ForForce(bj_FORCE_PLAYER[0], function destroy_ast_ef)
endfunction

globals
    private Jass_List array top_level_nodes
    private integer top_level_nodes_count
endglobals

// set program = Jass_Parser.create(script).parseProgram()
// call JassDestroy_destroy_program(program)
//
public function destroy_program takes Jass_List list_node returns nothing
    local integer i
    set top_level_nodes_count = 0

    loop
        exitwhen list_node == 0
        set top_level_nodes_count = top_level_nodes_count + 1
        set top_level_nodes[top_level_nodes_count] = list_node

        call destroy_ast(Jass_Ast(list_node.head))

        set list_node = list_node.tail
    endloop

    set i = 1
    loop
        exitwhen i > top_level_nodes_count
        call top_level_nodes[i].destroy()
        set i = i + 1
    endloop
endfunction

endlibrary


PS: the parser seems to not like functions that don't take any parameters:
JASS:
function foo takes nothing returns nothing
endfunction
error: Parser error: Expected IDENT but got NOTHING
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
[...]
PS: the parser seems to not like functions that don't take any parameters:
JASS:
function foo takes nothing returns nothing
endfunction
error: Parser error: Expected IDENT but got NOTHING
Fixed. I put my code on Github. See the first post.
 
Level 13
Joined
Nov 7, 2014
Messages
571
It seems that the struct NormalDecl doesn't have a "is-constant" flag.

When method nextsym is first called, cur == 0 so cur.destroy() reports an error, I suppose you can initialize it with a dummy Token?

PS: operator `or` having higher precedence than `and` seems exactly the opposite of what other languages do =)?
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
It seems that the struct NormalDecl doesn't have a "is-constant" flag.

When method nextsym is first called, cur == 0 so cur.destroy() reports an error, I suppose you can initialize it with a dummy Token?
Fixed.
 
Status
Not open for further replies.
Top