Operator Precendence Parsing Blues

BlitzMax Forums/BlitzMax Programming/Operator Precendence Parsing Blues

AntonyWells(Posted 2005) [#1]
I've been using OPP to evaluate expressions. It works fine but I'm not sure how to treat the combintions invoked by And/Or/Not/Greater Than/Less Than/Equal.

Does anyone know how? Someone here pointed me in the direction of opp so I'm hoping you're reading, and have already solved it.

Here's the code. Just type an expression and it will give you the result. Left and right () are allowed like normal blitz expressions.


Strict
Global toke_id=0

Type deli
	Field id
	Global cur:TList
	Field tag
	
	Function deliTag( deli$,ntag )
		Local del = deli[0]
		For Local d:deli = EachIn cur
			If d.id = del
				d.tag=ntag
				
			End If
		Next
	End Function
	
	Function setcurrent( d:TList )
		cur = d
	End Function 
	
	Function isDel( in )
		
		For Local de:Deli = EachIn cur
			If de.id = in Return True
		Next
		Return False
	
	End Function

	Function hastag( in )
		
		For Local de:Deli = EachIn cur
			If de.id = in
				If de.tag<>0 Return True 
			End If
		Next
		Return False
	End Function
		
	Function Create:Deli( word )
		Local out:deli = New deli
		out.id = word
		Return out
	End Function
	
	Method debug()
		Print "Delimiter Debug spool:"
		Print "Actual id:"+id
		Print "Textural referece "+Chr(id)
	End Method
		
	Function CreateDelis:TList( words$ )
			Local dli:TList = CreateList()
		 	Local wl = Len(words)
			For Local j=1 To wl
				Local d:Deli = deli.create( words[j] )
				dli.addlast( d )
			Next
			cur = dli 
			Return dli
	End Function
	
	Function debugList( in:TList )
		For Local v:Object = EachIn in
			Local d:Deli = Deli(v)
			d.debug()
		Next
	End Function
	
End Type

Type tokens
	
	Field tokes:TList
	Field toke:Object[]
	Field ctoke
	
	Method set( tok:TList )
		tokes = tok
		toke = tok.toarray()
	End Method
	
	Method NextToken:Token()
		ctoke:+1
		If ctoke>tokes.count()
			ctoke=tokes.count()
			Return Null
		End If
		Return Token(tokes.valueatindex(ctoke-1))
	End Method
	
	Function create:Tokens()
		Return New tokens
	End Function
	Method New()
		tokes = CreateList()
	End Method
	
	Method add( itoke:Token )
		tokes.addlast( itoke )
		toke = tokes.toarray()
	End Method		
	
	Method debug()
		For Local t:Token = EachIn tokes
			Print t.id
		Next
	End Method
	
		
End Type


Type token
	Field id$

	Function Create:Token( toke$ )
		Local out:token = New token
		out.id = toke
		Return out
	End Function
	
	Method getlut()
		Select id
			Case "+"
				Return plus
			Case "-"
				Return minus
			Case "*"
				Return times
			Case "/"
				Return devide
			Case "("	
				Return scope_entry
			Case "}"
				Return scope_exit
		End Select
		Return 0
	
	End Method
	
	
	Function CreateTokens:TList( tokes$ )
		Local out:TList = CreateList()
			Local tc = Len(tokes)
			For Local j=1 To tc
				Local nt:token = token.create( tokes[j] )
			out.addlast( nt )
				nt.debug()
			Next
			'Print "Approx " + CountList(out)+"Tokens were defined'
		Return out
	End Function
	
	Method debug()
		Print "Token symbolic info"

		Print id
	End Method
	
End Type

Type tokenizer
	
	Function create:tokenizer()
		Return New tokenizer
	End Function
	
	Method Tokenize:Tokens( text:String )
		text = " "+text+" "
		Local tk:Tokens = tokens.create()
		Local lvc,fvc
		lvc=-1
		fvc=-1
		Local tl= Len(text)
		Local snc=0,igdot=0
		For Local j=0 To tl-1
			Local li = text[j]
			
			If li = 39
				If snc=0
					fvc=j+1
					snc=1
				Else
					lvc=j-1
					snc=0
				EndIf
			EndIf
			
			If li>47 And li<58
				igdot = True
			End If
			
			Local skpdel=0
			If snc=0
			If deli.isdel( li )
				If igdot = True
					If li = 46
						skpdel=True
					End If
				EndIf
				If Not skpdel
				
				If	lvc<>fvc And fvc<>-1
					If lvc<fvc lvc=fvc
					Local tst$ = text[fvc..lvc+1]
		'			Print "Token:"+tst
					tk.add( token.create( tst ) )
					fvc=-1
					lvc=-1
				EndIf
					If deli.hastag( li )
						tk.add( token.create( Chr(li) ) )
						Print "deli "+li+" Had a tag"
					EndIf
					igdot=False
				End If	
			Else
				If fvc=-1
					fvc=j
				Else 
					lvc=j
				EndIf
			EndIf
			EndIf
			
		Next
		Return tk
	End Method
	
End Type

Const Scope_Entry=1,Scope_Exit=2
Const Assign = 3,Minus=4,Plus=5,Times=7,Devide=8
Const classentry=9,numeric=10,value=11,tend=12
Const reduce=1,shift=2

Global act:Int[,] = New Int[20,20]
act[Plus,plus] = reduce
act[plus,minus] = reduce
act[plus,times] = shift
act[plus,devide] = shift
act[plus,scope_entry] = shift
act[plus,scope_Exit] = reduce
act[plus,tend] = reduce
act[minus,plus] = reduce
act[minus,minus] = reduce
act[minus,times] = shift
act[minus,devide] = shift
act[minus,scope_entry] = shift
act[minus,scope_exit] = reduce
act[minus,tend] = reduce
act[times,plus] = reduce
act[times,minus] = reduce
act[times,times] = reduce
act[times,devide] = reduce
act[times,scope_entry] = shift
act[times,scope_Exit] = reduce
act[times,tend] = reduce
act[devide,plus] = reduce
act[devide,minus] = reduce
act[devide,Times] = reduce
act[devide,devide] = reduce
act[devide,scope_entry] = shift
act[devide,scope_exit] = reduce
act[devide,tend ] = reduce
act[scope_entry,plus] = shift
act[scope_entry,minus] = shift
act[scope_entry,times] = shift
act[scope_entry,devide] = shift
act[scope_entry,scope_Entry] = shift
act[scope_Entry,scope_exit] = shift
act[scope_Exit,plus] = reduce
act[scope_exit,minus] = reduce
act[scope_exit,devide] = reduce
act[scope_exit,times] = reduce
act[scope_Exit,scope_entry] = reduce
act[scope_exit,scope_exit] = reduce
act[scope_exit,tend] = reduce
act[tend,plus] = shift
act[tend,minus] = shift
act[tend,times] = shift
act[tend,devide] = shift
act[tend,scope_entry] = shift
act[tend,tend] = tend

Const delimiters:String = "{}[]';;!@#$%^&*()_+=-/.,?{><\|<>	` "
Global maindel:TList = deli.createdelis( delimiters )
deli.delitag( "(",Scope_Entry )
deli.delitag( ")",Scope_Exit )
deli.delitag( "=",Assign )
deli.delitag( "-",Minus)
deli.delitag( "+",Plus)
deli.delitag( "*",Times)
deli.delitag( "/",Devide)
deli.delitag( ".",ClassEntry)
deli.delitag( "#",Numeric)
deli.delitag( ";",TEnd)

Type Stack
	Field Val:Object[5000]
	Field at
	Method Push(obj:Object)
		val[at]=obj
		at:+1
	End Method
	Method Pop:Object()
		at:-1
		If at<0 at=0
		Return val[at]
	End Method
	Method Get:Object()
		If at>0	Return val[at-1]
	End Method
	Method size()
		Return at
	End Method
	
End Type


Type ExpressionParser
	Field Val:Double[2000],valTop
	Field Opr:String[2000],oprTop
	
	Field tokes:Tokens
	Method New()
	
	End Method
	
	Method Parse!( Expr:String )
		
		Local tk:Tokenizer = New Tokenizer
		tokes:Tokens = tk.tokenize( expr+";" )
		valtop=-1
		oprtop=0
		opr[oprtop]=tend
		If nxttok() Return 1
		Repeat
		
			
			If tok = value
				If shiftstack() Return 1
				 Continue
			End If
		
			Select act[Int(opr[oprtop]),tok]
				Case reduce
					If reducestack() Return 1
					
				Case shift
					If shiftstack() Return 1
				Case tend
					If valtop<>0
						Print "Syntax error"
					End If
					Return val[valtop]
				Default
					Print "Unknown combo:"
					Print opr[oprtop]+":"+tok
					End
			End Select
			
			
		Forever					
	End Method
	Field debug1:String,debug2:String
	Method nxtTok()
		Local tokey:Token = tokes.nextToken()
		If tokey=Null Return 1
		debug1 = tokey.id
		getTok( tokey.id )
		
	End Method
	
	Field tokval!,tok
	Method GetTok(Toke:String)
		
		Select toke
			Case "+"
				tok=plus
			Case "-"
				tok=minus
				
			Case "*"
				tok=times
			Case "/"
				tok=devide
			Case "("
				tok=scope_entry
			Case ")"
				tok=scope_exit
			Case ";"
				tok=tend
			Default 
				tok=value
				tokval=Double(toke)
		End Select
			
	End Method
		
	Method shiftstack()
		If tok = value
			valTop:+1
			val[valTop]=tokval
		Else
			oprTop:+1
			opr[oprtop]=String(tok)
		EndIf
		If nxtTok() Return 1
	End Method
	
	Method Reducestack()
		Select Int(opr[oprtop])
			Case plus
				val[valtop-1]:+val[valtop]
				valtop:-1
			Case minus
				val[valtop-1]:-val[valtop]
				valtop:-1
			Case times
				val[valtop-1]:*val[valtop]
				valtop:-1
			Case devide
				val[valtop-1]:/val[valtop]
				valtop:-1
			Case scope_exit
				oprtop:-1
		End Select
		oprtop:-1
	End Method

	
End Type

Print "Running."
While True
	Local ep:ExpressionParser = New ExpressionParser
	Print "Answer:"+ep.Parse(Input())
Wend







rdodson41(Posted 2005) [#2]
Here is a list of all the C operators, their precedence, and associativity. Take a look at that maybe it wil help.
http://www.difranco.net/cop2220/op-prec.htm


Grey Alien(Posted 2005) [#3]
from an algebra point of view you use BEDMAS (Brackets, exponentials, division, multiplication, addition, subtraction.


AntonyWells(Posted 2005) [#4]
Yeah I get the order of precendence, i'm just not sure of the combinations involved. OPP uses a two dimensional look up table based on the stack operator and the input operator. So +* and *+ give different results meaning it's hard to guess work it based on sheer precendence.
Thanks anyway.