GithubHelp home page GithubHelp logo

orvid / caprica Goto Github PK

View Code? Open in Web Editor NEW
75.0 75.0 14.0 1.05 MB

A compiler for the Papyrus scripting language used by the Creation Engine.

License: MIT License

CMake 0.78% C++ 99.20% C 0.02%

caprica's People

Contributors

nikitalita avatar orvid avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

caprica's Issues

Logical statements give unexpected results when compiling with -O optimization

Orvid, logical statement evaluation seems to be broken in Caprica v0.2.0 when using optimization.

I put the following debug code in a script and compiled it with Caprica using the -O optimization option.

Debug.Trace("!TRUE && TRUE = " + (!TRUE && TRUE), 1)
Debug.Trace("!TRUE && FALSE = " + (!TRUE && FALSE), 1)
Debug.Trace("!FALSE && TRUE = " + (!FALSE && TRUE), 1)
Debug.Trace("!FALSE && FALSE = " + (!FALSE && FALSE), 1)
Debug.Trace("!TRUE || TRUE = " + (!TRUE || TRUE), 1)
Debug.Trace("!TRUE || FALSE = " + (!TRUE || FALSE), 1)
Debug.Trace("!FALSE || TRUE = " + (!FALSE || TRUE), 1)
Debug.Trace("!FALSE || FALSE = " + (!FALSE || FALSE), 1)

RESULT:

[06/21/2016 - 10:52:53PM] !TRUE && TRUE = True (this should be FALSE)
[06/21/2016 - 10:52:53PM] !TRUE && FALSE = True (this should be FALSE)
[06/21/2016 - 10:52:53PM] !FALSE && TRUE = True (correct result)
[06/21/2016 - 10:52:53PM] !FALSE && FALSE = False (correct result)
[06/21/2016 - 10:52:53PM] !TRUE || TRUE = True (correct result)
[06/21/2016 - 10:52:53PM] !TRUE || FALSE = False (correct result)
[06/21/2016 - 10:52:53PM] !FALSE || TRUE = False (this should be TRUE)
[06/21/2016 - 10:52:53PM] !FALSE || FALSE = False (this should be TRUE)

As you can see, some of the results are incorrect compared with what I would expect from the CK compiler.

Edit: HOWEVER, it does work correctly if the -O option is not used, so it is an issue with the optimization. Cheers

Function Redefinition causes compilation to fail silently

This issue came up a few times for me so I thought I'd make note of it. It's not a huge deal, it can just be a little bit tricky when working with larger mods with similarly named functions. Haven't looked into the compilation process used, but it should be a fairly trivial fix to look for 2 names or checking the symbol table. (Low priority / QoL fix)

Specific example:

When defining multiple global functions with the name "LandShip" inside the same namespace of a script, the compiler will give the same message as success (press any key to continue...) without any warnings. I actually noticed it was failing because it did not produce a file and the usual warning message about experimental features (the guard ones) was lacking.

ForEach does not work with Formlists

Compiler Output

dubhAutoLootFunctions.psc(28,53): Fatal Error: Unresolved function name 'GetCount'!

Affected Source

ObjectReference[] Function FilterLootArrayByKeywords(Formlist akKeywords, ObjectReference akActor, Float afRadius)
    ObjectReference[] akResult = new ObjectReference[akKeywords.GetSize()]

    ForEach Form akKeyword In akKeywords
        ; empty
    EndForEach

    Return akResult
EndFunction

ForEach Documentation

The ForEach statement may be used to iterate over arrays or collections. A collection is defined as any Object which implements a method named GetCount that accepts no parameters and returns an Int, and also implements a method named GetAt which accepts a single Int argument and returns the value at that index. <expression> is evaluated exactly once before the loop begins. FormList and RefCollectionAlias are two examples of objects in Fallout 4 that are considered collections.

<foreach> ::= 'ForEach' (<type>|'Auto') <identifier> 'In' <expression>
                <statement>*
              'EndForEach'

Comments

Formlist uses GetSize(), not GetCount(). Caprica might need to take this into account.

namespace error with newer fallout 4 scriptnames in starfield

I'm trying to compile the script Fragments\TopicInfos\tif_ffclinicr02_001c2c28.psc for starfield

the scriptname is
ScriptName Fragments:TopicInfos:TIF_FFClinicR02_001C2C28 Extends TopicInfo Const hidden

--help doesnt have any info on changing the namespace

not sure how to compile these scripts with the newer fallout 4 style scriptnames

edit: not sure how possible this is but if no namespace is provided, it would be great if it checked the scriptname for : and automatically set the namespace if found or have an option to create the name space it expected instead of just erroring out

Unresolved function name

Hi, I have some issues compiling a Skyrim script as it doesn't detect some function names. Here is an example to reproduce.
Thank you for this project. Looks very cool!

My script:

Scriptname Test extends Actor

Function Test()
	GetActorBase()
EndFunction

Calling Caprica

.\Caprica.exe Test.psc -g skyrim -i "D:\Steam\steamapps\common\Skyrim Special Edition\Data\Scripts\Source" -f "D:\Steam\steamapps\common\Skyrim Special Edition\Data\Scripts\Source\TESV_Papyrus_Flags.flg"
Adding current working directory to import list: D:\Steam\steamapps\common\Skyrim Special Edition\Papyrus Compiler\Input
Adding import directory: D:\Steam\steamapps\common\Skyrim Special Edition\Data\Scripts\Source
Importing files...
Imported 13938 files.
Adding file 'Test.psc' to namespace ''.
Compiling Test.psc
Test.psc (4, 2:14): Fatal Error: Unresolved function name 'GetActorBase'!```

0.3.0 False positive warning for persistent script variable declaration

Caprica Papyrus Compiler v0.3.0
Compiling SKK_ShipToolsQuestScript.psc
SKK_ShipToolsQuestScript.psc (8, 7:21): Warning W4007: The script variable 'fScriptVersion' is initialized but is never used.

;*******************************************
Scriptname SKK_ShipToolsQuestScript extends Quest

Float fScriptVersion = 0.0

Event OnQuestInit()
fScriptVersion = 1.0 ;this would beg to differ
EndEvent
;*******************************************

LegendaryItemQuestScript.psc fails to compile

D:\Steam\steamapps\common\Fallout 4\Data\scripts\source\LegendaryItemQuestScript.psc(52,7):
    Fatal Error: Expected 'Identifier' got 'Continue'!

Looks like the error is related to:

bool continue = ListOfSpecificModsToDisallow.Find(ModToConsider as Form) <= -1

Does Caprica's Find() syntax match Champollion's?

Unhandled duplicate parameter syntax error

CAPRICA.EXE (Cmake#17 Master:6346694835) unhandled syntax error:

Debug.Trace("Scriptname.FunctionName.Content", aiSeverity=0, aiSeverity=0)

Importing files...
Imported 4560 files.
WARNING: Loose input files are assumed as being in the root namespace.
Adding file 'SKK_ConsoleUtilityScript.psc' to namespace ''.
Compiling SKK_ConsoleUtilityScript.psc
Press any key to continue . . .

No compile warnings or unexpected token errors.

Comparison of bools in structs fails under certain circumstances

So, I ran into a confusing thing earlier, where one line of code in one of my scripts wasn't working as expected. I investigated, and here's some test code that demonstrates the bug:

scriptname boolTestB

struct testStruct
	bool b
	int i
	float f
	ObjectReference O
endStruct

Function testBoolStruct() Global
	testStruct structA = new testStruct
	testStruct structB = new testStruct
	
	structA.b = false
	structA.i = 0
	structA.f = 0
	structA.O = Game.GetForm(0x14) as ObjectReference ; PlayerRef
	
	structB.b = true
	structB.i = 1
	structB.f = 1
	structB.O = Game.GetForm(0x22B94) as ObjectReference ; some arbitrary rock somewhere
	
	bool bFalse = false
	bool bTrue = true
	
	int i0 = 0
	int i1 = 1
	
	float f0 = 0
	float f1 = 1
	
	ObjectReference PlayerRef = Game.GetForm(0x14) as ObjectReference
	ObjectReference Rock = Game.GetForm(0x22B94) as ObjectReference
	
	Debug.Trace("##############################")
	; these work properly
	Debug.Trace("structA: " + structA.b)
	Debug.Trace("structB: " + structB.b)
	Debug.Trace("structA.b || structB.b: " + (structA.b || structB.b))
	Debug.Trace("structB.b || structA.b: " + (structB.b || structA.b))
	Debug.Trace("false || (bTrue != bFalse): " + (false || (bTrue != bFalse)))
	Debug.Trace("false || (i0 != i1): " + (false || (i0 != i1)))
	Debug.Trace("false || (f0 != f1): " + (false || (f0 != f1)))
	Debug.Trace("false || (PlayerRef != Rock): " + (false || (PlayerRef != Rock)))
	Debug.Trace("false || (structA.i != structB.i): " + (false || (structA.i != structB.i)))
	Debug.Trace("false || (structA.f != structB.f): " + (false || (structA.f != structB.f)))
	Debug.Trace("false || (structA.O != structB.O): " + (false || (structA.O != structB.O)))
	
	; this one doesn't, with Caprica
	Debug.Trace("false || (structA.b != structB.b): " + (false || (structA.b != structB.b)))
	
	Debug.Trace("##############################")
EndFunction

I compiled two separate scripts, identical except that one was compiled with the Bethesda compiler, and the other with Caprica (boolTestB and boolTestC respectively), and here's the log output, again in the same order:

[04/17/2018 - 07:46:57PM] ##############################
[04/17/2018 - 07:46:57PM] structA: False
[04/17/2018 - 07:46:57PM] structB: True
[04/17/2018 - 07:46:57PM] structA.b || structB.b: True
[04/17/2018 - 07:46:57PM] structB.b || structA.b: True
[04/17/2018 - 07:46:57PM] false || (bTrue != bFalse): True
[04/17/2018 - 07:46:57PM] false || (i0 != i1): True
[04/17/2018 - 07:46:57PM] false || (f0 != f1): True
[04/17/2018 - 07:46:57PM] false || (PlayerRef != Rock): True
[04/17/2018 - 07:46:57PM] false || (structA.i != structB.i): True
[04/17/2018 - 07:46:57PM] false || (structA.f != structB.f): True
[04/17/2018 - 07:46:57PM] false || (structA.O != structB.O): True
[04/17/2018 - 07:46:57PM] false || (structA.b != structB.b): True
[04/17/2018 - 07:46:57PM] ##############################

[04/17/2018 - 07:47:00PM] ##############################
[04/17/2018 - 07:47:00PM] structA: False
[04/17/2018 - 07:47:00PM] structB: True
[04/17/2018 - 07:47:00PM] structA.b || structB.b: True
[04/17/2018 - 07:47:00PM] structB.b || structA.b: True
[04/17/2018 - 07:47:00PM] false || (bTrue != bFalse): True
[04/17/2018 - 07:47:00PM] false || (i0 != i1): True
[04/17/2018 - 07:47:00PM] false || (f0 != f1): True
[04/17/2018 - 07:47:00PM] false || (PlayerRef != Rock): True
[04/17/2018 - 07:47:00PM] false || (structA.i != structB.i): True
[04/17/2018 - 07:47:00PM] false || (structA.f != structB.f): True
[04/17/2018 - 07:47:00PM] false || (structA.O != structB.O): True
[04/17/2018 - 07:47:00PM] false || (structA.b != structB.b): False
[04/17/2018 - 07:47:00PM] ##############################

Also, here's the assembly after running each through Bethesda's disassembler:

						JumpT ::temp15 _label9                                   ;@line 51
						StructGet ::temp13 structA b                             ;@line 51
						StructGet ::temp2 structB b                              ;@line 51
						CompareEQ ::temp12 ::temp13 ::temp2                      ;@line 51
						Not ::temp12 ::temp12                                    ;@line 51
						Cast ::temp15 ::temp12                                   ;@line 51
					_label9:
						Cast ::temp10 ::temp15                                   ;@line 51
						StrCat ::temp11 "false || (structA.b != structB.b): " ::temp10  ;@line 51
						CallStatic debug Trace ::nonevar ::temp11 0              ;@line 51
						JumpT ::temp3 _label9                                    ;@line 51
						StructGet ::temp3 structA b                              ;@line 51
						StructGet ::temp3 structB b                              ;@line 51
						CompareEQ ::temp3 ::temp3 ::temp3                        ;@line 51
						Not ::temp3 ::temp3                                      ;@line 51
						Assign ::temp3 ::temp3                                   ;@line 51
					_label9:
						Cast ::temp4 ::temp3                                     ;@line 51
						StrCat ::temp4 "false || (structA.b != structB.b): " ::temp4  ;@line 51
						CallStatic debug Trace ::nonevar ::temp4 0               ;@line 51

It appears that Caprica gets really confused and starts assigning everything to ::temp3 for some reason. This also makes Champollion fail miserably on the Caprica script, it just gives up when it gets to that section. This was compiled with the now 2-year-old v0.2.0 release binary from the Nexus, without the -O flag since I know it's borked in that version.

Also, here's a link to an archive with the sources, compiled scripts, Caprica .pas output (for boolTestC), and disassembled .pas outputs for both, in case it helps any.

This is easy enough to work around, but really confusing if you run into it like I did. Any ideas?

Script Wide Variables issue

It appears that using script wide variables causes the script to not function. It compiles without error but when called in game will not perform any action.

You can actually de-compile a working vanilla script that contains a script wide variable then recompile it without making any changes and it will no longer function.

Here is an example that compiles but does not function

const ScriptName Broken extends ObjectReference

;-- Script Wide Variables ---------------------------------------
int test = 0

Function OnInit()
    test = 5
    Debug.Notification("This text never appears")
EndFunction

Remove the script wide variable and suddenly it does work

const ScriptName Broken extends ObjectReference

Function OnInit()
    Debug.Notification("This text does appears")
EndFunction

No Disk Error

It seems that if there's a G:\ drive with no disk in it (in my case, a card reader), this error pops up endlessly until I stop Caprica with Ctrl-C/

untitled

I'm simply dropping a script on Caprica.exe (version 0.1.5) after having set the import folder containing the extracted Fallout 4 scripts in caprica.cfg

I've tried this with three different computers, and they all do this if there's an empty G drive. If there's a disk in G, or if it's removed or disabled, it works fine.

All the stock Fallout 4 scripts reference a G drive as their source. Perhaps that has something to do with it.

Fatal Error: Unable to locate line at offset 481

The latest build of Caprica fails early with this error:

Fatal Error: Unable to locate line at offset 481

The issue seems related to FO4_Papyrus_Flags.flg from 0.2.

The issue does not occur with Institute_Papyrus_Flags.flg but it also does not compile with the base game flags file.

Exclude conditional variables from "not used" warnings

Scripts SET confitional variables to manage terminal and message display options, example:

Bool bQuestObjectivesHidden = false Conditional ; GetVMQuestVariable condition SGMenu display options

Scripts may never GET the values as they are purely to condition UI elements which triggers a Caprica compile warning:

WARNING W4007: The script variable ABC is initalized byt is never used.

This is totally unnecessary and disrupts compiler output error checking, laeding to un-natural aces as workarounds:

Function BogusCapricaFunction() ; Unnecessary calls on conditional GetVMQuestVariables to clear caprica compiler warnings
Debug.Trace("SKK_CSNGQuestScript " + bQuestObjectivesHidden, aiSeverity=0)
EndFunction

See attachment for more examples of the same.
CapricaBogusConditionalVariableWarnings

Issue with Champollion output

v0.3 errors on compiling workshopnpcscript as output by the latest Champollion- it includes the construct If (Self as Actor is companionactorscript).
It compiles fine if you add brackets to produce If ((Self as Actor) is companionactorscript)

Structs

It seems that Caprica doesn't support Structs yet. Can you add this? I like Caprica's language extensions, but it's unfortunate that it doesn't support structs like Beth's compiler.

Integer initializer values expressed as decimal are compiled incorrectly if they have lead zeros

Regardless of a script property or variable, Caprica v0.3.0 is not parsing decimal integer values with lead zeros as initializers correctly.

Papyrus Script:

Scriptname TestInitializer Extends Form

Int i7dc   = 0007 Const
Int i8dc   = 0008 Const
Int i124dc = 0124 Const

Int i7hc   = 0x0007 Const
Int i8hc   = 0x0008 Const
Int i124hc = 0x007C Const

ASM output from Caprica v0.3.0:

.info
  .source "TestInitializer.psc"
  .modifyTime 0
  .compileTime 1700096266
  .user "REDACTED"
  .computer "REDACTED"
.endInfo
.userFlagsRef
.endUserFlagsRef
.objectTable
  .object TestInitializer Form
    .userFlags 0
    .docString ""
    .autoState 
    .structTable
    .endStructTable
    .variableTable
      .variable i7dc Int const
        .userFlags 0
        .initialValue 7
      .endVariable
      .variable i8dc Int const
        .userFlags 0
        .initialValue 0
      .endVariable
      .variable i124dc Int const
        .userFlags 0
        .initialValue 84
      .endVariable
      .variable i7hc Int const
        .userFlags 0
        .initialValue 7
      .endVariable
      .variable i8hc Int const
        .userFlags 0
        .initialValue 8
      .endVariable
      .variable i124hc Int const
        .userFlags 0
        .initialValue 124
      .endVariable
    .endVariableTable
    .guardTable
    .endGuardTable
    .propertyTable
    .endPropertyTable
    .propertyGroupTable
    .endPropertyGroupTable
    .stateTable
      .state
      .endState
    .endStateTable
  .endObject
.endObjectTable

Forced casting generates errors

Decompiling a script with Champollion and recompiling it with Caprica inserts forced casting (explicit coercion) in a bunch of places (variable as bool, variable as float, etc...). Just noticed you mentioned this already, so editing this ticket. The explicit coercion appears to be generating errors.

  1. Enable logging (so can see the stack traces)
    in Fallout.ini, under [Papyrus]:
    bEnableLogging=1
    bEnableTrace=1
    bLoadDebugInformation=1

  2. Decompile workshopscript.pex in Champollion and recompile the resulting workshopscript.psc

  3. Go to a Settlement, and move a Settler to another Settlement
    or

  4. Find an idle companion at a Settlement, ask him/her to join you, then dismiss him/her to a different Settlement

  5. errors are generated in \Documents\My Games\Fallout 4\Logs\Script\

(note that the vanilla script generates a couple of warnings and errors on its own, but recompiling generates a bunch of new ones that appear to be casting related)

Recompiling scripts generates duplicate property modifiers.

If you decompile a .pex with Champollion and recompile the .psc with Caprica, properties generate duplicate modifiers:

Before:
bool Property OwnedByPlayer = False auto conditional
bool Property PlayerHasVisited auto conditional hidden

After:
bool Property OwnedByPlayer = False auto conditional conditional
bool Property PlayerHasVisited auto conditional hidden conditional hidden

Feature Request: Allow "unsafe" code (structs and arrays in structs)

The only reason there is the restriction is because of bad coders and lazy GC. However, I'd like there to be an option to explicitly allow this. Certain things simply cannot be accomplished due to actual VM limitations but can be bypassed by encapsulating data.

One cannot do this:

function doFoo( int[] bar )
   ; blah
endfunction

function threadFoo( int[] bar )
   var[] varpar = new var[ 1 ]
   varpar[ 0 ] = bar
   callfunctionnowait( "doFoo", varpar )
endfunction

However, one could encapsulate that:

struct __FooParams
   int[] bar
endstruct

function __FooParamsDispose( __FooParams params )
   params.bar = None
endfunction

function __doFoo( __FooParams params )
   ; blah
   __FooParamsDispose( params )
endfunction

function doFoo( int[] bar )
   __FooParams params = new __FooParams
   params.bar = bar
   __doFoo( params )
endfunction

function threadFoo( int[] bar )
   __FooParams params = new __FooParams
   params.bar = bar
   var[] varpar = new var[ 1 ]
   varpar [ 0 ] = params 
   callfunctionnowait( "__doFoo", varpar  )
endfunction

Was able to do this with Fallout 4 and the CK Compiler Patch - would be great if Caprica supported this too. The VM doesn't care, it's just doing instructions, as I said - this restriction is just due to bad coders and lazy GC.

Edit: fixed typos and added code tags

Comparison of strings not implemented

The vanilla compiler can compare strings. It behaves like you would expect from any sensible language, returning the alphabetical order.
However, Caprica doesn't allow you to compile a script which attempts to compare two strings.

Building/Compiling

Please note that this is developed against MSVC 2015, so other compilers may not work, and earlier versions of MSVC almost certainly won't due to C++11 support.

That's what the readme says, but Caprica.vcxproj is configured for ToolsVersion="15.0" and <PlatformToolset>v141</PlatformToolset>.

The project also contains paths for boost_1_58_0, but CMakeLists.txt is looking for:

find_package(Boost 1.52.0 COMPONENTS filesystem program_options REQUIRED)

Just trying to open the project in VS2015 was a pain, but opening the project in VS2017 was a breeze. That said, I can't get Caprica to compile due to a wide assortment of errors. I was wondering if you could fix this project up so that it can be compiled, as well as some build instructions.

Unexpected behavior with certain op-equals operations on array elements

I'm not sure if this project is still active, but I still use Caprica constantly because I appreciate the faster compile speed and language extensions, so I'll leave this here anyway.

I was profiling a bit of code that looks like this, tweaking a mediocre random number generator I wrote to be "good enough", but more importantly is like 200x faster than Utility.RandomInt/Float because it isn't a delayed function.

	LCG n = GetLCG()
	float[] weights = new float[10]
	float totalWeight = 0
	for int i = 0 to 9
		weights[i] = (i+1)*0.1
		totalWeight += weights[i]
	endfor
	
	int[] bins = new int[10]
	for int i = 0 to 10000
		bins[WeightedRandom(n, weights, 5.5)] += 1
	endfor

So I noticed that bins[] was coming out with a uniform distribution, which is wrong, and that WeightedRandom() was getting run 20k times instead of the intended 10k. Obviously that means that that line of code is equivalent to

	bins[WeightedRandom(n, weights, 5.5)] = bins[WeightedRandom(n, weights, 5.5)] + 1

Tweaking it just slightly to pre-calculate the index fixed it, e.g.

	int index = WeightedRandom(n, weights, 5.5)
	bins[index] += 1

I suppose this makes sense in a way, but it's something to be aware of in case you're trying to write lazy test code like me.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.