How to create an 'interpreter'? Or some other term closely related..

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
vyraefi
Prole
Posts: 2
Joined: Sun Jul 05, 2020 3:20 am

How to create an 'interpreter'? Or some other term closely related..

Post by vyraefi »

My Background (skip if uninterested): Hello! I am new to game development, slightly familiar with programming, and I have a lot of questions. Because of this, and the fact I'm due to go to college in August, I think this forum will be my coding home for now haha.

Problem part 1: So I made a conscious effort to learn Lua for Love despite my first project being a visual novel. There are alternatives like Ren'Py, but I want the flexibility that a game framework can offer. One of the things I gave up in this choice, however, is Ren'Py's built-in *interpreter*? I don't know the proper term which is properly my biggest concern. What do you call that? A part of a program where you put a script (or any kind of file) and it *interprets* it (I think?). This would then display the text in a formatted, separated way. I couldn't find the term online so I'd like to know! If the question can't necessarily be answered, I would appreciate knowing the name of this concept. It could help me into researching and learning how to do one myself.

Problem part 2: How would you go about writing an *interpreter* written in Love/Lua for displaying pieces of text (one - by - one) in a program? It's not just going through a table of string via using love.graphics.print(), it would also detect things besides just plain text in a txt or lua file.

Is there books or articles on this sort of thing to read? I don't mind if they're not Lua specific since I'm interested in learning conceptually. I want an idea of how one would go about it. Sorry for being vague and making this question unnecessarily complicated. Any advice or resources would be appreciated. I want to make games! But I might have made a bad first choice of game haha..
User avatar
zorg
Party member
Posts: 3465
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: How to create an 'interpreter'? Or some other term closely related..

Post by zorg »

Hi and welcome to the forums!

I think you could call it an intrpreter, or maybe a script parser or even a scripting engine... to be honest, it's not the name that matters, but what it does.

Now, implementing one is as hard as you want to make it; there is no one solution. Also, while löve doesn't have one of these inbuilt, depending on how much you know coding-wise, a simple design is easy to do.

Also, for what is worth, others might say to just use ren'py but i'd disagree simply because löve's more capable... and that i have seen visusl novel like interfaces in projects done by others here before.

I'm on mobile atm so unfortunately i cant link you any examples nor learning materials. :c
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
duaner
Prole
Posts: 41
Joined: Thu May 07, 2020 6:43 pm
Contact:

Re: How to create an 'interpreter'? Or some other term closely related..

Post by duaner »

I wrote a markup interpreter in python, years ago. It was very simple. All I did was store a dozen or so regular expressions in a table and run them on each line of text in a series of if statements. This would be even easier in lua, since the pattern-matching is easier (at least for me) to use.

In some cases, you might just want to replace something, like *italic* with the word in italics. For that, I had a table with entries like:

Code: Select all

local rt = {
	{ look_for = '%*([^%*]+)%*', replace = '<em>%1</em>' },
}
In this case, I was writing out html, so there wasn't much to it. However, you could have the if statement change your font (or flag a change during rendering), insert an image, move the text around, etc.

I also looked for tags, such as "|title: My Title", and used that to keep entries for a table of contents as well as change the title font.

Code: Select all

local tit_find = '^|title: (.+)'
So, my advice would be, get familiar with lua pattern-matching, as that will do 90% of your work. Also, don't worry about getting it all working in one go. Get enough together that you can start writing, and add features as you write. You may not need all of the features other interpreters boast.
User avatar
Nikki
Citizen
Posts: 84
Joined: Wed Jan 25, 2017 5:42 pm

Re: How to create an 'interpreter'? Or some other term closely related..

Post by Nikki »

I was looking to procrastinate so i built a little text adventure engine game kinda thing
I hope it is a little bit useful,

Code: Select all

function love.keypressed(key)
   if key == 'escape' then love.event.quit() end

   -- a little bit of magic down here:
   -- we have a few keys that mean directions (up,down,left,right)
   -- if the current room has that specific direction set in its table
   -- it reads the string there (for example 'bar' or 'entrance')
   -- and uses the _G lookup to find the variable that is named like that
   -- and sets the current room to that.
   
   local dirs = {'up', 'down'}
   for i=1, #dirs do
      if key == dirs[i] and currentRoom.directions[key] then
         currentRoom = _G[currentRoom.directions[key]]
         interactingWithActor = nil
         currentDialogWithActor = nil
      end
   end
end

function love.load()
   handCursor = love.mouse.getSystemCursor("hand")
   arrowCursor = love.mouse.getSystemCursor("arrow")
   love.mouse.setCursor(arrowCursor)

   ------------------------------------
   -- actors
   barkeeper = {
      description = [[
This is a meaty guy, 
Are that some back hairs I see?,
ah whatever he serves the drinks around here.
]],
      
      dialogs = {
         {
            str= [[
"Hiya buddy! he shouts."
Over here at our place, its our policy to give anyone who doesnt have a whiskey some whiskey.
So waddaya say, you want a whiskey ? 
Dont care what you say, here you go, a whiskey!
]],
            action='addWhiskey', requirement='noWhiskey'
         },
         {
            str= [[
"Hiya buddy! he shouts."
I see you have some fine whiskey!
Isnt it lovely?
]],
            requirement='haveWhiskey'
         }
      }
   }

   drunk = {
      description = [[
Someone has rolled on his ? hers? i dunno? .
Anyway, a very drunk person, snoring like an enormous pig.
his belly i am sure?, 
Are that some back hairs too I see?,
What is this for a back haired place?.
Sorry i got distracted. this one is DRUNK.
]],
      dialogs = {
         {str=[["zzzz ggHHHGRRTTTTT zzZZZZ"]], requirement='noWhiskey'},
         {str=[[
Dimpledodiree, thankyoufor
THE w hi isssskey
]], requirement='haveWhiskey',  action='removeWhiskey'}

      }
      
   }

   -- rooms
   entrance = {
      description = [[
You are out on the street in front of a bar, Lefty's it is called.
Kinda reminds you of something that name, this location. ah well who knows? dejavu?
Your feeling good today, your newest polyester suit on.
Will this be your lucky night?
]],
      directions = {up='bar'},
   }

   bar = {
      description = [[
You are inside the bar, it stinks, a few customers and a barkeeper are unaware of you.
]],
      directions = {down='entrance', up='toilets'},
      actors = {'barkeeper'}
   }

   toilets = {
      description = [[
The stink here is unbearable!, A drunk has fallen asleep in a corner, 
and all the toilets are broken.
Well, looks like it's gonna be one of those nights.
]],
      directions = {down='bar'},
      actors = {'drunk'}
      
   }

   ---------------------------------------


   inventory = {whiskey=0}
   currentRoom = entrance
   interactingWithActor = nil
   currentDialogWithActor = nil
end


function addWhiskey()
   inventory.whiskey = inventory.whiskey + 1
end
function removeWhiskey()
   inventory.whiskey = inventory.whiskey - 1
end
function noWhiskey()
   return inventory.whiskey == 0
end
function haveWhiskey()
   return inventory.whiskey > 0
end

function pickBestDialog(dialogs)
   -- todo instead of hard requirements think of a scoring system for more fuzzyness
   
   local best = dialogs[1]
   for i = 1, #dialogs do
      if dialogs[i].requirement then
         local met = _G[dialogs[i].requirement]()
         if met then
            best =  dialogs[i]
         end
      end
   end
   return best

end



function button(id, x,y,str)
   local font = love.graphics.getFont()
   local w = font:getWidth(str)
   local h = font:getHeight(str)

   love.graphics.rectangle('fill', x,y, w+20, h)
   love.graphics.setColor(0,0,0)
   love.graphics.print(str, x, y)
   love.graphics.setColor(1,1,1)
   
   local clicked = false
   
   local mx, my = love.mouse:getPosition()
   if mx > x and mx < x+w+20 and my > y and my < y+h then
      love.mouse.setCursor(handCursor)
   end
   if mouseState and mouseState.released then
      if mx > x and mx < x+w+20 and my > y and my < y+h then
         clicked = true
      end
   end

   return {clicked=clicked}
end

function love.mousereleased(x,y)
   mouseState = {released=true}
end



function love.draw()
   love.mouse.setCursor(arrowCursor)
   
   local descriptionWidth = love.graphics.getFont():getWidth(currentRoom.description)

   love.graphics.print(currentRoom.description, 400-descriptionWidth/2, 60)

   if currentRoom.actors then
      local i = 1
      for i = 1, #currentRoom.actors do
         local str = currentRoom.actors[i]
         local b = button('actor'..i, 100, 200+i*20, str)
         if b.clicked then
            interactingWithActor = _G[str]
         end
         
         i= i+1
      end

      if interactingWithActor then
         love.graphics.print(interactingWithActor.description, 100, 200+30+i*20)
         if interactingWithActor.dialogs then
            local b =  button('talk', 100,  230+i*20 + 100, 'talk')
            if b.clicked then
               local d = pickBestDialog(interactingWithActor.dialogs)
               currentDialogWithActor = d
            end
            
         end
         if interactingWithActor.dialogs then
            local b = button('bye', 100 + 100,  230+i*20 + 100, 'bye')
            if b.clicked then
               interactingWithActor = nil
               if currentDialogWithActor then
                  currentDialogWithActor.isDone = nil
               end
               
               currentDialogWithActor = nil
            end
         end
      end
      if currentDialogWithActor then
         love.graphics.print(currentDialogWithActor.str, 100, 375+i*20)
         if currentDialogWithActor.action then

            local met = true
            if currentDialogWithActor.requirement then
               local result = _G[currentDialogWithActor.requirement]()
               if not result then
                  met = false
               end
            end

            if met then
               _G[currentDialogWithActor.action]()
            end
         end
      end
   end
   
   love.graphics.print('From here you go a few directions:', 500, 500)
   local i = 1
   for k,v in pairs(currentRoom.directions) do
      love.graphics.print('You can press '..k..' to go to the '..v, 500, 530+20*i)
      i = 1 + 1
   end

   mouseState = nil
   
end
vyraefi
Prole
Posts: 2
Joined: Sun Jul 05, 2020 3:20 am

Re: How to create an 'interpreter'? Or some other term closely related..

Post by vyraefi »

Well thank you for the replies everyone. @Nikki, I like your overall design and how you stored text was also real interesting. I'm wondering if I should stick to Lua strings like you or somehow use a txt file for parsing. I'll find out soon enough which is easier for me :)

As for @zorg, thanks! I think a script parser is exactly the word I was trying to use. I'll look around for visual novel projects and see what I can dig up to see if I can find one similar.

And lastly, @duaner, WOAH. Thank you! I think this is the exact concept that I will need to learn for building a proper text parser. I think learning pattern-matching will be a very useful skill. Thanks all for the assistance.
User avatar
pgimeno
Party member
Posts: 3656
Joined: Sun Oct 18, 2015 2:58 pm

Re: How to create an 'interpreter'? Or some other term closely related..

Post by pgimeno »

Lua expressions are simpler, but also less powerful. They lack two key features of regular expressions that would make them equivalent to finite automata, namely alternation like "expr1|expr2" and grouping of subexpressions like "(expr)". In Lua patterns, the parentheses are only used for capturing, but not for grouping, e.g. you can't do this: "match(es)?" to match either "match" or "matches".

They are sometimes good enough, but other times you have to add extra code to match what you need.
User avatar
zorg
Party member
Posts: 3465
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: How to create an 'interpreter'? Or some other term closely related..

Post by zorg »

Then again, you might not even need a parser that can do full regex and lua's patterns may be enough... up to you really.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
Post Reply

Who is online

Users browsing this forum: Bing [Bot] and 2 guests